diff options
author | Ola Aunrønning <olaa@verizonmedia.com> | 2021-02-04 18:44:08 +0100 |
---|---|---|
committer | Ola Aunrønning <olaa@verizonmedia.com> | 2021-02-04 18:44:08 +0100 |
commit | 4d8b5f024046be72949b04820b541db842fe9f01 (patch) | |
tree | 101654ae4f4eae7542a383e00ed2f3d71fdd740a /controller-server | |
parent | 0dc4d5d552893b6acd3b22073f15f57ea44d87b0 (diff) |
Allow configuration of secret stores
Diffstat (limited to 'controller-server')
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 9331e5086cc..8dd1b042b01 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()), |