diff options
author | Øyvind Grønnesby <oyving@verizonmedia.com> | 2020-08-06 14:00:05 +0200 |
---|---|---|
committer | Øyvind Grønnesby <oyving@verizonmedia.com> | 2020-08-06 14:00:05 +0200 |
commit | 6dea664f2f36a773f53ee975c9aea8b0f78095cc (patch) | |
tree | c99ede4382e326c08a2fd1b1b5fc2ef2e28bdb7c /controller-server | |
parent | 466ce73844e338c25cc643bba14f64c8c6aaadad (diff) |
keep which user created a tenant
Diffstat (limited to 'controller-server')
8 files changed, 34 insertions, 9 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 599fa962527..f2e6cd0e15f 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 @@ -100,20 +100,22 @@ public abstract class LockedTenant { /** A locked CloudTenant. */ public static class Cloud extends LockedTenant { + private final Optional<Principal> creator; private final BiMap<PublicKey, Principal> developerKeys; - private Cloud(TenantName name, BiMap<PublicKey, Principal> developerKeys) { + private Cloud(TenantName name, Optional<Principal> creator, BiMap<PublicKey, Principal> developerKeys) { super(name); this.developerKeys = ImmutableBiMap.copyOf(developerKeys); + this.creator = creator; } private Cloud(CloudTenant tenant) { - this(tenant.name(), tenant.developerKeys()); + this(tenant.name(), Optional.empty(), tenant.developerKeys()); } @Override public CloudTenant get() { - return new CloudTenant(name, developerKeys); + return new CloudTenant(name, creator, developerKeys); } public Cloud withDeveloperKey(PublicKey key, Principal principal) { @@ -121,13 +123,13 @@ 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, keys); + return new Cloud(name, creator, keys); } public Cloud withoutDeveloperKey(PublicKey key) { BiMap<PublicKey, Principal> keys = HashBiMap.create(developerKeys); keys.remove(key); - return new Cloud(name, keys); + return new Cloud(name, creator, keys); } } 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 5e9d5c44d4c..3f25fd9ae1d 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 @@ -46,6 +46,8 @@ public class TenantSerializer { private static final String athenzDomainField = "athenzDomain"; private static final String propertyField = "property"; private static final String propertyIdField = "propertyId"; + private static final String creatorField = "creator"; + private static final String createdAtField = "createdAt"; private static final String contactField = "contact"; private static final String contactUrlField = "contactUrl"; private static final String propertyUrlField = "propertyUrl"; @@ -88,6 +90,7 @@ public class TenantSerializer { // field we continue to write the default value and stop reading it. // TODO(ogronnesby, 2020-08-05): Remove when a version where we do not read the field has propagated. var legacyBillingInfo = new BillingInfo("customer", "Vespa"); + tenant.creator().ifPresent(creator -> root.setString(creatorField, creator.getName())); developerKeysToSlime(tenant.developerKeys(), root.setArray(pemDeveloperKeysField)); toSlime(legacyBillingInfo, root.setObject(billingInfoField)); } @@ -128,8 +131,13 @@ public class TenantSerializer { private CloudTenant cloudTenantFrom(Inspector tenantObject) { TenantName name = TenantName.from(tenantObject.field(nameField).asString()); + + Optional<Principal> creator = tenantObject.field(creatorField).valid() ? + Optional.of(new SimplePrincipal(tenantObject.field(creatorField).asString())) : + Optional.empty(); + BiMap<PublicKey, Principal> developerKeys = developerKeysFromSlime(tenantObject.field(pemDeveloperKeysField)); - return new CloudTenant(name, developerKeys); + return new CloudTenant(name, creator, developerKeys); } private BiMap<PublicKey, Principal> developerKeysFromSlime(Inspector array) { 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 c5fb507749c..427d1d66ee8 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 @@ -1635,6 +1635,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler { case cloud: { CloudTenant cloudTenant = (CloudTenant) tenant; + cloudTenant.creator().ifPresent(creator -> object.setString("creator", creator.getName())); Cursor pemDeveloperKeysArray = object.setArray("pemDeveloperKeys"); cloudTenant.developerKeys().forEach((key, user) -> { Cursor keyObject = pemDeveloperKeysArray.addObject(); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/security/CloudAccessControl.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/security/CloudAccessControl.java index 320b376c2ae..6bdd91c91ce 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/security/CloudAccessControl.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/security/CloudAccessControl.java @@ -50,7 +50,7 @@ public class CloudAccessControl implements AccessControl { requireTenantCreationAllowed((Auth0Credentials) credentials); CloudTenantSpec spec = (CloudTenantSpec) tenantSpec; - CloudTenant tenant = CloudTenant.create(spec.tenant()); + CloudTenant tenant = CloudTenant.create(spec.tenant(), credentials.user()); for (Role role : Roles.tenantRoles(spec.tenant())) { userManagement.createRole(role); 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 d208a1e4e63..a74c39dc362 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 @@ -16,20 +16,28 @@ import java.util.Optional; */ public class CloudTenant extends Tenant { + private final Optional<Principal> creator; private final BiMap<PublicKey, Principal> developerKeys; /** Public for the serialization layer — do not use! */ - public CloudTenant(TenantName name, BiMap<PublicKey, Principal> developerKeys) { + public CloudTenant(TenantName name, Optional<Principal> creator, BiMap<PublicKey, Principal> developerKeys) { super(name, Optional.empty()); + this.creator = creator; this.developerKeys = developerKeys; } /** Creates a tenant with the given name, provided it passes validation. */ - public static CloudTenant create(TenantName tenantName) { + public static CloudTenant create(TenantName tenantName, Principal creator) { return new CloudTenant(requireName(tenantName), + Optional.ofNullable(creator), ImmutableBiMap.of()); } + /** The user that created the tenant */ + public Optional<Principal> creator() { + return creator; + } + /** Returns the set of developer keys and their corresponding developers for this tenant. */ public BiMap<PublicKey, Principal> developerKeys() { return developerKeys; } 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 0f4ea3dd2ce..9f36275e196 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 @@ -78,10 +78,12 @@ public class TenantSerializerTest { @Test public void cloud_tenant() { CloudTenant tenant = new CloudTenant(TenantName.from("elderly-lady"), + Optional.of(new SimplePrincipal("foobar-user")), ImmutableBiMap.of(publicKey, new SimplePrincipal("joe"), otherPublicKey, new SimplePrincipal("jane"))); CloudTenant serialized = (CloudTenant) serializer.tenantFrom(serializer.toSlime(tenant)); assertEquals(tenant.name(), serialized.name()); + assertEquals(tenant.creator(), serialized.creator()); assertEquals(tenant.developerKeys(), serialized.developerKeys()); } 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 3c13962916a..28db2cf2224 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 @@ -27,6 +27,7 @@ import java.net.URI; import java.net.http.HttpRequest; import java.security.PrivateKey; import java.security.PublicKey; +import java.util.Optional; import java.util.Set; import static org.junit.Assert.assertEquals; @@ -66,6 +67,7 @@ public class SignatureFilterTest { signer = new RequestSigner(privateKey, id.serializedForm(), tester.clock()); tester.curator().writeTenant(new CloudTenant(appId.tenant(), + Optional.empty(), ImmutableBiMap.of())); tester.curator().writeApplication(new Application(appId, tester.clock().instant())); } @@ -104,6 +106,7 @@ public class SignatureFilterTest { // Signed request gets a developer role when a matching developer key is stored for the tenant. tester.curator().writeTenant(new CloudTenant(appId.tenant(), + Optional.empty(), ImmutableBiMap.of(publicKey, () -> "user"))); verifySecurityContext(requestOf(signer.signed(request.copy(), Method.POST, () -> new ByteArrayInputStream(hiBytes)), hiBytes), new SecurityContext(new SimplePrincipal("user"), diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-without-applications.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-without-applications.json index 39b6cccbab0..bce3d185977 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-without-applications.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-without-applications.json @@ -1,6 +1,7 @@ { "tenant": "my-tenant", "type": "CLOUD", + "creator": "administrator@tenant", "pemDeveloperKeys": [], "applications": [] } |