diff options
author | Ola Aunronning <olaa@yahooinc.com> | 2023-10-16 15:27:12 +0200 |
---|---|---|
committer | Ola Aunronning <olaa@yahooinc.com> | 2023-10-16 15:27:12 +0200 |
commit | 121cd04e40ff1de17e68e52ca982aaea8d5e2bc0 (patch) | |
tree | 7bd3d9c2db2c54513128becfcde860716bb8f968 /controller-server/src | |
parent | 90c59bfc313263a238c464b21221d1ede8bf997a (diff) |
Billing mail verification
Diffstat (limited to 'controller-server/src')
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 + "}"; |