summaryrefslogtreecommitdiffstats
path: root/controller-server
diff options
context:
space:
mode:
authorOla Aunrønning <olaa@verizonmedia.com>2021-02-15 10:37:20 +0100
committerGitHub <noreply@github.com>2021-02-15 10:37:20 +0100
commit1e83a0f397f9a895f3e397a9dca1c52333a7e415 (patch)
tree651289f91f97c4bfb20253cf210a78e248e4a44e /controller-server
parent9c519b1d3f14fb40f3d66487d6c1dcd6e63e1128 (diff)
parent4d8b5f024046be72949b04820b541db842fe9f01 (diff)
Merge pull request #16399 from vespa-engine/olaa/store-cloud-id
Allow configuration of secret stores
Diffstat (limited to 'controller-server')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedTenant.java25
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java37
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java33
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/CloudTenant.java14
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfo.java4
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java8
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java13
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java32
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/SignatureFilterTest.java7
9 files changed, 159 insertions, 14 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedTenant.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedTenant.java
index e45bda0708e..519c08b66f5 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedTenant.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedTenant.java
@@ -11,6 +11,7 @@ import com.yahoo.vespa.curator.Lock;
import com.yahoo.vespa.hosted.controller.api.identifiers.Property;
import com.yahoo.vespa.hosted.controller.api.identifiers.PropertyId;
import com.yahoo.vespa.hosted.controller.api.integration.organization.Contact;
+import com.yahoo.vespa.hosted.controller.api.integration.secrets.TenantSecretStore;
import com.yahoo.vespa.hosted.controller.tenant.AthenzTenant;
import com.yahoo.vespa.hosted.controller.tenant.CloudTenant;
import com.yahoo.vespa.hosted.controller.tenant.LastLoginInfo;
@@ -20,6 +21,8 @@ import com.yahoo.vespa.hosted.controller.tenant.TenantInfo;
import java.security.Principal;
import java.security.PublicKey;
import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Optional;
import static java.util.Objects.requireNonNull;
@@ -117,21 +120,23 @@ public abstract class LockedTenant {
private final Optional<Principal> creator;
private final BiMap<PublicKey, Principal> developerKeys;
private final TenantInfo info;
+ private final List<TenantSecretStore> tenantSecretStores;
- private Cloud(TenantName name, Instant createdAt, LastLoginInfo lastLoginInfo, Optional<Principal> creator, BiMap<PublicKey, Principal> developerKeys, TenantInfo info) {
+ private Cloud(TenantName name, Instant createdAt, LastLoginInfo lastLoginInfo, Optional<Principal> creator, BiMap<PublicKey, Principal> developerKeys, TenantInfo info, List<TenantSecretStore> tenantSecretStores) {
super(name, createdAt, lastLoginInfo);
this.developerKeys = ImmutableBiMap.copyOf(developerKeys);
this.creator = creator;
this.info = info;
+ this.tenantSecretStores = tenantSecretStores;
}
private Cloud(CloudTenant tenant) {
- this(tenant.name(), tenant.createdAt(), tenant.lastLoginInfo(), Optional.empty(), tenant.developerKeys(), tenant.info());
+ this(tenant.name(), tenant.createdAt(), tenant.lastLoginInfo(), Optional.empty(), tenant.developerKeys(), tenant.info(), tenant.tenantSecretStores());
}
@Override
public CloudTenant get() {
- return new CloudTenant(name, createdAt, lastLoginInfo, creator, developerKeys, info);
+ return new CloudTenant(name, createdAt, lastLoginInfo, creator, developerKeys, info, tenantSecretStores);
}
public Cloud withDeveloperKey(PublicKey key, Principal principal) {
@@ -139,22 +144,28 @@ public abstract class LockedTenant {
if (keys.containsKey(key))
throw new IllegalArgumentException("Key " + KeyUtils.toPem(key) + " is already owned by " + keys.get(key));
keys.put(key, principal);
- return new Cloud(name, createdAt, lastLoginInfo, creator, keys, info);
+ return new Cloud(name, createdAt, lastLoginInfo, creator, keys, info, tenantSecretStores);
}
public Cloud withoutDeveloperKey(PublicKey key) {
BiMap<PublicKey, Principal> keys = HashBiMap.create(developerKeys);
keys.remove(key);
- return new Cloud(name, createdAt, lastLoginInfo, creator, keys, info);
+ return new Cloud(name, createdAt, lastLoginInfo, creator, keys, info, tenantSecretStores);
}
public Cloud withInfo(TenantInfo newInfo) {
- return new Cloud(name, createdAt, lastLoginInfo, creator, developerKeys, newInfo);
+ return new Cloud(name, createdAt, lastLoginInfo, creator, developerKeys, newInfo, tenantSecretStores);
}
@Override
public LockedTenant with(LastLoginInfo lastLoginInfo) {
- return new Cloud(name, createdAt, lastLoginInfo, creator, developerKeys, info);
+ return new Cloud(name, createdAt, lastLoginInfo, creator, developerKeys, info, tenantSecretStores);
+ }
+
+ public Cloud withSecretStore(TenantSecretStore tenantSecretStore) {
+ ArrayList<TenantSecretStore> secretStores = new ArrayList<>(tenantSecretStores);
+ secretStores.add(tenantSecretStore);
+ return new Cloud(name, createdAt, lastLoginInfo, creator, developerKeys, info, secretStores);
}
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java
index cc2d7c207e5..beaf546930f 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java
@@ -13,6 +13,7 @@ import com.yahoo.vespa.athenz.api.AthenzDomain;
import com.yahoo.slime.SlimeUtils;
import com.yahoo.vespa.hosted.controller.api.identifiers.Property;
import com.yahoo.vespa.hosted.controller.api.identifiers.PropertyId;
+import com.yahoo.vespa.hosted.controller.api.integration.secrets.TenantSecretStore;
import com.yahoo.vespa.hosted.controller.api.role.SimplePrincipal;
import com.yahoo.vespa.hosted.controller.api.integration.organization.Contact;
import com.yahoo.vespa.hosted.controller.api.integration.organization.BillingInfo;
@@ -69,6 +70,9 @@ public class TenantSerializer {
private static final String pemDeveloperKeysField = "pemDeveloperKeys";
private static final String tenantInfoField = "info";
private static final String lastLoginInfoField = "lastLoginInfo";
+ private static final String secretStoresField = "secretStores";
+ private static final String awsIdField = "awsId";
+ private static final String roleField = "role";
public Slime toSlime(Tenant tenant) {
Slime slime = new Slime();
@@ -105,6 +109,7 @@ public class TenantSerializer {
developerKeysToSlime(tenant.developerKeys(), root.setArray(pemDeveloperKeysField));
toSlime(legacyBillingInfo, root.setObject(billingInfoField));
toSlime(tenant.info(), root);
+ toSlime(tenant.tenantSecretStores(), root);
}
private void developerKeysToSlime(BiMap<PublicKey, Principal> keys, Cursor array) {
@@ -156,7 +161,8 @@ public class TenantSerializer {
Optional<Principal> creator = SlimeUtils.optionalString(tenantObject.field(creatorField)).map(SimplePrincipal::new);
BiMap<PublicKey, Principal> developerKeys = developerKeysFromSlime(tenantObject.field(pemDeveloperKeysField));
TenantInfo info = tenantInfoFromSlime(tenantObject.field(tenantInfoField));
- return new CloudTenant(name, createdAt, lastLoginInfo, creator, developerKeys, info);
+ List<TenantSecretStore> tenantSecretStores = secretStoresFromSlime(tenantObject.field(secretStoresField));
+ return new CloudTenant(name, createdAt, lastLoginInfo, creator, developerKeys, info, tenantSecretStores);
}
private BiMap<PublicKey, Principal> developerKeysFromSlime(Inspector array) {
@@ -199,6 +205,22 @@ public class TenantSerializer {
.withAddress(tenantInfoAddressFromSlime(billingObject.field("address")));
}
+ private List<TenantSecretStore> secretStoresFromSlime(Inspector secretStoresObject) {
+ List<TenantSecretStore> secretStores = new ArrayList<>();
+ if (!secretStoresObject.valid()) return secretStores;
+
+ secretStoresObject.traverse((ArrayTraverser) (index, inspector) -> {
+ secretStores.add(
+ new TenantSecretStore(
+ inspector.field(nameField).asString(),
+ inspector.field(awsIdField).asString(),
+ inspector.field(roleField).asString()
+ )
+ );
+ });
+ return secretStores;
+ }
+
private LastLoginInfo lastLoginInfoFromSlime(Inspector lastLoginInfoObject) {
Map<LastLoginInfo.UserLevel, Instant> lastLoginByUserLevel = new HashMap<>();
lastLoginInfoObject.traverse((String name, Inspector value) ->
@@ -240,6 +262,19 @@ public class TenantSerializer {
toSlime(billingContact.address(), addressCursor);
}
+ private void toSlime(List<TenantSecretStore> tenantSecretStores, Cursor parentCursor) {
+ if (tenantSecretStores.isEmpty()) return;
+
+ Cursor secretStoresCursor = parentCursor.setArray(secretStoresField);
+ tenantSecretStores.forEach(tenantSecretStore -> {
+ Cursor secretStoreCursor = secretStoresCursor.addObject();
+ secretStoreCursor.setString(nameField, tenantSecretStore.getName());
+ secretStoreCursor.setString(awsIdField, tenantSecretStore.getAwsId());
+ secretStoreCursor.setString(roleField, tenantSecretStore.getRole());
+ });
+
+ }
+
private Optional<Contact> contactFrom(Inspector object) {
if ( ! object.valid()) return Optional.empty();
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
index 431a694fcd8..f9dc62d7f34 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
@@ -98,6 +98,7 @@ import com.yahoo.vespa.hosted.controller.tenant.Tenant;
import com.yahoo.vespa.hosted.controller.tenant.TenantInfo;
import com.yahoo.vespa.hosted.controller.tenant.TenantInfoAddress;
import com.yahoo.vespa.hosted.controller.tenant.TenantInfoBillingContact;
+import com.yahoo.vespa.hosted.controller.api.integration.secrets.TenantSecretStore;
import com.yahoo.vespa.hosted.controller.versions.VersionStatus;
import com.yahoo.vespa.hosted.controller.versions.VespaVersion;
import com.yahoo.vespa.serviceview.bindings.ApplicationView;
@@ -117,6 +118,7 @@ import java.security.PublicKey;
import java.time.DayOfWeek;
import java.time.Duration;
import java.time.Instant;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.Comparator;
@@ -267,6 +269,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
private HttpResponse handlePUT(Path path, HttpRequest request) {
if (path.matches("/application/v4/tenant/{tenant}")) return updateTenant(path.get("tenant"), request);
if (path.matches("/application/v4/tenant/{tenant}/info")) return updateTenantInfo(path.get("tenant"), request);
+ if (path.matches("/application/v4/tenant/{tenant}/secret-store")) return addSecretStore(path.get("tenant"), request);
if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/environment/{environment}/region/{region}/global-rotation/override")) return setGlobalRotationOverride(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), false, request);
if (path.matches("/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/global-rotation/override")) return setGlobalRotationOverride(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), false, request);
return ErrorResponse.notFoundError("Nothing at " + path);
@@ -631,6 +634,36 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
return new SlimeJsonResponse(root);
}
+ private HttpResponse addSecretStore(String tenantName, HttpRequest request) {
+ if (controller.tenants().require(TenantName.from(tenantName)).type() != Tenant.Type.cloud)
+ throw new IllegalArgumentException("Tenant '" + tenantName + "' is not a cloud tenant");
+
+ var data = toSlime(request.getData()).get();
+ var awsId = mandatory("awsId", data).asString();
+ var externalId = mandatory("externalId", data).asString();
+ var name = mandatory("name", data).asString();
+ var role = mandatory("role", data).asString();
+
+ var tenant = (CloudTenant) controller.tenants().require(TenantName.from(tenantName));
+ var tenantSecretStore = new TenantSecretStore(name, awsId, role);
+
+ if (!tenantSecretStore.isValid()) {
+ return ErrorResponse.badRequest(String.format("Secret store " + tenantSecretStore + " is invalid"));
+ }
+ if (tenant.tenantSecretStores().contains(tenantSecretStore)) {
+ return ErrorResponse.badRequest(String.format("Secret store " + tenantSecretStore + " is already configured"));
+ }
+
+ controller.serviceRegistry().roleService().createTenantPolicy(TenantName.from(tenantName), name, awsId, role);
+ controller.serviceRegistry().tenantSecretService().addSecretStore(tenantSecretStore, externalId);
+ // Store changes
+ controller.tenants().lockOrThrow(tenant.name(), LockedTenant.Cloud.class, lockedTenant -> {
+ lockedTenant = lockedTenant.withSecretStore(tenantSecretStore);
+ controller.tenants().store(lockedTenant);
+ });
+ return new MessageResponse("Configured secret store: " + tenantSecretStore);
+ }
+
private HttpResponse patchApplication(String tenantName, String applicationName, HttpRequest request) {
Inspector requestObject = toSlime(request.getData()).get();
StringJoiner messageBuilder = new StringJoiner("\n").setEmptyValue("No applicable changes.");
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/CloudTenant.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/CloudTenant.java
index 5d0bb780c81..888bfd7fe42 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/CloudTenant.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/CloudTenant.java
@@ -4,10 +4,12 @@ package com.yahoo.vespa.hosted.controller.tenant;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
import com.yahoo.config.provision.TenantName;
+import com.yahoo.vespa.hosted.controller.api.integration.secrets.TenantSecretStore;
import java.security.Principal;
import java.security.PublicKey;
import java.time.Instant;
+import java.util.List;
import java.util.Objects;
import java.util.Optional;
@@ -21,14 +23,17 @@ public class CloudTenant extends Tenant {
private final Optional<Principal> creator;
private final BiMap<PublicKey, Principal> developerKeys;
private final TenantInfo info;
+ private final List<TenantSecretStore> tenantSecretStores;
+
/** Public for the serialization layer — do not use! */
public CloudTenant(TenantName name, Instant createdAt, LastLoginInfo lastLoginInfo, Optional<Principal> creator,
- BiMap<PublicKey, Principal> developerKeys, TenantInfo info) {
+ BiMap<PublicKey, Principal> developerKeys, TenantInfo info, List<TenantSecretStore> tenantSecretStores) {
super(name, createdAt, lastLoginInfo, Optional.empty());
this.creator = creator;
this.developerKeys = developerKeys;
this.info = Objects.requireNonNull(info);
+ this.tenantSecretStores = tenantSecretStores;
}
/** Creates a tenant with the given name, provided it passes validation. */
@@ -37,7 +42,7 @@ public class CloudTenant extends Tenant {
createdAt,
LastLoginInfo.EMPTY,
Optional.ofNullable(creator),
- ImmutableBiMap.of(), TenantInfo.EMPTY);
+ ImmutableBiMap.of(), TenantInfo.EMPTY, List.of());
}
/** The user that created the tenant */
@@ -53,6 +58,11 @@ public class CloudTenant extends Tenant {
/** Returns the set of developer keys and their corresponding developers for this tenant. */
public BiMap<PublicKey, Principal> developerKeys() { return developerKeys; }
+ /** List of configured secret stores */
+ public List<TenantSecretStore> tenantSecretStores() {
+ return tenantSecretStores;
+ }
+
@Override
public Type type() {
return Type.cloud;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfo.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfo.java
index 81c08e1083b..a20477d7aab 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfo.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfo.java
@@ -1,6 +1,10 @@
// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.tenant;
+import com.yahoo.vespa.hosted.controller.api.integration.secrets.TenantSecretStore;
+
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
/**
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java
index 1b7970f6fcb..69960ed393b 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java
@@ -24,6 +24,8 @@ import com.yahoo.vespa.hosted.controller.api.integration.organization.MockIssueH
import com.yahoo.vespa.hosted.controller.api.integration.resource.CostReportConsumerMock;
import com.yahoo.vespa.hosted.controller.api.integration.routing.GlobalRoutingService;
import com.yahoo.vespa.hosted.controller.api.integration.routing.MemoryGlobalRoutingService;
+import com.yahoo.vespa.hosted.controller.api.integration.secrets.NoopTenantSecretService;
+import com.yahoo.vespa.hosted.controller.api.integration.secrets.TenantSecretService;
import com.yahoo.vespa.hosted.controller.api.integration.stubs.DummyOwnershipIssues;
import com.yahoo.vespa.hosted.controller.api.integration.stubs.DummySystemMonitor;
import com.yahoo.vespa.hosted.controller.api.integration.stubs.LoggingDeploymentIssues;
@@ -64,6 +66,7 @@ public class ServiceRegistryMock extends AbstractComponent implements ServiceReg
private final RoleService roleService = new NoopRoleService();
private final BillingController billingController = new MockBillingController();
private final ContainerRegistryMock containerRegistry = new ContainerRegistryMock();
+ private final NoopTenantSecretService tenantSecretService = new NoopTenantSecretService();
public ServiceRegistryMock(SystemName system) {
this.zoneRegistryMock = new ZoneRegistryMock(system);
@@ -205,6 +208,11 @@ public class ServiceRegistryMock extends AbstractComponent implements ServiceReg
return containerRegistry;
}
+ @Override
+ public TenantSecretService tenantSecretService() {
+ return tenantSecretService;
+ }
+
public ConfigServerMock configServerMock() {
return configServerMock;
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java
index 9d187d1d76a..1467c1f6392 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java
@@ -10,6 +10,7 @@ import com.yahoo.vespa.athenz.api.AthenzDomain;
import com.yahoo.vespa.hosted.controller.api.identifiers.Property;
import com.yahoo.vespa.hosted.controller.api.identifiers.PropertyId;
import com.yahoo.vespa.hosted.controller.api.integration.organization.Contact;
+import com.yahoo.vespa.hosted.controller.api.integration.secrets.TenantSecretStore;
import com.yahoo.vespa.hosted.controller.api.role.SimplePrincipal;
import com.yahoo.vespa.hosted.controller.tenant.AthenzTenant;
import com.yahoo.vespa.hosted.controller.tenant.CloudTenant;
@@ -95,7 +96,9 @@ public class TenantSerializerTest {
Optional.of(new SimplePrincipal("foobar-user")),
ImmutableBiMap.of(publicKey, new SimplePrincipal("joe"),
otherPublicKey, new SimplePrincipal("jane")),
- TenantInfo.EMPTY);
+ TenantInfo.EMPTY,
+ List.of()
+ );
CloudTenant serialized = (CloudTenant) serializer.tenantFrom(serializer.toSlime(tenant));
assertEquals(tenant.name(), serialized.name());
assertEquals(tenant.creator(), serialized.creator());
@@ -111,9 +114,15 @@ public class TenantSerializerTest {
Optional.of(new SimplePrincipal("foobar-user")),
ImmutableBiMap.of(publicKey, new SimplePrincipal("joe"),
otherPublicKey, new SimplePrincipal("jane")),
- TenantInfo.EMPTY.withName("Ofni Tnanet"));
+ TenantInfo.EMPTY.withName("Ofni Tnanet"),
+ List.of(
+ new TenantSecretStore("ss1", "123", "role1"),
+ new TenantSecretStore("ss2", "124", "role2")
+ )
+ );
CloudTenant serialized = (CloudTenant) serializer.tenantFrom(serializer.toSlime(tenant));
assertEquals(tenant.info(), serialized.info());
+ assertEquals(tenant.tenantSecretStores(), serialized.tenantSecretStores());
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java
index 10bb722a605..8d6b4f1f629 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java
@@ -114,6 +114,38 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest {
}
}
+ @Test
+ public void test_secret_store_configuration() {
+ var secretStoreRequest =
+ request("/application/v4/tenant/scoober/secret-store", PUT)
+ .data("{" +
+ "\"awsId\": \"123\"," +
+ "\"name\": \"some-name\"," +
+ "\"role\": \"role-id\"," +
+ "\"externalId\": \"321\"" +
+ "}")
+ .roles(Set.of(Role.administrator(tenantName)));
+ tester.assertResponse(secretStoreRequest, "{\"message\":\"Configured secret store: TenantSecretStore{name='some-name', awsId='123', role='role-id'}\"}", 200);
+ tester.assertResponse(secretStoreRequest, "{" +
+ "\"error-code\":\"BAD_REQUEST\"," +
+ "\"message\":\"Secret store TenantSecretStore{name='some-name', awsId='123', role='role-id'} is already configured\"" +
+ "}", 400);
+
+ secretStoreRequest =
+ request("/application/v4/tenant/scoober/secret-store", PUT)
+ .data("{" +
+ "\"awsId\": \"123\"," +
+ "\"name\": \" \"," +
+ "\"role\": \"role-id\"," +
+ "\"externalId\": \"321\"" +
+ "}")
+ .roles(Set.of(Role.administrator(tenantName)));
+ tester.assertResponse(secretStoreRequest, "{" +
+ "\"error-code\":\"BAD_REQUEST\"," +
+ "\"message\":\"Secret store TenantSecretStore{name=' ', awsId='123', role='role-id'} is invalid\"" +
+ "}", 400);
+ }
+
private ApplicationPackageBuilder prodBuilder() {
return new ApplicationPackageBuilder()
.instances("default")
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/SignatureFilterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/SignatureFilterTest.java
index 390823271b4..85aa01a11be 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/SignatureFilterTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/SignatureFilterTest.java
@@ -29,6 +29,7 @@ import java.net.http.HttpRequest;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.time.Instant;
+import java.util.List;
import java.util.Optional;
import java.util.Set;
@@ -73,7 +74,8 @@ public class SignatureFilterTest {
LastLoginInfo.EMPTY,
Optional.empty(),
ImmutableBiMap.of(),
- TenantInfo.EMPTY));
+ TenantInfo.EMPTY,
+ List.of()));
tester.curator().writeApplication(new Application(appId, tester.clock().instant()));
}
@@ -117,7 +119,8 @@ public class SignatureFilterTest {
LastLoginInfo.EMPTY,
Optional.empty(),
ImmutableBiMap.of(publicKey, () -> "user"),
- TenantInfo.EMPTY));
+ TenantInfo.EMPTY,
+ List.of()));
verifySecurityContext(requestOf(signer.signed(request.copy(), Method.POST, () -> new ByteArrayInputStream(hiBytes)), hiBytes),
new SecurityContext(new SimplePrincipal("user"),
Set.of(Role.reader(id.tenant()),