diff options
author | Øyvind Grønnesby <oyving@verizonmedia.com> | 2020-08-04 21:29:58 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-08-04 21:29:58 +0200 |
commit | 2e82ff0ff2e24811d96c42126d81526da7ea3779 (patch) | |
tree | 88eb9029a585ff2caafae8ee05cf49559aeacd8e /controller-server/src | |
parent | 26846137d774879c1e68ed8a7975f782bb19976c (diff) | |
parent | 7cd14a2802f4f16afb40f05681cc11c61ad6fc79 (diff) |
Merge pull request #13983 from vespa-engine/olaa/disallow-deleting-tenant-with-outstanding-charges
Disallow deleting tenants with outstanding charges
Diffstat (limited to 'controller-server/src')
2 files changed, 80 insertions, 6 deletions
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 13cf992cd52..ec4d9ff3626 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 @@ -22,6 +22,8 @@ import com.yahoo.vespa.hosted.controller.tenant.CloudTenant; import com.yahoo.vespa.hosted.controller.tenant.Tenant; import javax.ws.rs.ForbiddenException; +import java.math.BigDecimal; +import java.time.LocalDate; import java.util.List; import static com.yahoo.vespa.hosted.controller.api.role.RoleDefinition.*; @@ -36,13 +38,13 @@ public class CloudAccessControl implements AccessControl { private final UserManagement userManagement; private final BooleanFlag enablePublicSignup; - private final BillingController planController; + private final BillingController billingController; @Inject public CloudAccessControl(UserManagement userManagement, FlagSource flagSource, ServiceRegistry serviceRegistry) { this.userManagement = userManagement; this.enablePublicSignup = Flags.ENABLE_PUBLIC_SIGNUP_FLOW.bindTo(flagSource); - planController = serviceRegistry.billingController(); + billingController = serviceRegistry.billingController(); } @Override @@ -101,15 +103,17 @@ public class CloudAccessControl implements AccessControl { @Override public void deleteTenant(TenantName tenant, Credentials credentials) { - if(!(allowedByPrivilegedRole((Auth0Credentials) credentials) || isTrial(tenant))) - throw new ForbiddenException("Please contact the Vespa team for assistance in deleting non-trial tenants"); + if(!(allowedByPrivilegedRole((Auth0Credentials) credentials) || noOutstandingCharges(tenant))) + throw new ForbiddenException("Please contact the Vespa team for assistance in deleting tenants with outstanding charges"); for (TenantRole role : Roles.tenantRoles(tenant)) userManagement.deleteRole(role); } - private boolean isTrial(TenantName tenant) { - return planController.getPlan(tenant).value().equals("trial"); + private boolean noOutstandingCharges(TenantName tenant) { + return billingController.createUncommittedInvoice(tenant, LocalDate.now()).sum().compareTo(BigDecimal.ZERO) == 0 && + billingController.getUnusedLineItems(tenant).size() == 0 && + billingController.getPlan(tenant).value().equals("trial"); } @Override diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/security/CloudAccessControlTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/security/CloudAccessControlTest.java new file mode 100644 index 00000000000..baf7d3826f1 --- /dev/null +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/security/CloudAccessControlTest.java @@ -0,0 +1,70 @@ +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.security; + +import com.yahoo.config.provision.TenantName; +import com.yahoo.vespa.flags.FlagSource; +import com.yahoo.vespa.flags.InMemoryFlagSource; +import com.yahoo.vespa.hosted.controller.api.integration.ServiceRegistry; +import com.yahoo.vespa.hosted.controller.api.integration.billing.Invoice; +import com.yahoo.vespa.hosted.controller.api.integration.billing.MockBillingController; +import com.yahoo.vespa.hosted.controller.api.integration.billing.PlanId; +import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockUserManagement; +import com.yahoo.vespa.hosted.controller.api.integration.user.UserManagement; +import com.yahoo.vespa.hosted.controller.integration.ServiceRegistryMock; +import org.junit.Test; + +import javax.ws.rs.ForbiddenException; +import java.math.BigDecimal; +import java.security.Principal; +import java.util.HashSet; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * @author olaa + */ +public class CloudAccessControlTest { + + private final UserManagement userManagement = new MockUserManagement(); + private final FlagSource flagSource = new InMemoryFlagSource(); + private final ServiceRegistry serviceRegistry = new ServiceRegistryMock(); + private final MockBillingController billingController = (MockBillingController) serviceRegistry.billingController(); + private final CloudAccessControl cloudAccessControl = new CloudAccessControl(userManagement, flagSource, serviceRegistry); + + @Test + public void tenant_deletion_fails_when_outstanding_charges() { + // First verify that it works with no outstanding charges + var tenant = TenantName.defaultName(); + var principal = mock(Principal.class); + var credentials = new Auth0Credentials(principal, new HashSet<>()); + cloudAccessControl.deleteTenant(tenant, credentials); + + // Forbidden if plan != trial + billingController.setPlan(tenant, PlanId.from("subscription"), false); + try { + cloudAccessControl.deleteTenant(tenant, credentials); + fail(); + } catch (ForbiddenException ignored) {} + billingController.setPlan(tenant, PlanId.from("trial"), false); + + // Forbidden if outstanding lineitems + billingController.addLineItem(tenant, "Some expense", BigDecimal.TEN, "agent"); + try { + cloudAccessControl.deleteTenant(tenant, credentials); + fail(); + } catch (ForbiddenException ignored) {} + billingController.deleteLineItem("line-item-id"); + + // Forbidden if uncommited invoice exists + var invoice = mock(Invoice.class); + when(invoice.sum()).thenReturn(BigDecimal.TEN); + billingController.addInvoice(tenant, invoice, false); + try { + cloudAccessControl.deleteTenant(tenant, credentials); + fail(); + } catch (ForbiddenException ignored) {} + + } +}
\ No newline at end of file |