@@ -15,12 +15,12 @@ import bubble.model.bill.BubblePlan; | |||
import bubble.model.cloud.BubbleNetwork; | |||
import bubble.model.cloud.BubbleNetworkState; | |||
import bubble.model.cloud.CloudService; | |||
import bubble.model.cloud.HostnameValidationResult; | |||
import bubble.notify.payment.PaymentValidationResult; | |||
import bubble.server.BubbleConfiguration; | |||
import bubble.service.bill.RefundService; | |||
import bubble.service.cloud.NetworkService; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.cobbzilla.wizard.validation.ValidationResult; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Repository; | |||
@@ -79,8 +79,9 @@ public class AccountPlanDAO extends AccountOwnedEntityDAO<AccountPlan> { | |||
} | |||
@Override public Object preCreate(AccountPlan accountPlan) { | |||
final ValidationResult errors = validateHostname(accountPlan, accountDAO, networkDAO); | |||
final HostnameValidationResult errors = validateHostname(accountPlan, accountDAO, networkDAO); | |||
if (errors.isInvalid()) throw invalidEx(errors); | |||
if (errors.hasSuggestedName()) accountPlan.setName(errors.getSuggestedName()); | |||
if (configuration.paymentsEnabled()) { | |||
if (!accountPlan.hasPaymentMethodObject()) throw invalidEx("err.paymentMethod.required"); | |||
@@ -14,7 +14,6 @@ import bubble.service.boot.SelfNodeService; | |||
import bubble.service.cloud.NetworkService; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.cobbzilla.wizard.model.IdentifiableBase; | |||
import org.cobbzilla.wizard.validation.ValidationResult; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Repository; | |||
@@ -42,8 +41,9 @@ public class BubbleNetworkDAO extends AccountOwnedEntityDAO<BubbleNetwork> { | |||
@Override public Object preCreate(BubbleNetwork network) { | |||
if (!network.hasForkHost()) { | |||
final ValidationResult errors = validateHostname(network, accountDAO, this); | |||
final HostnameValidationResult errors = validateHostname(network, accountDAO, this); | |||
if (errors.isInvalid()) throw invalidEx(errors); | |||
if (errors.hasSuggestedName()) network.setName(errors.getSuggestedName()); | |||
} | |||
final AnsibleInstallType installType = network.hasForkHost() && configuration.isSageLauncher() | |||
? AnsibleInstallType.sage | |||
@@ -25,7 +25,6 @@ import org.cobbzilla.wizard.model.IdentifiableBase; | |||
import org.cobbzilla.wizard.model.entityconfig.EntityFieldType; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.*; | |||
import org.cobbzilla.wizard.validation.HasValue; | |||
import org.cobbzilla.wizard.validation.ValidationResult; | |||
import org.hibernate.annotations.Type; | |||
import javax.persistence.*; | |||
@@ -180,16 +179,10 @@ public class BubbleNetwork extends IdentifiableBase implements HasNetwork, HasBu | |||
return die("hostFromFqdn("+fqdn+"): expected suffix ."+getNetworkDomain()); | |||
} | |||
public static ValidationResult validateHostname(HasNetwork request, | |||
AccountDAO accountDAO, | |||
BubbleNetworkDAO networkDAO) { | |||
return validateHostname(request, new ValidationResult(), accountDAO, networkDAO); | |||
} | |||
public static ValidationResult validateHostname(HasNetwork request, | |||
ValidationResult errors, | |||
AccountDAO accountDAO, | |||
BubbleNetworkDAO networkDAO) { | |||
public static HostnameValidationResult validateHostname(HasNetwork request, | |||
AccountDAO accountDAO, | |||
BubbleNetworkDAO networkDAO) { | |||
HostnameValidationResult errors = new HostnameValidationResult(); | |||
if (!request.hasName()) { | |||
errors.addViolation("err.name.required"); | |||
} else { | |||
@@ -203,15 +196,20 @@ public class BubbleNetwork extends IdentifiableBase implements HasNetwork, HasBu | |||
} else if (name.length() < NETWORK_NAME_MINLEN) { | |||
errors.addViolation("err.name.tooShort"); | |||
} else { | |||
final BubbleNetwork network = networkDAO.findByNameAndDomainUuid(name, request.getDomain()); | |||
if (network != null && !network.getUuid().equals(request.getNetwork())) { | |||
errors.addViolation("err.name.alreadyInUse"); | |||
} else { | |||
final Account acct = accountDAO.findByName(name); | |||
if (acct != null && !acct.getUuid().equals(request.getAccount())) { | |||
errors.addViolation("err.name.reservedForAccount"); | |||
for (int i=0; i<100; i++) { | |||
final String tryName = i == 0 ? name : name + i; | |||
final BubbleNetwork network = networkDAO.findByNameAndDomainUuid(tryName, request.getDomain()); | |||
if (network != null && !network.getUuid().equals(request.getNetwork())) { | |||
continue; | |||
} else { | |||
final Account acct = accountDAO.findByName(name); | |||
if (acct != null && !acct.getUuid().equals(request.getAccount())) { | |||
continue; | |||
} | |||
} | |||
return tryName.equals(name) ? errors : errors.setSuggestedName(tryName); | |||
} | |||
errors.addViolation("err.name.alreadyInUse"); | |||
} | |||
} | |||
return errors; | |||
@@ -0,0 +1,21 @@ | |||
/** | |||
* Copyright (c) 2020 Bubble, Inc. All rights reserved. | |||
* For personal (non-commercial) use, see license: https://bubblev.com/bubble-license/ | |||
*/ | |||
package bubble.model.cloud; | |||
import lombok.Getter; | |||
import lombok.NoArgsConstructor; | |||
import lombok.Setter; | |||
import lombok.experimental.Accessors; | |||
import org.cobbzilla.wizard.validation.ValidationResult; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.empty; | |||
@NoArgsConstructor @Accessors(chain=true) | |||
public class HostnameValidationResult extends ValidationResult { | |||
@Getter @Setter private String suggestedName; | |||
public boolean hasSuggestedName () { return !empty(suggestedName); } | |||
} |
@@ -20,10 +20,7 @@ import bubble.model.bill.AccountPaymentMethod; | |||
import bubble.model.bill.AccountPlan; | |||
import bubble.model.bill.BubblePlan; | |||
import bubble.model.bill.PaymentMethodType; | |||
import bubble.model.cloud.BubbleDomain; | |||
import bubble.model.cloud.BubbleFootprint; | |||
import bubble.model.cloud.BubbleNetwork; | |||
import bubble.model.cloud.CloudService; | |||
import bubble.model.cloud.*; | |||
import bubble.resources.account.AccountOwnedResource; | |||
import bubble.server.BubbleConfiguration; | |||
import bubble.service.account.StandardAuthenticatorService; | |||
@@ -146,11 +143,11 @@ public class AccountPlansResource extends AccountOwnedResource<AccountPlan, Acco | |||
errors.addViolation("err.forkHost.domainMismatch"); | |||
} else if (domain != null) { | |||
request.setName(domain.networkFromFqdn(forkHost, errors)); | |||
validateHostname(request, errors, accountDAO, networkDAO); | |||
validateName(request, errors); | |||
} | |||
} | |||
} else { | |||
validateHostname(request, errors, accountDAO, networkDAO); | |||
validateName(request, errors); | |||
} | |||
final BubblePlan plan = planDAO.findByAccountOrParentAndId(caller, request.getPlan()); | |||
@@ -224,6 +221,15 @@ public class AccountPlansResource extends AccountOwnedResource<AccountPlan, Acco | |||
return request; | |||
} | |||
private void validateName(AccountPlan request, ValidationResult errors) { | |||
final HostnameValidationResult hostnameErrors = validateHostname(request, accountDAO, networkDAO); | |||
if (hostnameErrors.isInvalid()) { | |||
errors.addAll(hostnameErrors); | |||
} else if (hostnameErrors.hasSuggestedName()) { | |||
request.setName(hostnameErrors.getSuggestedName()); | |||
} | |||
} | |||
private CloudService selectStorageCloud(ContainerRequest ctx, Account caller, AccountPlan request, ValidationResult result) { | |||
final List<CloudService> storageClouds = cloudDAO.findByAccountAndType(caller.getUuid(), CloudServiceType.storage); | |||
@@ -170,7 +170,6 @@ err.name.tooShort=Name must be at least 4 characters | |||
err.name.tooLong=Name cannot be longer than 100 characters | |||
err.name.planMaxAccountLimit=No more accounts can be created on this plan. Upgrade your plan or delete some accounts. | |||
err.name.alreadyInUse=Name is already in use | |||
err.name.reservedForAccount=Name is already in use | |||
err.name.reserved=Name is reserved | |||
err.name.invalid=Name is invalid | |||
err.name.networkNameAlreadyExists=Name is already in use | |||