aboutsummaryrefslogtreecommitdiffstats
path: root/controller-server/src
diff options
context:
space:
mode:
authorOla Aunronning <olaa@yahooinc.com>2023-10-16 15:27:12 +0200
committerOla Aunronning <olaa@yahooinc.com>2023-10-16 15:27:12 +0200
commit121cd04e40ff1de17e68e52ca982aaea8d5e2bc0 (patch)
tree7bd3d9c2db2c54513128becfcde860716bb8f968 /controller-server/src
parent90c59bfc313263a238c464b21221d1ede8bf997a (diff)
Billing mail verification
Diffstat (limited to 'controller-server/src')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/MailVerifier.java8
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java16
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java18
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/MailVerifierTest.java28
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java4
6 files changed, 59 insertions, 17 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/MailVerifier.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/MailVerifier.java
index 4d29abaa212..ecdfc5990c0 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/MailVerifier.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/MailVerifier.java
@@ -86,6 +86,7 @@ public class MailVerifier {
case NOTIFICATIONS -> withTenantContacts(oldTenantInfo, pendingMailVerification);
case TENANT_CONTACT -> oldTenantInfo.withContact(oldTenantInfo.contact()
.withEmail(oldTenantInfo.contact().email().withVerification(true)));
+ case BILLING -> withVerifiedBillingMail(oldTenantInfo);
};
tenantController.lockOrThrow(tenant.name(), LockedTenant.Cloud.class, lockedTenant -> {
@@ -111,6 +112,13 @@ public class MailVerifier {
return oldInfo.withContacts(new TenantContacts(newContacts));
}
+ private TenantInfo withVerifiedBillingMail(TenantInfo oldInfo) {
+ var verifiedMail = oldInfo.billingContact().contact().email().withVerification(true);
+ var billingContact = oldInfo.billingContact()
+ .withContact(oldInfo.billingContact().contact().withEmail(verifiedMail));
+ return oldInfo.withBilling(billingContact);
+ }
+
private void writePendingVerification(PendingMailVerification pendingMailVerification) {
try (var lock = curatorDb.lockPendingMailVerification(pendingMailVerification.getVerificationCode())) {
curatorDb.writePendingMailVerification(pendingMailVerification);
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 381a5eaaa26..4404063456c 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
@@ -282,10 +282,13 @@ public class TenantSerializer {
}
private TenantBilling tenantInfoBillingContactFromSlime(Inspector billingObject) {
+ //TODO: Remove validity check once emailVerified has been written for all tenants
+ var emailVerified = billingObject.field("emailVerified").valid() ?
+ billingObject.field("emailVerified").asBool() : true;
return TenantBilling.empty()
.withContact(TenantContact.from(
billingObject.field("name").asString(),
- new Email(billingObject.field("email").asString(), true),
+ new Email(billingObject.field("email").asString(), emailVerified),
billingObject.field("phone").asString()))
.withAddress(tenantInfoAddressFromSlime(billingObject.field("address")));
}
@@ -344,11 +347,12 @@ public class TenantSerializer {
private void toSlime(TenantBilling billingContact, Cursor parentCursor) {
if (billingContact.isEmpty()) return;
- Cursor addressCursor = parentCursor.setObject("billingContact");
- addressCursor.setString("name", billingContact.contact().name());
- addressCursor.setString("email", billingContact.contact().email().getEmailAddress());
- addressCursor.setString("phone", billingContact.contact().phone());
- toSlime(billingContact.address(), addressCursor);
+ Cursor billingCursor = parentCursor.setObject("billingContact");
+ billingCursor.setString("name", billingContact.contact().name());
+ billingCursor.setString("email", billingContact.contact().email().getEmailAddress());
+ billingCursor.setBool("emailVerified", billingContact.contact().email().isVerified());
+ billingCursor.setString("phone", billingContact.contact().phone());
+ toSlime(billingContact.address(), billingCursor);
}
private void toSlime(List<TenantSecretStore> tenantSecretStores, Cursor parentCursor) {
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 d274d59c417..1cf1f35493d 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
@@ -779,11 +779,12 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
private void toSlime(TenantBilling billingContact, Cursor parentCursor) {
if (billingContact.isEmpty()) return;
- Cursor addressCursor = parentCursor.setObject("billingContact");
- addressCursor.setString("name", billingContact.contact().name());
- addressCursor.setString("email", billingContact.contact().email().getEmailAddress());
- addressCursor.setString("phone", billingContact.contact().phone());
- toSlime(billingContact.address(), addressCursor);
+ Cursor billingCursor = parentCursor.setObject("billingContact");
+ billingCursor.setString("name", billingContact.contact().name());
+ billingCursor.setString("email", billingContact.contact().email().getEmailAddress());
+ billingCursor.setBool("emailVerified", billingContact.contact().email().isVerified());
+ billingCursor.setString("phone", billingContact.contact().phone());
+ toSlime(billingContact.address(), billingCursor);
}
private void toSlime(TenantContacts contacts, Cursor parentCursor) {
@@ -898,9 +899,9 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
var mergedEmail = optional("email", insp)
.filter(address -> !address.equals(oldContact.email().getEmailAddress()))
.map(address -> {
- if (isBillingContact)
- return new Email(address, true);
- controller.mailVerifier().sendMailVerification(tenantName, address, PendingMailVerification.MailType.TENANT_CONTACT);
+ var mailType = isBillingContact ? PendingMailVerification.MailType.BILLING :
+ PendingMailVerification.MailType.TENANT_CONTACT;
+ controller.mailVerifier().sendMailVerification(tenantName, address, mailType);
return new Email(address, false);
})
.orElse(oldContact.email());
@@ -1700,6 +1701,7 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
var mailType = switch (type) {
case "contact" -> PendingMailVerification.MailType.TENANT_CONTACT;
case "notifications" -> PendingMailVerification.MailType.NOTIFICATIONS;
+ case "billing" -> PendingMailVerification.MailType.BILLING;
default -> throw new IllegalArgumentException("Unknown mail type " + type);
};
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/MailVerifierTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/MailVerifierTest.java
index e641b332a72..ca71b912feb 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/MailVerifierTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/MailVerifierTest.java
@@ -3,12 +3,15 @@ package com.yahoo.vespa.hosted.controller;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.TenantName;
+import com.yahoo.vespa.hosted.controller.api.integration.organization.BillingInfo;
import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockMailer;
import com.yahoo.vespa.hosted.controller.application.MailVerifier;
import com.yahoo.vespa.hosted.controller.tenant.CloudTenant;
import com.yahoo.vespa.hosted.controller.tenant.Email;
import com.yahoo.vespa.hosted.controller.tenant.PendingMailVerification;
import com.yahoo.vespa.hosted.controller.tenant.Tenant;
+import com.yahoo.vespa.hosted.controller.tenant.TenantBilling;
+import com.yahoo.vespa.hosted.controller.tenant.TenantContact;
import com.yahoo.vespa.hosted.controller.tenant.TenantContacts;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -100,4 +103,29 @@ class MailVerifierTest {
assertTrue(tester.curator().getPendingMailVerification(resentVerification.get().getVerificationCode()).isPresent());
}
+ @Test
+ public void test_billing_mail_verification() {
+ var billingMail = "billing@foo.bar";
+ tester.controller().tenants().lockOrThrow(tenantName, LockedTenant.Cloud.class, lockedTenant -> {
+ var tenantBilling = TenantBilling.empty().withContact(TenantContact.empty().withEmail(new Email(billingMail, false)));
+ lockedTenant = lockedTenant.withInfo(lockedTenant.get().info().withBilling(tenantBilling));
+ tester.controller().tenants().store(lockedTenant);
+ });
+ mailVerifier.sendMailVerification(tenantName, billingMail, PendingMailVerification.MailType.BILLING);
+
+ // Assert written verification data
+ var writtenMailVerification = tester.curator().listPendingMailVerifications().get(0);
+ assertEquals(PendingMailVerification.MailType.BILLING, writtenMailVerification.getMailType());
+ assertEquals(tenantName, writtenMailVerification.getTenantName());
+ assertEquals(tester.clock().instant().plus(Duration.ofDays(7)), writtenMailVerification.getVerificationDeadline());
+ assertEquals(billingMail, writtenMailVerification.getMailAddress());
+
+ // Assert mail is verified
+ mailVerifier.verifyMail(writtenMailVerification.getVerificationCode());
+ assertTrue(tester.curator().listPendingMailVerifications().isEmpty());
+ var tenant = tester.controller().tenants().require(tenantName, CloudTenant.class);
+ var expectedBillingContact = TenantContact.empty().withEmail(new Email(billingMail, true));
+ assertEquals(expectedBillingContact, tenant.info().billingContact().contact());
+ }
+
}
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 8f114a7255c..cb867c76f1f 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
@@ -229,7 +229,7 @@ public class TenantSerializerTest {
.withCode("3510")
.withRegion("Viken"))
.withBilling(TenantBilling.empty()
- .withContact(TenantContact.from("Thomas The Tank Engine", new Email("ceo@mycomp.any", true), "NA"))
+ .withContact(TenantContact.from("Thomas The Tank Engine", new Email("ceo@mycomp.any", false), "NA"))
.withAddress(TenantAddress.empty()
.withCity("Suddery")
.withCountry("Sodor")
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 e89c913ab7d..bf908069c1e 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
@@ -151,10 +151,10 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest {
tester.assertResponse(postPartialContacts, "{\"message\":\"Tenant info updated\"}", 200);
// Read back the updated info
- tester.assertResponse(infoRequest, "{\"name\":\"\",\"email\":\"\",\"website\":\"\",\"contactName\":\"newName\",\"contactEmail\":\"foo@example.com\",\"contactEmailVerified\":false,\"billingContact\":{\"name\":\"billingName\",\"email\":\"\",\"phone\":\"\"},\"contacts\":[{\"audiences\":[\"tenant\"],\"email\":\"contact1@example.com\",\"emailVerified\":false}]}", 200);
+ tester.assertResponse(infoRequest, "{\"name\":\"\",\"email\":\"\",\"website\":\"\",\"contactName\":\"newName\",\"contactEmail\":\"foo@example.com\",\"contactEmailVerified\":false,\"billingContact\":{\"name\":\"billingName\",\"email\":\"\",\"emailVerified\":true,\"phone\":\"\"},\"contacts\":[{\"audiences\":[\"tenant\"],\"email\":\"contact1@example.com\",\"emailVerified\":false}]}", 200);
String fullAddress = "{\"addressLines\":\"addressLines\",\"postalCodeOrZip\":\"postalCodeOrZip\",\"city\":\"city\",\"stateRegionProvince\":\"stateRegionProvince\",\"country\":\"country\"}";
- String fullBillingContact = "{\"name\":\"name\",\"email\":\"foo@example\",\"phone\":\"phone\",\"address\":" + fullAddress + "}";
+ String fullBillingContact = "{\"name\":\"name\",\"email\":\"foo@example\",\"emailVerified\":false,\"phone\":\"phone\",\"address\":" + fullAddress + "}";
String fullContacts = "[{\"audiences\":[\"tenant\"],\"email\":\"contact1@example.com\",\"emailVerified\":false},{\"audiences\":[\"notifications\"],\"email\":\"contact2@example.com\",\"emailVerified\":false},{\"audiences\":[\"tenant\",\"notifications\"],\"email\":\"contact3@example.com\",\"emailVerified\":false}]";
String fullInfo = "{\"name\":\"name\",\"email\":\"foo@example\",\"website\":\"https://yahoo.com\",\"contactName\":\"contactName\",\"contactEmail\":\"contact@example.com\",\"contactEmailVerified\":false,\"address\":" + fullAddress + ",\"billingContact\":" + fullBillingContact + ",\"contacts\":" + fullContacts + "}";