diff --git a/bubble-server/src/main/java/bubble/app/analytics/TrafficAnalyticsAppDataDriver.java b/bubble-server/src/main/java/bubble/app/analytics/TrafficAnalyticsAppDataDriver.java index 23251f3e..5418611e 100644 --- a/bubble-server/src/main/java/bubble/app/analytics/TrafficAnalyticsAppDataDriver.java +++ b/bubble-server/src/main/java/bubble/app/analytics/TrafficAnalyticsAppDataDriver.java @@ -11,8 +11,8 @@ import bubble.model.app.BubbleApp; import bubble.model.app.config.AppDataDriverBase; import bubble.model.app.config.AppDataView; import bubble.model.device.Device; +import bubble.rule.TrafficRecord; import bubble.rule.analytics.TrafficAnalyticsRuleDriver; -import bubble.rule.analytics.TrafficRecord; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.cobbzilla.util.network.NetworkUtil; @@ -125,7 +125,7 @@ public class TrafficAnalyticsAppDataDriver extends AppDataDriverBase { + fileExt(TYPICAL_WEB_TYPES[nextInt(0, TYPICAL_WEB_TYPES.length)])) .setReferer("NONE") .setAccountUuid(caller.getUuid()) - .setAccountName(caller.getName()) + .setAccountEmail(caller.getEmail()) .setDeviceUuid(device.getUuid()) .setDeviceName(device.getName()), getRecentTraffic()); diff --git a/bubble-server/src/main/java/bubble/cloud/CloudRegion.java b/bubble-server/src/main/java/bubble/cloud/CloudRegion.java index 1a79aa75..ce0b9f0f 100644 --- a/bubble-server/src/main/java/bubble/cloud/CloudRegion.java +++ b/bubble-server/src/main/java/bubble/cloud/CloudRegion.java @@ -7,11 +7,12 @@ package bubble.cloud; import bubble.cloud.geoLocation.GeoLocation; import lombok.Getter; import lombok.Setter; +import lombok.ToString; import lombok.experimental.Accessors; import static java.util.UUID.randomUUID; -@Accessors(chain=true) +@Accessors(chain=true) @ToString(of={"cloud","name","internalName"}) public class CloudRegion { public static final CloudRegion[] EMPTY_REGIONS = new CloudRegion[0]; diff --git a/bubble-server/src/main/java/bubble/cloud/email/EmailServiceDriver.java b/bubble-server/src/main/java/bubble/cloud/email/EmailServiceDriver.java index ee3e6cd6..875b0aa5 100644 --- a/bubble-server/src/main/java/bubble/cloud/email/EmailServiceDriver.java +++ b/bubble-server/src/main/java/bubble/cloud/email/EmailServiceDriver.java @@ -32,7 +32,6 @@ public interface EmailServiceDriver extends AuthenticationDriver { final RenderedEmail email = new RenderedEmail(ctx); email.setToEmail(contact.getInfo()); - email.setToName(account.getName()); email.setFromEmail(AuthenticationDriver.render("fromEmail", ctx, message, configuration, templatePath)); email.setFromName(AuthenticationDriver.render("fromName", ctx, message, configuration, templatePath)); email.setSubject(AuthenticationDriver.render("subject", ctx, message, configuration, templatePath)); diff --git a/bubble-server/src/main/java/bubble/cloud/payment/promo/firstMonthFree/FirstMonthFreePaymentDriver.java b/bubble-server/src/main/java/bubble/cloud/payment/promo/firstMonthFree/FirstMonthFreePaymentDriver.java index c57a6111..a9b15848 100644 --- a/bubble-server/src/main/java/bubble/cloud/payment/promo/firstMonthFree/FirstMonthFreePaymentDriver.java +++ b/bubble-server/src/main/java/bubble/cloud/payment/promo/firstMonthFree/FirstMonthFreePaymentDriver.java @@ -29,7 +29,7 @@ public class FirstMonthFreePaymentDriver extends PromotionalPaymentDriverBase existingCreditPaymentMethods = paymentMethodDAO.findByAccountAndCloud(caller.getUuid(), promo.getCloud()); if (!empty(existingCreditPaymentMethods)) { - log.warn("applyPromo: promo="+promo.getName()+", account="+caller.getName()+", account already has one of these promos applied"); + log.warn("applyPromo: promo="+promo.getName()+", account="+caller.getEmail()+", account already has one of these promos applied"); return true; // promo has already been applied, return true } paymentMethodDAO.create(new AccountPaymentMethod() diff --git a/bubble-server/src/main/java/bubble/cloud/payment/promo/referralMonthFree/ReferralMonthFreePaymentDriver.java b/bubble-server/src/main/java/bubble/cloud/payment/promo/referralMonthFree/ReferralMonthFreePaymentDriver.java index cd5c9272..f2a55ee3 100644 --- a/bubble-server/src/main/java/bubble/cloud/payment/promo/referralMonthFree/ReferralMonthFreePaymentDriver.java +++ b/bubble-server/src/main/java/bubble/cloud/payment/promo/referralMonthFree/ReferralMonthFreePaymentDriver.java @@ -41,7 +41,7 @@ public class ReferralMonthFreePaymentDriver extends PromotionalPaymentDriverBase // caller must not have any bills final int billCount = billDAO.countByAccount(caller.getUuid()); if (billCount != 0) { - log.warn("applyReferralPromo: promo="+promo.getName()+", account="+caller.getName()+", account must have no Bills, found "+billCount+" bills"); + log.warn("applyReferralPromo: promo="+promo.getName()+", account="+caller.getEmail()+", account must have no Bills, found "+billCount+" bills"); return false; } @@ -51,7 +51,7 @@ public class ReferralMonthFreePaymentDriver extends PromotionalPaymentDriverBase // It's OK for the referredFrom user to have many of these, as long as there is not one for this user for (AccountPaymentMethod apm : referredFromCreditPaymentMethods) { if (apm.getPaymentInfo().equals(caller.getUuid())) { - log.error("applyReferralPromo: promo="+promo.getName()+", account="+caller.getName()+", referredFrom="+referredFrom.getName()+" has already referred this caller"); + log.error("applyReferralPromo: promo="+promo.getName()+", account="+caller.getEmail()+", referredFrom="+referredFrom.getEmail()+" has already referred this caller"); return false; } } @@ -59,18 +59,18 @@ public class ReferralMonthFreePaymentDriver extends PromotionalPaymentDriverBase // does the caller already have one of these? final List existingCreditPaymentMethods = paymentMethodDAO.findByAccountAndCloud(caller.getUuid(), promo.getCloud()); if (!empty(existingCreditPaymentMethods)) { - log.warn("applyReferralPromo: promo="+promo.getName()+", account="+caller.getName()+", account already has one of these promos applied"); + log.warn("applyReferralPromo: promo="+promo.getName()+", account="+caller.getEmail()+", account already has one of these promos applied"); return true; // promo has already been applied, return true } // sanity check before using final ReferralCode ref = referralCodeDAO.findByUuid(referralCode.getUuid()); if (ref == null) { - log.warn("applyReferralPromo: promo="+promo.getName()+", account="+caller.getName()+", referralCode "+referralCode.getUuid()+" was not found"); + log.warn("applyReferralPromo: promo="+promo.getName()+", account="+caller.getEmail()+", referralCode "+referralCode.getUuid()+" was not found"); return false; } if (ref.claimed()) { - log.warn("applyReferralPromo: promo="+promo.getName()+", account="+caller.getName()+", referralCode "+referralCode.getUuid()+" has already been claimed by "+ref.getClaimedByUuid()); + log.warn("applyReferralPromo: promo="+promo.getName()+", account="+caller.getEmail()+", referralCode "+referralCode.getUuid()+" has already been claimed by "+ref.getClaimedByUuid()); return false; } diff --git a/bubble-server/src/main/java/bubble/dao/account/AccountDAO.java b/bubble-server/src/main/java/bubble/dao/account/AccountDAO.java index 69158d17..59e3e9bd 100644 --- a/bubble-server/src/main/java/bubble/dao/account/AccountDAO.java +++ b/bubble-server/src/main/java/bubble/dao/account/AccountDAO.java @@ -5,6 +5,7 @@ package bubble.dao.account; import bubble.cloud.CloudServiceDriver; +import bubble.cloud.CloudServiceType; import bubble.cloud.compute.ComputeNodeSizeType; import bubble.dao.account.message.AccountMessageDAO; import bubble.dao.app.*; @@ -46,6 +47,7 @@ import static bubble.model.account.AutoUpdatePolicy.EMPTY_AUTO_UPDATE_POLICY; import static bubble.server.BubbleConfiguration.getDEFAULT_LOCALE; import static java.lang.Thread.currentThread; import static java.util.concurrent.TimeUnit.MINUTES; +import static org.cobbzilla.util.daemon.ZillaRuntime.bool; import static org.cobbzilla.util.daemon.ZillaRuntime.daemon; import static org.cobbzilla.wizard.model.IdentifiableBase.CTIME_ASC; import static org.cobbzilla.wizard.resources.ResourceUtil.invalidEx; @@ -75,18 +77,25 @@ public class AccountDAO extends AbstractCRUDDAO implements SqlViewSearc @Autowired private ReferralCodeDAO referralCodeDAO; public Account newAccount(Request req, Account caller, AccountRegistration request, Account parent) { + final AccountContact contact = new AccountContact() + .setType(CloudServiceType.email) + .setInfo(request.getEmail()) + .setRemovable(false) + .setReceiveInformationalMessages(bool(request.getReceiveInformationalMessages())) + .setReceivePromotionalMessages(bool(request.getReceivePromotionalMessages())); + return create(new Account(request) .setAdmin(caller != null && caller.admin() && request.admin()) // only admins can create other admins .setRemoteHost(getRemoteHost(req)) .setParent(parent.getUuid()) - .setPolicy(new AccountPolicy().setContact(request.getContact()))); + .setPolicy(new AccountPolicy().setContact(contact, null, configuration))); } - public Account findByName(String name) { return findByUniqueField("name", name); } + public Account findByEmail(String email) { return findByUniqueField("email", email.trim()); } public Account findById(String id) { final Account found = findByUuid(id); - return found != null ? found : findByUniqueField("name", id); + return found != null ? found : findByUniqueField("email", id); } @Override public Object preCreate(Account account) { @@ -97,11 +106,13 @@ public class AccountDAO extends AbstractCRUDDAO implements SqlViewSearc if (plan != null && plan.hasMaxAccounts()) { final int numAccounts = countNotDeleted(); if (numAccounts >= plan.getMaxAccounts()) { - throw invalidEx("err.name.planMaxAccountLimit", "Account limit for plan reached (max "+plan.getMaxAccounts()+" accounts)"); + throw invalidEx("err.plan.planMaxAccountLimit", "Account limit for plan reached (max "+plan.getMaxAccounts()+" accounts)"); } } - final ValidationResult result = account.validateName(); + // if activated is false, then we are creating the root account + // do not validate, it's not a valid email address and that is ok. + final ValidationResult result = !activated() ? new ValidationResult() : account.validateEmail(); if (result.isInvalid()) throw invalidEx(result); if (account.getAutoUpdatePolicy() == null) { diff --git a/bubble-server/src/main/java/bubble/model/account/Account.java b/bubble-server/src/main/java/bubble/model/account/Account.java index 2d79a469..6352d70e 100644 --- a/bubble-server/src/main/java/bubble/model/account/Account.java +++ b/bubble-server/src/main/java/bubble/model/account/Account.java @@ -40,9 +40,7 @@ import javax.persistence.Embedded; import javax.persistence.Entity; import javax.persistence.Transient; import javax.validation.constraints.Size; -import java.util.Arrays; import java.util.List; -import java.util.regex.Pattern; import static bubble.ApiConstants.ACCOUNTS_ENDPOINT; import static bubble.server.BubbleConfiguration.getDEFAULT_LOCALE; @@ -50,6 +48,7 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.SECONDS; import static org.cobbzilla.util.daemon.ZillaRuntime.*; import static org.cobbzilla.util.reflect.ReflectionUtil.copy; +import static org.cobbzilla.util.string.ValidationRegexes.EMAIL_PATTERN; import static org.cobbzilla.util.system.Sleep.sleep; import static org.cobbzilla.util.time.TimeUtil.formatDuration; import static org.cobbzilla.wizard.model.crypto.EncryptedTypes.ENCRYPTED_STRING; @@ -60,8 +59,8 @@ import static org.cobbzilla.wizard.model.entityconfig.annotations.ECForeignKeySe import static org.cobbzilla.wizard.resources.ResourceUtil.invalidEx; @ECType(root=true) -@ECTypeURIs(baseURI=ACCOUNTS_ENDPOINT, listFields={"name", "url", "description", "admin", "suspended"}, isDeleteDefined=false) -@ECTypeChildren(uriPrefix=ACCOUNTS_ENDPOINT+"/{Account.name}", value={ +@ECTypeURIs(baseURI=ACCOUNTS_ENDPOINT, listFields={"email", "url", "description", "admin", "suspended"}, isDeleteDefined=false) +@ECTypeChildren(uriPrefix=ACCOUNTS_ENDPOINT+"/{Account.email}", value={ @ECTypeChild(type=Device.class, backref="account"), @ECTypeChild(type=RuleDriver.class, backref="account"), @ECTypeChild(type=BubbleApp.class, backref="account"), @@ -89,8 +88,7 @@ public class Account extends IdentifiableBaseParentEntity implements TokenPrinci "name", "referralCode", "termsAgreed", "preferredPlan"); public static final String ROOT_USERNAME = "root"; - public static final int NAME_MIN_LENGTH = 4; - public static final int NAME_MAX_LENGTH = 100; + public static final int EMAIL_MAX_LENGTH = 100; public static Account sageMask(Account sage) { final Account masked = new Account(sage) @@ -106,22 +104,14 @@ public class Account extends IdentifiableBaseParentEntity implements TokenPrinci public static String accountField(String table) { return table.equalsIgnoreCase("account") ? UUID : "account"; } @ECSearchable(filter=true) @ECField(index=10) - @HasValue(message="err.name.required") - @ECIndex(unique=true) @Column(nullable=false, updatable=false, length=NAME_MAX_LENGTH) - @Getter private String name; - public Account setName (String n) { this.name = n == null ? null : n.toLowerCase(); return this; } - public boolean hasName () { return !empty(name); } + @HasValue(message="err.email.required") + @ECIndex(unique=true) @Column(nullable=false, updatable=false, length=EMAIL_MAX_LENGTH) + @Getter private String email; + public Account setEmail(String n) { this.email = n == null ? null : n.toLowerCase().trim(); return this; } + public boolean hasEmail() { return !empty(email); } - public static final Pattern VALID_NAME_PATTERN = Pattern.compile("^[A-Za-z][-\\.A-Za-z0-9_]+$"); - public boolean hasInvalidName() { return hasName() && !VALID_NAME_PATTERN.matcher(getName()).matches(); } - - private static final List RESERVED_NAMES = Arrays.asList( - "root", "postmaster", "hostmaster", "webmaster", - "dns", "dnscrypt", "dnscrypt-proxy", "ftp", "www", "www-data", "postgres", "ipfs", - "redis", "nginx", "mitmproxy", "mitmdump", "algo", "algovpn"); - public boolean hasReservedName () { return hasName() && isReservedName(getName()); } - - public static boolean isReservedName(String name) { return RESERVED_NAMES.contains(name); } + @Override @Transient public String getName() { return getEmail(); } + public Account setName(String n) { return setEmail(n); } // make this updatable if we ever want accounts to be able to change parents // there might be a lot more involved in that action though (read-only parent objects that will no longer be visible, must be copied in?) @@ -244,7 +234,7 @@ public class Account extends IdentifiableBaseParentEntity implements TokenPrinci public Account(Account other) { copy(this, other, CREATE_FIELDS); } public Account(ActivationRequest request) { - setName(request.getName()); + setEmail(request.getEmail()); setHashedPassword(new HashedPassword(request.getPassword())); setAdmin(true); setDescription(request.hasDescription() ? request.getDescription() : "root user"); @@ -253,7 +243,7 @@ public class Account extends IdentifiableBaseParentEntity implements TokenPrinci } public Account(AccountRegistration request) { - setName(request.getName()); + setEmail(request.getEmail()); setHashedPassword(new HashedPassword(request.getPassword())); setTermsAgreed(request.getTermsAgreed()); setPreferredPlan(request.getPreferredPlan()); @@ -289,21 +279,17 @@ public class Account extends IdentifiableBaseParentEntity implements TokenPrinci @Transient @Getter @Setter private transient String remoteHost; @Transient @JsonIgnore @Getter @Setter private transient Boolean verifyContact; - public ValidationResult validateName () { + public ValidationResult validateEmail() { return validateEmail(getEmail()); } + + public static ValidationResult validateEmail(String email) { final ValidationResult result = new ValidationResult(); - if (!hasName()) { - result.addViolation("err.name.required"); + if (empty(email)) { + result.addViolation("err.email.required"); } else { - if (getName().length() < NAME_MIN_LENGTH) { - result.addViolation("err.name.tooShort"); - } else if (getName().length() > NAME_MAX_LENGTH) { - result.addViolation("err.name.tooLong"); - } - if (!admin() && hasReservedName()) { - result.addViolation("err.name.reserved"); - } - if (hasInvalidName()) { - result.addViolation("err.name.regexFailed"); + if (!EMAIL_PATTERN.matcher(email).matches()) { + result.addViolation("err.email.invalid"); + } else if (email.length() > EMAIL_MAX_LENGTH) { + result.addViolation("err.email.tooLong"); } } return result; diff --git a/bubble-server/src/main/java/bubble/model/account/AccountContact.java b/bubble-server/src/main/java/bubble/model/account/AccountContact.java index 2e67d1e1..6c944916 100644 --- a/bubble-server/src/main/java/bubble/model/account/AccountContact.java +++ b/bubble-server/src/main/java/bubble/model/account/AccountContact.java @@ -5,6 +5,7 @@ package bubble.model.account; import bubble.cloud.CloudServiceType; +import bubble.dao.account.AccountDAO; import bubble.model.account.message.AccountAction; import bubble.model.account.message.AccountMessage; import bubble.model.account.message.AccountMessageType; @@ -40,7 +41,7 @@ import static org.cobbzilla.wizard.resources.ResourceUtil.invalidEx; public class AccountContact implements Serializable { public static final int MAX_NICK_LENGTH = 100; - public static final String[] UPDATE_EXCLUDE_FIELDS = {UUID, "type", "info"}; + public static final String[] UPDATE_EXCLUDE_FIELDS = {UUID, "type", "info", "removable"}; public AccountContact(AccountContact other) { copy(this, other); } @@ -67,12 +68,15 @@ public class AccountContact implements Serializable { @Getter @Setter private CloudServiceType type; @JsonIgnore public boolean isAuthenticator () { return type == CloudServiceType.authenticator; } @JsonIgnore public boolean isNotAuthenticator () { return !isAuthenticator(); } - @JsonIgnore public boolean getIsEmail () { return type == CloudServiceType.email; } - @JsonIgnore public boolean getIsSms () { return type == CloudServiceType.sms; } + @JsonIgnore public boolean isEmail () { return type == CloudServiceType.email; } + @JsonIgnore public boolean isSms () { return type == CloudServiceType.sms; } @Getter @Setter private Boolean verified = null; public boolean verified () { return bool(verified); } + @Getter @Setter private Boolean removable = true; + public boolean removable () { return bool(removable); } + @Getter @Setter private Boolean requiredForNetworkOperations = true; @Getter @Setter private Boolean requiredForAccountOperations = true; @@ -107,7 +111,12 @@ public class AccountContact implements Serializable { if (!c.hasUuid()) c.initUuid(); - if (contacts == null) contacts = new AccountContact[0]; + if (contacts == null) { + contacts = new AccountContact[0]; + c.setRemovable(false); // first contact is not removable + } else { + c.setRemovable(true); // all other contacts are removable + } final AccountContact existing = Arrays.stream(contacts).filter(contactMatch(c)).findFirst().orElse(null); if (existing != null) { // updating a contact -- cannot set authFactor if verification is still required @@ -125,6 +134,14 @@ public class AccountContact implements Serializable { existing.update(c); } else { + if (c.isEmail()) { + // has another user registered this as their primary email? + final Account registeredWithEmail = configuration.getBean(AccountDAO.class).findByEmail(c.getInfo()); + if (registeredWithEmail != null && (account == null || !registeredWithEmail.getUuid().equals(account.getUuid()))) { + throw invalidEx("err.email.registered", "Email already registered"); + } + } + // creating a new contact -- cannot set authFactor for contacts requiring verification if (c.getType().isVerifiableAuthenticationType() && c.authFactor()) { throw invalidEx("err.contact.unverified", "cannot set authFactor on an unverified contact; verify first"); @@ -164,10 +181,14 @@ public class AccountContact implements Serializable { .orElse(null); } - public static AccountContact[] remove(AccountContact contact, AccountContact[] contacts) { + public static AccountContact[] remove(Account account, AccountContact contact, AccountContact[] contacts) { if (contacts == null || contacts.length == 0) return contacts; + if (contact.getType() == CloudServiceType.email && contact.getInfo().equals(account.getEmail())) { + log.warn("remove: cannot remove account.email contact"); + return contacts; + } final List contactList = new ArrayList<>(Arrays.asList(contacts)); - return contactList.removeIf(contactMatch(contact)) + return contactList.removeIf(c -> c.removable() && contactMatch(contact).test(c)) ? contactList.toArray(new AccountContact[contacts.length-1]) : contacts; } diff --git a/bubble-server/src/main/java/bubble/model/account/AccountLoginRequest.java b/bubble-server/src/main/java/bubble/model/account/AccountLoginRequest.java new file mode 100644 index 00000000..5fac2c17 --- /dev/null +++ b/bubble-server/src/main/java/bubble/model/account/AccountLoginRequest.java @@ -0,0 +1,18 @@ +package bubble.model.account; + +import org.cobbzilla.wizard.auth.LoginRequest; + +import static org.cobbzilla.util.daemon.ZillaRuntime.empty; + +public class AccountLoginRequest extends LoginRequest { + + public String getEmail () { return getName(); } + + public AccountLoginRequest setEmail(String email) { + setName(email); + return this; + } + + public boolean hasEmail () { return !empty(getEmail()); } + +} diff --git a/bubble-server/src/main/java/bubble/model/account/AccountPolicy.java b/bubble-server/src/main/java/bubble/model/account/AccountPolicy.java index bb62bdc1..758760c1 100644 --- a/bubble-server/src/main/java/bubble/model/account/AccountPolicy.java +++ b/bubble-server/src/main/java/bubble/model/account/AccountPolicy.java @@ -112,13 +112,14 @@ public class AccountPolicy extends IdentifiableBase implements HasAccount { @Transient public AccountContact[] getAccountContacts () { return accountContactsJson == null ? null : json(accountContactsJson, AccountContact[].class); } public AccountPolicy setAccountContacts(AccountContact[] contacts) { return setAccountContactsJson(contacts == null ? null : json(contacts, DB_JSON_MAPPER)); } - public AccountPolicy setContact(AccountContact c) { return setContact(c, null, null); } - public AccountPolicy setContact(AccountContact c, Account account, BubbleConfiguration configuration) { setAccountContacts(AccountContact.set(c, getAccountContacts(), account, configuration)); return this; } - public AccountPolicy removeContact(AccountContact c) { setAccountContacts(AccountContact.remove(c, getAccountContacts())); return this; } + public AccountPolicy removeContact(Account account, AccountContact c) { + setAccountContacts(AccountContact.remove(account, c, getAccountContacts())); + return this; + } public List getAllowedContacts(AccountMessage message) { if (!hasAccountContacts()) return Collections.emptyList(); diff --git a/bubble-server/src/main/java/bubble/model/account/AccountRegistration.java b/bubble-server/src/main/java/bubble/model/account/AccountRegistration.java index ff22c0df..b809ca49 100644 --- a/bubble-server/src/main/java/bubble/model/account/AccountRegistration.java +++ b/bubble-server/src/main/java/bubble/model/account/AccountRegistration.java @@ -17,12 +17,15 @@ public class AccountRegistration extends Account { @Getter @Setter private String promoCode; public boolean hasPromoCode () { return !empty(promoCode); } - @Getter @Setter private AccountContact contact; - public boolean hasContact () { return contact != null; } - @Getter @Setter private Boolean agreeToTerms = null; public boolean agreeToTerms () { return agreeToTerms != null && agreeToTerms; } + @Getter @Setter private Boolean receiveInformationalMessages = null; + public boolean receiveInformationalMessages () { return receiveInformationalMessages != null && receiveInformationalMessages; } + + @Getter @Setter private Boolean receivePromotionalMessages = null; + public boolean receivePromotionalMessages () { return receivePromotionalMessages != null && receivePromotionalMessages; } + @Getter @Setter private AccountPaymentMethod paymentMethodObject; public boolean hasPaymentMethod () { return paymentMethodObject != null; } } diff --git a/bubble-server/src/main/java/bubble/model/account/TotpBean.java b/bubble-server/src/main/java/bubble/model/account/TotpBean.java index 62c3e683..bd911248 100644 --- a/bubble-server/src/main/java/bubble/model/account/TotpBean.java +++ b/bubble-server/src/main/java/bubble/model/account/TotpBean.java @@ -18,7 +18,7 @@ public class TotpBean { public TotpBean(GoogleAuthenticatorKey creds, Account account, BubbleConfiguration configuration) { setKey(creds.getKey()); - setUrl(getOtpAuthTotpURL(configuration.getThisNetwork().getNetworkDomain(), account.getName()+"@"+configuration.getThisNetwork().getNetworkDomain(), creds)); + setUrl(getOtpAuthTotpURL(configuration.getThisNetwork().getNetworkDomain(), account.getEmail()+"/"+configuration.getThisNetwork().getNetworkDomain(), creds)); setBackupCodes(creds.getScratchCodes().toArray(new Integer[0])); } diff --git a/bubble-server/src/main/java/bubble/model/account/message/handlers/AccountVerifyHandler.java b/bubble-server/src/main/java/bubble/model/account/message/handlers/AccountVerifyHandler.java index a126a6e7..d8ffe14d 100644 --- a/bubble-server/src/main/java/bubble/model/account/message/handlers/AccountVerifyHandler.java +++ b/bubble-server/src/main/java/bubble/model/account/message/handlers/AccountVerifyHandler.java @@ -4,7 +4,9 @@ */ package bubble.model.account.message.handlers; +import bubble.dao.account.AccountDAO; import bubble.dao.account.AccountPolicyDAO; +import bubble.model.account.Account; import bubble.model.account.AccountContact; import bubble.model.account.AccountPolicy; import bubble.model.account.message.AccountMessage; @@ -16,6 +18,7 @@ import org.springframework.beans.factory.annotation.Autowired; @Slf4j public class AccountVerifyHandler implements AccountMessageCompletionHandler { + @Autowired private AccountDAO accountDAO; @Autowired private AccountPolicyDAO policyDAO; @Override public void confirm(AccountMessage message, NameAndValue[] data) { @@ -26,10 +29,19 @@ public class AccountVerifyHandler implements AccountMessageCompletionHandler { } @Override public void deny(AccountMessage message) { + final Account account = accountDAO.findByUuid(message.getAccount()); + if (account == null) { + log.warn("deny: account not found: "+message.getAccount()); + return; + } final AccountPolicy policy = policyDAO.findSingleByAccount(message.getAccount()); - final String contact = message.getRequest().getContact(); + final AccountContact contact = policy.findContact(new AccountContact().setUuid(message.getRequest().getContact())); + if (contact == null) { + log.warn("deny: contact not found in policy: "+contact); + return; + } log.info("deny: removing contact "+ contact +" from account "+message.getAccount()); - policy.removeContact(new AccountContact().setUuid(contact)); + policy.removeContact(account, contact); policyDAO.update(policy); } diff --git a/bubble-server/src/main/java/bubble/model/boot/ActivationRequest.java b/bubble-server/src/main/java/bubble/model/boot/ActivationRequest.java index cc92bb4d..648abdfb 100644 --- a/bubble-server/src/main/java/bubble/model/boot/ActivationRequest.java +++ b/bubble-server/src/main/java/bubble/model/boot/ActivationRequest.java @@ -22,9 +22,12 @@ import static org.cobbzilla.util.daemon.ZillaRuntime.empty; @NoArgsConstructor @Accessors(chain=true) public class ActivationRequest { - @HasValue(message="err.name.required") - @Getter @Setter private String name; - public boolean hasName () { return !empty(name); } + @HasValue(message="err.email.required") + @Getter @Setter private String email; + public boolean hasEmail() { return !empty(email); } + + public String getName() { return getEmail(); } + public ActivationRequest setName(String n) { return setEmail(n); } @HasValue(message="err.password.required") @Getter @Setter private String password; diff --git a/bubble-server/src/main/java/bubble/model/cloud/BubbleNetwork.java b/bubble-server/src/main/java/bubble/model/cloud/BubbleNetwork.java index de79173c..7197bf20 100644 --- a/bubble-server/src/main/java/bubble/model/cloud/BubbleNetwork.java +++ b/bubble-server/src/main/java/bubble/model/cloud/BubbleNetwork.java @@ -201,6 +201,13 @@ public class BubbleNetwork extends IdentifiableBase implements HasNetwork, HasBu return die("hostFromFqdn("+fqdn+"): expected suffix ."+getNetworkDomain()); } + private static final List RESERVED_NAMES = Arrays.asList( + "root", "postmaster", "hostmaster", "webmaster", + "dns", "dnscrypt", "dnscrypt-proxy", "ftp", "www", "www-data", "postgres", "ipfs", + "redis", "nginx", "mitmproxy", "mitmdump", "algo", "algovpn"); + + public static boolean isReservedName(String name) { return RESERVED_NAMES.contains(name); } + public static HostnameValidationResult validateHostname(HasNetwork request, AccountDAO accountDAO, BubbleNetworkDAO networkDAO) { @@ -211,7 +218,7 @@ public class BubbleNetwork extends IdentifiableBase implements HasNetwork, HasBu final String name = request.getName(); if (!validateRegexMatches(HOST_PART_PATTERN, name)) { errors.addViolation("err.name.invalid"); - } else if (Account.isReservedName(name)) { + } else if (isReservedName(name)) { errors.addViolation("err.name.reserved"); } else if (name.length() > NETWORK_NAME_MAXLEN) { errors.addViolation("err.name.length"); @@ -225,7 +232,7 @@ public class BubbleNetwork extends IdentifiableBase implements HasNetwork, HasBu log.info("validateHostname: name "+tryName+" is ineligible (network="+network.getUuid()+") exists"); continue; } else { - final Account acct = accountDAO.findByName(tryName); + final Account acct = accountDAO.findByEmail(tryName); if (acct != null && !acct.getUuid().equals(request.getAccount())) { log.info("validateHostname: name "+tryName+" is ineligible (account="+acct.getUuid()+") exists (request.account="+request.getAccount()+")"); continue; diff --git a/bubble-server/src/main/java/bubble/resources/IdentityResource.java b/bubble-server/src/main/java/bubble/resources/IdentityResource.java index ac7a31f0..dd496d44 100644 --- a/bubble-server/src/main/java/bubble/resources/IdentityResource.java +++ b/bubble-server/src/main/java/bubble/resources/IdentityResource.java @@ -55,7 +55,7 @@ public class IdentityResource { if (caller.admin()) { // only admin can find any user found = ((AccountDAO) dao).findById(id); - } else if (id.equals(caller.getUuid()) || id.equals(caller.getName())) { + } else if (id.equals(caller.getUuid()) || id.equals(caller.getEmail())) { // other callers can find themselves found = caller; } else { diff --git a/bubble-server/src/main/java/bubble/resources/account/AccountsResource.java b/bubble-server/src/main/java/bubble/resources/account/AccountsResource.java index 06ad3aef..dd5ee85d 100644 --- a/bubble-server/src/main/java/bubble/resources/account/AccountsResource.java +++ b/bubble-server/src/main/java/bubble/resources/account/AccountsResource.java @@ -46,8 +46,7 @@ import java.util.List; import java.util.Map; import static bubble.ApiConstants.*; -import static bubble.model.account.Account.ADMIN_UPDATE_FIELDS; -import static bubble.model.account.Account.validatePassword; +import static bubble.model.account.Account.*; import static bubble.resources.account.AuthResource.forgotPasswordMessage; import static org.cobbzilla.util.http.HttpContentTypes.APPLICATION_JSON; import static org.cobbzilla.wizard.resources.ResourceUtil.*; @@ -86,22 +85,24 @@ public class AccountsResource { @Context ContainerRequest ctx, AccountRegistration request) { - final AccountContext c = new AccountContext(ctx, request.getName(), true); + final AccountContext c = new AccountContext(ctx, request.getEmail(), true); // only admins can use this endpoint // regular users must use AuthResource.register if (!c.caller.admin()) return forbidden(); - if (c.account != null) return invalid("err.user.exists", "User with name "+request.getName()+" already exists", request.getName()); + if (c.account != null) { + // trying to create self -- just return self. tests sometimes do this + if (c.account.getUuid().equals(c.caller.getUuid())) return ok(c.caller); + + // not self, trying to create a user that already exists, return error + return invalid("err.user.exists", "User with name "+request.getEmail()+" already exists", request.getEmail()); + } final ValidationResult errors = new ValidationResult(); final ConstraintViolationBean passwordViolation = validatePassword(request.getPassword()); if (passwordViolation != null) errors.addViolation(passwordViolation); - if (!request.hasContact()) { - errors.addViolation("err.contact.required", "No contact information provided", request.getName()); - } else { - request.getContact().validate(errors); - } + errors.addAll(validateEmail(request.getEmail())); if (!request.agreeToTerms()) { errors.addViolation("err.terms.required", "You must agree to the legal terms to use this service"); } else { @@ -283,7 +284,7 @@ public class AccountsResource { authenticatorService.ensureAuthenticated(ctx, policy, ActionTarget.account); final AccountContact contact = policy.findContact(new AccountContact().setType(type).setInfo(info)); if (contact == null) return notFound(type.name()+"/"+info); - return ok(policyDAO.update(policy.removeContact(contact)).mask()); + return ok(policyDAO.update(policy.removeContact(c.account, contact)).mask()); } @DELETE @Path("/{id}"+EP_POLICY+EP_CONTACTS+EP_AUTHENTICATOR) @@ -297,7 +298,7 @@ public class AccountsResource { final AccountContact contact = policy.findContact(new AccountContact().setType(CloudServiceType.authenticator)); if (contact == null) return notFound(CloudServiceType.authenticator.name()); - final AccountPolicy updated = policyDAO.update(policy.removeContact(contact)).mask(); + final AccountPolicy updated = policyDAO.update(policy.removeContact(c.account, contact)).mask(); authenticatorService.flush(c.caller.getToken()); return ok(updated); @@ -313,7 +314,7 @@ public class AccountsResource { final AccountContact found = policy.findContact(new AccountContact().setUuid(uuid)); if (found == null) return notFound(uuid); - return ok(policyDAO.update(policy.removeContact(found)).mask()); + return ok(policyDAO.update(policy.removeContact(c.account, found)).mask()); } @DELETE @Path("/{id}"+EP_REQUEST) diff --git a/bubble-server/src/main/java/bubble/resources/account/AuthResource.java b/bubble-server/src/main/java/bubble/resources/account/AuthResource.java index b0becffe..404ad064 100644 --- a/bubble-server/src/main/java/bubble/resources/account/AuthResource.java +++ b/bubble-server/src/main/java/bubble/resources/account/AuthResource.java @@ -37,7 +37,6 @@ import bubble.service.cloud.DeviceIdService; import bubble.service.notify.NotificationService; import lombok.extern.slf4j.Slf4j; import org.cobbzilla.util.collection.NameAndValue; -import org.cobbzilla.wizard.auth.LoginRequest; import org.cobbzilla.wizard.stream.FileSendableResource; import org.cobbzilla.wizard.validation.ConstraintViolationBean; import org.cobbzilla.wizard.validation.SimpleViolationException; @@ -133,7 +132,7 @@ public class AuthResource { if (accountDAO.activated()) { return invalid("err.activation.alreadyDone", "activation has already been done"); } - if (!request.hasName()) return invalid("err.name.required", "name is required"); + if (!request.hasEmail()) return invalid("err.email.required", "email is required"); if (!request.hasPassword()) return invalid("err.password.required", "password is required"); if (request.getPassword().contains("{{") && request.getPassword().contains("}}")) { @@ -212,21 +211,15 @@ public class AuthResource { request.setAdmin(false); // cannot register admins, they must be created - final ValidationResult errors = request.validateName(); + final ValidationResult errors = request.validateEmail(); if (errors.isValid()) { - final Account existing = accountDAO.findByName(request.getName()); - if (existing != null) errors.addViolation("err.name.registered", "Name is already registered: ", request.getName()); + final Account existing = accountDAO.findByEmail(request.getEmail()); + if (existing != null) errors.addViolation("err.name.registered", "Name is already registered: ", request.getEmail()); } final ConstraintViolationBean passwordViolation = validatePassword(request.getPassword()); if (passwordViolation != null) errors.addViolation(passwordViolation); - if (!request.hasContact()) { - errors.addViolation("err.contact.required", "No contact information provided", request.getName()); - } else { - request.getContact().validate(errors); - } - if (!request.agreeToTerms()) { errors.addViolation("err.terms.required", "You must agree to the legal terms to use this service"); } else { @@ -301,21 +294,21 @@ public class AuthResource { @POST @Path(EP_LOGIN) public Response login(@Context Request req, @Context ContainerRequest ctx, - LoginRequest request, + AccountLoginRequest request, @QueryParam("k") String unlockKey) { - if (!request.hasName()) return invalid("err.name.required", "name is required"); + if (!request.hasEmail()) return invalid("err.email.required", "email is required"); if (!request.hasPassword()) return invalid("err.password.required", "password is required"); - final Account account = accountDAO.findByName(request.getName()); - if (account == null || account.deleted()) return notFound(request.getName()); + final Account account = accountDAO.findByEmail(request.getEmail()); + if (account == null || account.deleted()) return notFound(request.getEmail()); if (!account.getHashedPassword().isCorrectPassword(request.getPassword())) { - return notFound(request.getName()); + return notFound(request.getEmail()); } if (account.suspended()) return invalid("err.account.suspended"); boolean isUnlock = false; if (account.locked()) { if (!accountDAO.locked()) { - log.info("login: account "+account.getName()+" was locked, but system is unlocked, unlocking again"); + log.info("login: account "+account.getEmail()+" was locked, but system is unlocked, unlocking again"); accountDAO.unlock(); } else { @@ -360,7 +353,7 @@ public class AuthResource { ); } return ok(new Account() - .setName(account.getName()) + .setEmail(account.getEmail()) .setLoginRequest(loginRequest != null ? loginRequest.getUuid() : null) .setMultifactorAuth(AccountContact.mask(authFactors))); } @@ -387,9 +380,9 @@ public class AuthResource { @POST @Path(EP_FORGOT_PASSWORD) public Response forgotPassword(@Context Request req, @Context ContainerRequest ctx, - LoginRequest request) { - if (!request.hasName()) return invalid("err.name.required"); - final Account account = accountDAO.findById(request.getName()); + AccountLoginRequest request) { + if (!request.hasEmail()) return invalid("err.email.required"); + final Account account = accountDAO.findById(request.getEmail()); if (account == null) return ok(); accountMessageDAO.create(forgotPasswordMessage(req, account, configuration)); diff --git a/bubble-server/src/main/java/bubble/resources/message/MessagesResource.java b/bubble-server/src/main/java/bubble/resources/message/MessagesResource.java index 4eb8b954..6bc0ef9b 100644 --- a/bubble-server/src/main/java/bubble/resources/message/MessagesResource.java +++ b/bubble-server/src/main/java/bubble/resources/message/MessagesResource.java @@ -103,9 +103,9 @@ public class MessagesResource { if (!messageCache.containsKey(cacheKey)) { final Properties props; if (isAppsGroup) { - if (log.isDebugEnabled()) log.debug("loadMessages: loading app messages for caller="+caller.getName()+", locale="+locale); + if (log.isDebugEnabled()) log.debug("loadMessages: loading app messages for caller="+caller.getEmail()+", locale="+locale); props = appMessageService.loadAppMessages(caller, locale); - if (log.isDebugEnabled()) log.debug("loadMessages: loaded app messages for caller="+caller.getName()+", locale="+locale+", props.size="+props.size()); + if (log.isDebugEnabled()) log.debug("loadMessages: loaded app messages for caller="+caller.getEmail()+", locale="+locale+", props.size="+props.size()); } else { props = new Properties(); props.load(new BufferedReader(new InputStreamReader(loadResourceAsStream(MESSAGE_RESOURCE_BASE + locale + MESSAGE_RESOURCE_PATH + group + "/" + RESOURCE_MESSAGES_PROPS), UTF8cs))); diff --git a/bubble-server/src/main/java/bubble/rule/TrafficRecord.java b/bubble-server/src/main/java/bubble/rule/TrafficRecord.java index a9e010e9..66402faf 100644 --- a/bubble-server/src/main/java/bubble/rule/TrafficRecord.java +++ b/bubble-server/src/main/java/bubble/rule/TrafficRecord.java @@ -2,7 +2,7 @@ * Copyright (c) 2020 Bubble, Inc. All rights reserved. * For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ */ -package bubble.rule.analytics; +package bubble.rule; import bubble.model.account.Account; import bubble.model.device.Device; @@ -22,7 +22,7 @@ public class TrafficRecord { @Getter @Setter private long requestTime = now(); @Getter @Setter private String action; @Getter @Setter private String accountUuid; - @Getter @Setter private String accountName; + @Getter @Setter private String accountEmail; @Getter @Setter private String deviceUuid; @Getter @Setter private String deviceName; @Getter @Setter private String ip; @@ -32,7 +32,7 @@ public class TrafficRecord { @Getter @Setter private String referer; public TrafficRecord(FilterMatchersRequest filter, Account account, Device device) { - setAccountName(account == null ? null : account.getName()); + setAccountEmail(account == null ? null : account.getEmail()); setAccountUuid(account == null ? null : account.getUuid()); setDeviceName(device == null ? null : device.getName()); setDeviceUuid(device == null ? null : device.getUuid()); diff --git a/bubble-server/src/main/java/bubble/rule/analytics/TrafficAnalyticsRuleDriver.java b/bubble-server/src/main/java/bubble/rule/analytics/TrafficAnalyticsRuleDriver.java index 2bb2de08..29f9e403 100644 --- a/bubble-server/src/main/java/bubble/rule/analytics/TrafficAnalyticsRuleDriver.java +++ b/bubble-server/src/main/java/bubble/rule/analytics/TrafficAnalyticsRuleDriver.java @@ -10,6 +10,7 @@ import bubble.model.device.Device; import bubble.resources.stream.FilterMatchersRequest; import bubble.rule.AbstractAppRuleDriver; import bubble.rule.FilterMatchDecision; +import bubble.rule.TrafficRecord; import bubble.service.stream.AppRuleHarness; import lombok.Getter; import lombok.extern.slf4j.Slf4j; diff --git a/bubble-server/src/main/java/bubble/server/listener/BubbleFirstTimeListener.java b/bubble-server/src/main/java/bubble/server/listener/BubbleFirstTimeListener.java index b6d91602..8cab31d3 100644 --- a/bubble-server/src/main/java/bubble/server/listener/BubbleFirstTimeListener.java +++ b/bubble-server/src/main/java/bubble/server/listener/BubbleFirstTimeListener.java @@ -64,7 +64,7 @@ public class BubbleFirstTimeListener extends RestServerLifecycleListenerBase>", + "email": "<>", "password": "<>", "admin": "<>", - "agreeToTerms": true, - "contact": {"type": "email", "info": "<>"} + "agreeToTerms": true } }, "response": { @@ -56,7 +54,7 @@ "session": "new", "uri": "auth/login", "entity": { - "name": "<>", + "name": "<>", "password": "<>" } }, @@ -96,7 +94,7 @@ "request": { "session": "new", "uri": "auth/approve/{{userInbox.[0].ctx.confirmationToken}}", - "entity": [{"name": "account", "value": "<>"}] + "entity": [{"name": "account", "value": "<>"}] } }, diff --git a/bubble-server/src/test/resources/models/include/referral_signup.json b/bubble-server/src/test/resources/models/include/referral_signup.json index 9e12cdce..dd140992 100644 --- a/bubble-server/src/test/resources/models/include/referral_signup.json +++ b/bubble-server/src/test/resources/models/include/referral_signup.json @@ -19,10 +19,9 @@ "session": "new", "uri": "auth/register", "entity": { - "name": "<>", + "name": "<>@example.com", "password": "password1!", "agreeToTerms": true, - "contact": {"type": "email", "info": "<>@example.com"}, "promoCode": "<>" } }, diff --git a/bubble-server/src/test/resources/models/system/account_testDevice.json b/bubble-server/src/test/resources/models/system/account_testDevice.json index e2e40cae..65910cb8 100644 --- a/bubble-server/src/test/resources/models/system/account_testDevice.json +++ b/bubble-server/src/test/resources/models/system/account_testDevice.json @@ -1,6 +1,6 @@ [ { - "name": "root", + "email": "root", "children": { "Device": [ {"name": "test"} ] } diff --git a/bubble-server/src/test/resources/models/tests/account_deletion/block_delete_account.json b/bubble-server/src/test/resources/models/tests/account_deletion/block_delete_account.json index 3d6269ae..9e8fc127 100644 --- a/bubble-server/src/test/resources/models/tests/account_deletion/block_delete_account.json +++ b/bubble-server/src/test/resources/models/tests/account_deletion/block_delete_account.json @@ -3,8 +3,6 @@ "comment": "activate service, create account, login for block_delete", "include": "new_account", "params": { - "username": "user-to-partially-delete", - "password": "foobar1!", "email": "user-partially-account-deletion@example.com", "verifyEmail": "true" } diff --git a/bubble-server/src/test/resources/models/tests/account_deletion/block_delete_account_with_payments.json b/bubble-server/src/test/resources/models/tests/account_deletion/block_delete_account_with_payments.json index 8b18ea15..4f40360b 100644 --- a/bubble-server/src/test/resources/models/tests/account_deletion/block_delete_account_with_payments.json +++ b/bubble-server/src/test/resources/models/tests/account_deletion/block_delete_account_with_payments.json @@ -5,10 +5,9 @@ "uri": "users", "method": "put", "entity": { - "name": "user_with_payment_to_block_delete", + "name": "user_with_payment_to_block_delete@example.com", "password": "password1!", - "agreeToTerms": true, - "contact": { "type": "email", "info": "user_with_payment_to_block_delete@example.com" } + "agreeToTerms": true } }, "response": { "store": "testAccount" } diff --git a/bubble-server/src/test/resources/models/tests/account_deletion/full_delete_account.json b/bubble-server/src/test/resources/models/tests/account_deletion/full_delete_account.json index 9e0468c9..3d95fd50 100644 --- a/bubble-server/src/test/resources/models/tests/account_deletion/full_delete_account.json +++ b/bubble-server/src/test/resources/models/tests/account_deletion/full_delete_account.json @@ -3,8 +3,6 @@ "comment": "activate service, create account, login", "include": "new_account", "params": { - "username": "user-to-delete", - "password": "foobar1!", "email": "user-account-deletion@example.com", "verifyEmail": "true" } @@ -42,7 +40,7 @@ { "comment": "lookup user - just checking it is the one for deletion", "request": { "uri": "users/{{user.uuid}}" }, - "response": { "check": [{ "condition": "json.getName() == 'user-to-delete'" }] } + "response": { "check": [{ "condition": "json.getName() == 'user-account-deletion@example.com'" }] } }, { diff --git a/bubble-server/src/test/resources/models/tests/account_deletion/full_delete_account_with_payments.json b/bubble-server/src/test/resources/models/tests/account_deletion/full_delete_account_with_payments.json index 6f05d8c1..3591d01c 100644 --- a/bubble-server/src/test/resources/models/tests/account_deletion/full_delete_account_with_payments.json +++ b/bubble-server/src/test/resources/models/tests/account_deletion/full_delete_account_with_payments.json @@ -5,10 +5,9 @@ "uri": "users", "method": "put", "entity": { - "name": "user_with_payment_to_delete", + "name": "user_with_payment_to_delete@example.com", "password": "password1!", - "agreeToTerms": true, - "contact": { "type": "email", "info": "user_with_payment_to_delete@example.com" } + "agreeToTerms": true } }, "response": { "store": "testAccount" } diff --git a/bubble-server/src/test/resources/models/tests/auth/account_crud.json b/bubble-server/src/test/resources/models/tests/auth/account_crud.json index 6b2ce998..a1d19bdd 100644 --- a/bubble-server/src/test/resources/models/tests/auth/account_crud.json +++ b/bubble-server/src/test/resources/models/tests/auth/account_crud.json @@ -3,7 +3,6 @@ "comment": "activate service, create account, login", "include": "new_account", "params": { - "password": "foobar1!", "email": "user-account-crud@example.com", "verifyEmail": "true" } diff --git a/bubble-server/src/test/resources/models/tests/auth/account_registration.json b/bubble-server/src/test/resources/models/tests/auth/account_registration.json index d0d278c7..dec2c545 100644 --- a/bubble-server/src/test/resources/models/tests/auth/account_registration.json +++ b/bubble-server/src/test/resources/models/tests/auth/account_registration.json @@ -14,9 +14,8 @@ "session": "new", "uri": "auth/register", "entity": { - "name": "foobar1", - "password": "password1!", - "contact": { "type": "email", "info": "user-{{rand 5}}@example.com" } + "name": "account_reg_user@example.com", + "password": "password1!" } }, "response": { @@ -31,10 +30,9 @@ "session": "new", "uri": "auth/register", "entity": { - "name": "foobar1", + "name": "account_reg_user@example.com", "password": "password1!", - "agreeToTerms": true, - "contact": { "type": "email", "info": "user-{{rand 5}}@example.com" } + "agreeToTerms": true } }, "response": { @@ -47,7 +45,7 @@ "comment": "view current user", "request": { "uri": "me" }, "response": { - "check": [ {"condition": "json.getName() == 'foobar1'"} ] + "check": [ {"condition": "json.getName() == 'account_reg_user@example.com'"} ] } }, @@ -60,12 +58,12 @@ }, { - "comment": "login as foobar1, works", + "comment": "login as account_reg_user@example.com, works", "request": { "session": "new", "uri": "auth/login", "entity": { - "name": "foobar1", + "name": "account_reg_user@example.com", "password": "password1!" } }, @@ -80,7 +78,7 @@ "request": { "uri": "me" }, "response": { "store": "user1", - "check": [ {"condition": "json.getName() == 'foobar1'"} ] + "check": [ {"condition": "json.getName() == 'account_reg_user@example.com'"} ] } }, @@ -158,7 +156,7 @@ "session": "new", "uri": "auth/register", "entity": { - "name": "foobar1", + "name": "account_reg_user@example.com", "password": "password1!", "agreeToTerms": true } @@ -176,7 +174,7 @@ "session": "new", "uri": "auth/login", "entity": { - "name": "foobar1", + "name": "account_reg_user@example.com", "password": "password1!" } }, @@ -202,7 +200,7 @@ "session": "new", "uri": "auth/register", "entity": { - "name": "foobar1", + "name": "account_reg_user@example.com", "password": "password1!", "agreeToTerms": true } @@ -219,10 +217,9 @@ "session": "new", "uri": "auth/register", "entity": { - "name": "foobar2", + "name": "another_account_reg_user@example.com", "password": "password2!", - "agreeToTerms": true, - "contact": { "type": "email", "info": "user2-{{rand 5}}@example.com" } + "agreeToTerms": true } }, "response": { @@ -235,7 +232,7 @@ "comment": "view new user", "request": { "uri": "me" }, "response": { - "check": [ {"condition": "json.getName() == 'foobar2'"} ] + "check": [ {"condition": "json.getName() == 'another_account_reg_user@example.com'"} ] } } ] \ No newline at end of file diff --git a/bubble-server/src/test/resources/models/tests/auth/basic_auth.json b/bubble-server/src/test/resources/models/tests/auth/basic_auth.json index a61b7901..41a65a08 100644 --- a/bubble-server/src/test/resources/models/tests/auth/basic_auth.json +++ b/bubble-server/src/test/resources/models/tests/auth/basic_auth.json @@ -6,7 +6,7 @@ "method": "put", "uri": "auth/activate", "entity": { - "name": "root2", + "email": "root2", "password": "pass123!@x" } }, @@ -39,7 +39,7 @@ "comment": "read self", "request": { "uri": "me" }, "response": { - "check": [ {"condition": "json.getName() == 'root'"} ] + "check": [ {"condition": "json.getEmail() == 'root'"} ] } }, diff --git a/bubble-server/src/test/resources/models/tests/auth/change_admin_password.json b/bubble-server/src/test/resources/models/tests/auth/change_admin_password.json index e25171f8..7efa14f3 100644 --- a/bubble-server/src/test/resources/models/tests/auth/change_admin_password.json +++ b/bubble-server/src/test/resources/models/tests/auth/change_admin_password.json @@ -21,7 +21,6 @@ "comment": "as root user, create another admin user", "include": "new_account", "params": { - "username": "admin-change_password", "email": "admin-change_password@example.com", "password": "bazquux1!", "admin": true, @@ -35,7 +34,7 @@ "session": "new", "uri": "auth/login", "entity": { - "name": "admin-change_password", + "name": "admin-change_password@example.com", "password": "bazquux1!" } }, @@ -50,7 +49,7 @@ "request": { "uri": "me" }, "response": { "check": [ - {"condition": "json.getName() === 'admin-change_password'"}, + {"condition": "json.getName() === 'admin-change_password@example.com'"}, {"condition": "json.admin() === true"} ] } @@ -150,7 +149,7 @@ { "comment": "as root user, try to change admin user password without sending current password, succeeds because we are senior admin", "request": { - "uri": "users/admin-change_password/changePassword", + "uri": "users/admin-change_password@example.com/changePassword", "entity": { "newPassword": "newadminPASS1!" } @@ -163,7 +162,7 @@ "session": "new", "uri": "auth/login", "entity": { - "name": "admin-change_password", + "name": "admin-change_password@example.com", "password": "bazquux1!" } }, @@ -178,7 +177,7 @@ "session": "new", "uri": "auth/login", "entity": { - "name": "admin-change_password", + "name": "admin-change_password@example.com", "password": "newadminPASS1!" } }, @@ -192,7 +191,7 @@ "comment": "as second admin, read self, succeeds", "request": { "uri": "me" }, "response": { - "check": [ {"condition": "json.getName() === 'admin-change_password'"} ] + "check": [ {"condition": "json.getName() === 'admin-change_password@example.com'"} ] } } diff --git a/bubble-server/src/test/resources/models/tests/auth/change_password.json b/bubble-server/src/test/resources/models/tests/auth/change_password.json index 9960f55b..905ad693 100644 --- a/bubble-server/src/test/resources/models/tests/auth/change_password.json +++ b/bubble-server/src/test/resources/models/tests/auth/change_password.json @@ -140,9 +140,7 @@ "comment": "create a non-admin user", "include": "new_account", "params": { - "username": "user-change_password", "email": "user-change_password@example.com", - "password": "foobar1!", "verifyEmail": "true" } }, @@ -153,7 +151,7 @@ "session": "new", "uri": "auth/login", "entity": { - "name": "user-change_password", + "name": "user-change_password@example.com", "password": "foobar1!" } }, @@ -167,14 +165,14 @@ "comment": "as non-admin, read self-profile, succeeds", "request": { "uri": "me" }, "response": { - "check": [ {"condition": "json.getName() === 'user-change_password'"} ] + "check": [ {"condition": "json.getName() === 'user-change_password@example.com'"} ] } }, { "comment": "as root, change non-admin user password, do not need to send old password, invalidates all sessions", "request": { - "uri": "users/user-change_password/changePassword", + "uri": "users/user-change_password@example.com/changePassword", "session": "rootSession", "entity": { "newPassword": "newuserpass1!" @@ -199,7 +197,7 @@ "session": "new", "uri": "auth/login", "entity": { - "name": "user-change_password", + "name": "user-change_password@example.com", "password": "foobar1!" } }, @@ -214,7 +212,7 @@ "session": "new", "uri": "auth/login", "entity": { - "name": "user-change_password", + "name": "user-change_password@example.com", "password": "newuserpass1!" } }, @@ -226,7 +224,7 @@ { "comment": "as non-admin, read self policy, succeeds", - "request": { "uri": "users/user-change_password/policy" }, + "request": { "uri": "users/user-change_password@example.com/policy" }, "response": { "store": "userPolicy" } }, @@ -284,7 +282,7 @@ "uri": "auth/approve/{{emailInbox.[0].ctx.confirmationToken}}", "method": "post", "entity": [ - { "name": "account", "value": "user-change_password" }, + { "name": "account", "value": "user-change_password@example.com" }, { "name": "password", "value": "new_password3!" } ] } @@ -296,7 +294,7 @@ "session": "new", "uri": "auth/login", "entity": { - "name": "user-change_password", + "name": "user-change_password@example.com", "password": "new_password3!" } }, @@ -309,7 +307,7 @@ { "comment": "as non-admin user, update profile, set email as not required for account operations", "request": { - "uri": "users/user-change_password/policy/contacts", + "uri": "users/user-change_password@example.com/policy/contacts", "entity": { "type": "email", "info": "user-change_password@example.com", @@ -343,7 +341,7 @@ "session": "new", "uri": "auth/login", "entity": { - "name": "user-change_password", + "name": "user-change_password@example.com", "password": "new_password4!" } }, @@ -357,14 +355,14 @@ "comment": "update user policy, add authenticator", "include": "add_authenticator", "params": { - "userId": "user-change_password", + "userId": "user-change_password@example.com", "authenticatorVar": "userAuthenticator" } }, { "comment": "re-read user policy, verify authenticator is required for account operations", - "request": { "uri": "users/user-change_password/policy" }, + "request": { "uri": "users/user-change_password@example.com/policy" }, "response": { "store": "userPolicy", "check": [ diff --git a/bubble-server/src/test/resources/models/tests/auth/device_crud.json b/bubble-server/src/test/resources/models/tests/auth/device_crud.json index 3b4c6673..e020d518 100644 --- a/bubble-server/src/test/resources/models/tests/auth/device_crud.json +++ b/bubble-server/src/test/resources/models/tests/auth/device_crud.json @@ -1,11 +1,7 @@ [ { "comment": "create an account", - "include": "new_account", - "params": { - "name": "test-user-{{rand 5}}", - "password": "password1!" - } + "include": "new_account" }, { diff --git a/bubble-server/src/test/resources/models/tests/auth/download_account.json b/bubble-server/src/test/resources/models/tests/auth/download_account.json index 423429f9..4567585e 100644 --- a/bubble-server/src/test/resources/models/tests/auth/download_account.json +++ b/bubble-server/src/test/resources/models/tests/auth/download_account.json @@ -3,9 +3,7 @@ "comment": "activate service, create account, login", "include": "new_account", "params": { - "username": "testuser", - "password": "foobar1!", - "email": "testuser@example.com", + "email": "download_account_user@example.com", "verifyEmail": "true", "userSessionName": "userSession" } @@ -33,7 +31,7 @@ "comment": "as root, check email inbox for download request message", "request": { "session": "rootSession", - "uri": "debug/inbox/email/testuser@example.com?type=request&action=download" + "uri": "debug/inbox/email/download_account_user@example.com?type=request&action=download" }, "response": { "store": "emailInbox", @@ -59,7 +57,7 @@ "comment": "as root, check email inbox for download confirmation", "request": { "session": "rootSession", - "uri": "debug/inbox/email/testuser@example.com?type=confirmation&action=download" + "uri": "debug/inbox/email/download_account_user@example.com?type=confirmation&action=download" }, "response": { "store": "emailInbox", diff --git a/bubble-server/src/test/resources/models/tests/auth/forgot_password.json b/bubble-server/src/test/resources/models/tests/auth/forgot_password.json index 7ca1830f..69faa0ee 100644 --- a/bubble-server/src/test/resources/models/tests/auth/forgot_password.json +++ b/bubble-server/src/test/resources/models/tests/auth/forgot_password.json @@ -1,11 +1,9 @@ [ { - "comment": "activate service, create account, login", + "comment": "create account, login", "include": "new_account", "params": { - "username": "user-forgot_password", "email": "user-forgot_password@example.com", - "password": "foobar1!", "verifyEmail": "true" } }, @@ -62,7 +60,7 @@ "request": { "session": "userSession", "uri": "auth/approve/{{smsInbox.[0].ctx.confirmationToken}}", - "entity": [{"name": "account", "value": "user-forgot_password"}] + "entity": [{"name": "account", "value": "user-forgot_password@example.com"}] } }, @@ -120,7 +118,7 @@ "session": "new", "uri": "auth/approve/{{emailInbox.[0].ctx.confirmationToken}}", "entity": [ - { "name": "account", "value": "user-forgot_password" }, + { "name": "account", "value": "user-forgot_password@example.com" }, { "name": "password", "value": "new_password" } ] }, @@ -136,7 +134,7 @@ "session": "new", "uri": "auth/approve/{{emailInbox.[0].ctx.confirmationToken}}", "entity": [ - { "name": "account", "value": "user-forgot_password" }, + { "name": "account", "value": "user-forgot_password@example.com" }, { "name": "password", "value": "try_new_password1!" } ] }, @@ -155,7 +153,7 @@ "session": "new", "uri": "auth/approve/{{smsInbox.[0].ctx.confirmationToken}}", "entity": [ - { "name": "account", "value": "user-forgot_password" }, + { "name": "account", "value": "user-forgot_password@example.com" }, { "name": "password", "value": "new_password1!" } ] } @@ -167,7 +165,7 @@ "session": "new", "uri": "auth/approve/{{smsInbox.[0].ctx.confirmationToken}}", "entity": [ - { "name": "account", "value": "user-forgot_password" }, + { "name": "account", "value": "user-forgot_password@example.com" }, { "name": "password", "value": "new_password2!" } ] }, diff --git a/bubble-server/src/test/resources/models/tests/auth/multifactor_auth.json b/bubble-server/src/test/resources/models/tests/auth/multifactor_auth.json index f4348b6c..9b0e5201 100644 --- a/bubble-server/src/test/resources/models/tests/auth/multifactor_auth.json +++ b/bubble-server/src/test/resources/models/tests/auth/multifactor_auth.json @@ -3,9 +3,7 @@ "comment": "create new account and login", "include": "new_account", "params": { - "username": "user-multifactor_auth", - "email": "user-multifactor_auth@example.com", - "password": "foobar1!" + "email": "user-multifactor_auth@example.com" } }, @@ -74,7 +72,7 @@ "request": { "session": "userSession", "uri": "auth/approve/{{userInbox.[0].ctx.confirmationToken}}", - "entity": [{"name": "account", "value": "user-multifactor_auth"}] + "entity": [{"name": "account", "value": "user-multifactor_auth@example.com"}] } }, @@ -172,7 +170,7 @@ "request": { "session": "userSession", "uri": "auth/approve/{{userInbox.[0].ctx.confirmationToken}}", - "entity": [{"name": "account", "value": "user-multifactor_auth"}] + "entity": [{"name": "account", "value": "user-multifactor_auth@example.com"}] }, "response": { "sessionName": "userSession", @@ -250,7 +248,7 @@ "request": { "session": "userSession", "uri": "auth/approve/{{userInbox.[0].ctx.confirmationToken}}", - "entity": [{"name": "account", "value": "user-multifactor_auth"}] + "entity": [{"name": "account", "value": "user-multifactor_auth@example.com"}] }, "response": { "status": 422, @@ -264,7 +262,7 @@ "session": "userSession", "uri": "auth/approve/{{userInbox.[0].ctx.confirmationToken}}", "entity": [ - {"name": "account", "value": "user-multifactor_auth"}, + {"name": "account", "value": "user-multifactor_auth@example.com"}, {"name": "totpToken", "value": "{{authenticator_token authenticator.totpKey}}"} ] }, @@ -374,7 +372,7 @@ "request": { "session": "userSession", "uri": "auth/approve/{{smsInbox.[0].ctx.confirmationToken}}", - "entity": [{"name": "account", "value": "user-multifactor_auth"}] + "entity": [{"name": "account", "value": "user-multifactor_auth@example.com"}] } }, @@ -461,7 +459,7 @@ "request": { "session": "userSession", "uri": "auth/approve/{{smsInbox.[0].ctx.confirmationToken}}", - "entity": [{"name": "account", "value": "user-multifactor_auth"}] + "entity": [{"name": "account", "value": "user-multifactor_auth@example.com"}] }, "response": { "status": 422, @@ -475,7 +473,7 @@ "session": "userSession", "uri": "auth/approve/{{smsInbox.[0].ctx.confirmationToken}}", "entity": [ - {"name": "account", "value": "user-multifactor_auth"}, + {"name": "account", "value": "user-multifactor_auth@example.com"}, {"name": "totpToken", "value": "{{authenticator_token authenticator.totpKey}}"} ] }, @@ -552,7 +550,7 @@ "request": { "session": "userSession", "uri": "auth/approve/{{userInbox.[0].ctx.confirmationToken}}", - "entity": [{"name": "account", "value": "user-multifactor_auth"}] + "entity": [{"name": "account", "value": "user-multifactor_auth@example.com"}] } }, @@ -621,7 +619,7 @@ "request": { "session": "userSession", "uri": "auth/approve/{{userInbox.[0].ctx.confirmationToken}}", - "entity": [{"name": "account", "value": "user-multifactor_auth"}] + "entity": [{"name": "account", "value": "user-multifactor_auth@example.com"}] }, "response": { "sessionName": "userSession", diff --git a/bubble-server/src/test/resources/models/tests/live/backup_and_restore.json b/bubble-server/src/test/resources/models/tests/live/backup_and_restore.json index b3235094..33716f47 100644 --- a/bubble-server/src/test/resources/models/tests/live/backup_and_restore.json +++ b/bubble-server/src/test/resources/models/tests/live/backup_and_restore.json @@ -89,10 +89,9 @@ "uri": "users", "method": "put", "entity": { - "name": "user1", + "name": "backup_and_restore_user@example.com", "password": "password1!", - "agreeToTerms": true, - "contact": {"type": "email", "info": "user-{{rand 5}}@example.com"} + "agreeToTerms": true } } }, @@ -100,7 +99,7 @@ { "before": "sleep 24s", "comment": "lookup policy for new account", - "request": { "uri": "users/user1/policy" }, + "request": { "uri": "users/backup_and_restore_user/policy" }, "response": { "store": "user1policy" } }, @@ -212,7 +211,7 @@ { "comment": "verify account we added has been restored", "request": { - "uri": "users/user1/policy" + "uri": "users/backup_and_restore_user/policy" }, "response": { "check": [ {"condition": "json.getFirstEmail() == user1policy.getFirstEmail()"} ] diff --git a/bubble-server/src/test/resources/models/tests/network/network_regions.json b/bubble-server/src/test/resources/models/tests/network/network_regions.json index bf534501..774c34b6 100644 --- a/bubble-server/src/test/resources/models/tests/network/network_regions.json +++ b/bubble-server/src/test/resources/models/tests/network/network_regions.json @@ -14,11 +14,10 @@ "comment": "list all available compute regions", "request": { "uri": "me/regions" }, "response": { + "store": "regions", "check": [ - // ensure we have at least one region from each cloud - {"condition": "_find(json, function (r) { return r.getCloud() == computeClouds[0].getUuid(); }) != null"}, - {"condition": "_find(json, function (r) { return r.getCloud() == computeClouds[1].getUuid(); }) != null"}, - {"condition": "_find(json, function (r) { return r.getCloud() == computeClouds[2].getUuid(); }) != null"} + // ensure we have several regions + {"condition": "json.length > 5"} ] } }, @@ -28,10 +27,9 @@ "request": { "uri": "me/regions/closest" }, "response": { "check": [ - // ensure we have at least one region from each cloud - {"condition": "_find(json, function (r) { return r.getCloud() == computeClouds[0].getUuid(); }) != null"}, - {"condition": "_find(json, function (r) { return r.getCloud() == computeClouds[1].getUuid(); }) != null"}, - {"condition": "_find(json, function (r) { return r.getCloud() == computeClouds[2].getUuid(); }) != null"} + // ensure we have the same number of regions and that first is closer than last + {"condition": "json.length === regions.length"}, + {"condition": "json[0].getDistance() < json[json.length-1].getDistance()"} ] } } diff --git a/bubble-server/src/test/resources/models/tests/network/simple_network.json b/bubble-server/src/test/resources/models/tests/network/simple_network.json index df622f92..a9eea899 100644 --- a/bubble-server/src/test/resources/models/tests/network/simple_network.json +++ b/bubble-server/src/test/resources/models/tests/network/simple_network.json @@ -13,10 +13,9 @@ "uri": "users", "method": "put", "entity": { - "name": "test_user_simple_net", + "name": "test_user_simple_net@example.com", "password": "password1!", - "agreeToTerms": true, - "contact": {"type": "email", "info": "test-user@example.com"} + "agreeToTerms": true } }, "response": { @@ -341,10 +340,9 @@ "uri": "users", "method": "put", "entity": { - "name": "bubble_user", + "name": "bubble_user@example.com", "password": "password1!", - "agreeToTerms": true, - "contact": {"type": "email", "info": "bubble-user@example.com"} + "agreeToTerms": true } } }, diff --git a/bubble-server/src/test/resources/models/tests/payment/pay_code.json b/bubble-server/src/test/resources/models/tests/payment/pay_code.json index f4b6495b..d9567c9c 100644 --- a/bubble-server/src/test/resources/models/tests/payment/pay_code.json +++ b/bubble-server/src/test/resources/models/tests/payment/pay_code.json @@ -5,10 +5,9 @@ "uri": "users", "method": "put", "entity": { - "name": "test_user_code", + "name": "test_user_code@example.com", "password": "password1!", - "agreeToTerms": true, - "contact": {"type": "email", "info": "test-user@example.com"} + "agreeToTerms": true } } }, @@ -20,7 +19,7 @@ "session": "new", "uri": "auth/login", "entity": { - "name": "test_user_code", + "name": "test_user_code@example.com", "password": "password1!" } }, diff --git a/bubble-server/src/test/resources/models/tests/payment/pay_credit.json b/bubble-server/src/test/resources/models/tests/payment/pay_credit.json index a291a849..0108980b 100644 --- a/bubble-server/src/test/resources/models/tests/payment/pay_credit.json +++ b/bubble-server/src/test/resources/models/tests/payment/pay_credit.json @@ -5,10 +5,9 @@ "uri": "users", "method": "put", "entity": { - "name": "test_user_credit", + "name": "test_user_credit@example.com", "password": "password1!", - "agreeToTerms": true, - "contact": {"type": "email", "info": "test-user@example.com"} + "agreeToTerms": true } } }, @@ -20,7 +19,7 @@ "session": "new", "uri": "auth/login", "entity": { - "name": "test_user_credit", + "name": "test_user_credit@example.com", "password": "password1!" } }, @@ -106,7 +105,7 @@ "comment": "as root, check email inbox for verification message", "request": { "session": "rootSession", - "uri": "debug/inbox/email/test-user@example.com?type=request&action=verify&target=account" + "uri": "debug/inbox/email/test_user_credit@example.com?type=request&action=verify&target=account" }, "response": { "store": "emailInbox", diff --git a/bubble-server/src/test/resources/models/tests/payment/pay_credit_refund_and_restart.json b/bubble-server/src/test/resources/models/tests/payment/pay_credit_refund_and_restart.json index ae6ff4b1..535c0724 100644 --- a/bubble-server/src/test/resources/models/tests/payment/pay_credit_refund_and_restart.json +++ b/bubble-server/src/test/resources/models/tests/payment/pay_credit_refund_and_restart.json @@ -5,10 +5,9 @@ "uri": "users", "method": "put", "entity": { - "name": "test_user_credit_refund_restart", + "name": "test_user_credit_refund_restart@example.com", "password": "password1!", - "agreeToTerms": true, - "contact": {"type": "email", "info": "test-user@example.com"} + "agreeToTerms": true } } }, @@ -20,7 +19,7 @@ "session": "new", "uri": "auth/login", "entity": { - "name": "test_user_credit_refund_restart", + "name": "test_user_credit_refund_restart@example.com", "password": "password1!" } }, @@ -45,7 +44,7 @@ "comment": "as root, check email inbox for verification message", "request": { "session": "rootSession", - "uri": "debug/inbox/email/test-user@example.com?type=request&action=verify&target=account" + "uri": "debug/inbox/email/test_user_credit_refund_restart@example.com?type=request&action=verify&target=account" }, "response": { "store": "emailInbox", @@ -198,7 +197,7 @@ {"condition": "json[0].getAmount() < {{bills.[0].total}}"}, // not sure if current month has 28, 29, 30 or 31 days, so let's make some reasonable bounds // we should only have a refund for all but one day - {"condition": "json[0].getAmount() > Math.round(bills[0].getTotal() - (bills[0].getTotal() / (bills[0].daysInPeriod() - 1)))"}, + {"condition": "json[0].getAmount() >= Math.round((bills[0].daysInPeriod() - 1) * (bills[0].getTotal() / bills[0].daysInPeriod()))"}, {"condition": "json[0].getAmount() <= Math.round(bills[0].getTotal() - (bills[0].getTotal() / bills[0].daysInPeriod()))"} ] } diff --git a/bubble-server/src/test/resources/models/tests/payment/pay_free.json b/bubble-server/src/test/resources/models/tests/payment/pay_free.json index 72436093..350d6d1c 100644 --- a/bubble-server/src/test/resources/models/tests/payment/pay_free.json +++ b/bubble-server/src/test/resources/models/tests/payment/pay_free.json @@ -5,10 +5,9 @@ "uri": "users", "method": "put", "entity": { - "name": "test_user_free", + "name": "test_user_free@example.com", "password": "password1!", - "agreeToTerms": true, - "contact": {"type": "email", "info": "test-user@example.com"} + "agreeToTerms": true } } }, @@ -20,7 +19,7 @@ "session": "new", "uri": "auth/login", "entity": { - "name": "test_user_free", + "name": "test_user_free@example.com", "password": "password1!" } }, diff --git a/bubble-server/src/test/resources/models/tests/payment/plan_apps.json b/bubble-server/src/test/resources/models/tests/payment/plan_apps.json index 8f36ff5a..6c5644a9 100644 --- a/bubble-server/src/test/resources/models/tests/payment/plan_apps.json +++ b/bubble-server/src/test/resources/models/tests/payment/plan_apps.json @@ -5,10 +5,9 @@ "uri": "users", "method": "put", "entity": { - "name": "test_plan_apps_user", + "name": "test_plan_apps_user@example.com", "password": "password1!", - "agreeToTerms": true, - "contact": {"type": "email", "info": "test-user@example.com"} + "agreeToTerms": true } } }, @@ -20,7 +19,7 @@ "session": "new", "uri": "auth/login", "entity": { - "name": "test_plan_apps_user", + "name": "test_plan_apps_user@example.com", "password": "password1!" } }, diff --git a/bubble-server/src/test/resources/models/tests/payment/recurring_billing.json b/bubble-server/src/test/resources/models/tests/payment/recurring_billing.json index 7a2de13a..395f0429 100644 --- a/bubble-server/src/test/resources/models/tests/payment/recurring_billing.json +++ b/bubble-server/src/test/resources/models/tests/payment/recurring_billing.json @@ -5,10 +5,9 @@ "uri": "users", "method": "put", "entity": { - "name": "test_user_recurring", + "name": "test_user_recurring@example.com", "password": "password1!", - "agreeToTerms": true, - "contact": {"type": "email", "info": "test-user@example.com"} + "agreeToTerms": true } } }, @@ -20,7 +19,7 @@ "session": "new", "uri": "auth/login", "entity": { - "name": "test_user_recurring", + "name": "test_user_recurring@example.com", "password": "password1!" } }, @@ -45,7 +44,7 @@ "comment": "as root, check email inbox for verification message", "request": { "session": "rootSession", - "uri": "debug/inbox/email/test-user@example.com?type=request&action=verify&target=account" + "uri": "debug/inbox/email/test_user_recurring@example.com?type=request&action=verify&target=account" }, "response": { "store": "emailInbox", @@ -346,7 +345,7 @@ "comment": "as root, verify payment reminder message has been sent", "request": { "session": "rootSession", - "uri": "debug/inbox/email/test-user@example.com?type=request&action=payment&target=network" + "uri": "debug/inbox/email/test_user_recurring@example.com?type=request&action=payment&target=network" }, "response": { "store": "emailInbox", @@ -380,7 +379,7 @@ "comment": "as root, verify nonpayment message has been sent", "request": { "session": "rootSession", - "uri": "debug/inbox/email/test-user@example.com?type=notice&action=payment&target=network" + "uri": "debug/inbox/email/test_user_recurring@example.com?type=notice&action=payment&target=network" }, "response": { "store": "emailInbox", diff --git a/bubble-server/src/test/resources/models/tests/promo/account_credit.json b/bubble-server/src/test/resources/models/tests/promo/account_credit.json index 6d17d51f..d8f08147 100644 --- a/bubble-server/src/test/resources/models/tests/promo/account_credit.json +++ b/bubble-server/src/test/resources/models/tests/promo/account_credit.json @@ -5,10 +5,9 @@ "session": "new", "uri": "auth/register", "entity": { - "name": "test_user_small_promo", + "name": "account_credit_user@example.com", "password": "password1!", - "agreeToTerms": true, - "contact": {"type": "email", "info": "test_user_small_promo@example.com"} + "agreeToTerms": true } }, "response": { @@ -23,7 +22,7 @@ "comment": "root: check email inbox for verification message", "request": { "session": "rootSession", - "uri": "debug/inbox/email/test_user_small_promo@example.com?type=request&action=verify&target=account" + "uri": "debug/inbox/email/account_credit_user@example.com?type=request&action=verify&target=account" }, "response": { "store": "emailInbox", @@ -83,7 +82,7 @@ { "comment": "root: apply AccountCredit5 promotion to account", "request": { - "uri": "users/test_user_small_promo/promos", + "uri": "users/account_credit_user@example.com/promos", "method": "put", "entity": { "name": "AccountCredit5" @@ -93,7 +92,7 @@ { "comment": "root: list promos available, should be credit", - "request": {"uri": "users/test_user_small_promo/promos"}, + "request": {"uri": "users/account_credit_user@example.com/promos"}, "response": { "check": [ {"condition": "json.length === 1"}, @@ -119,7 +118,7 @@ { "comment": "user: try to add second AccountCredit5, admin only", "request": { - "uri": "users/test_user_small_promo/promos", + "uri": "users/account_credit_user@example.com/promos", "method": "put", "entity": { "name": "AccountCredit5" @@ -132,7 +131,7 @@ "comment": "root: add second AccountCredit5 promotion to account", "request": { "session": "rootSession", - "uri": "users/test_user_small_promo/promos", + "uri": "users/account_credit_user@example.com/promos", "method": "put", "entity": { "name": "AccountCredit5" @@ -143,7 +142,7 @@ { "comment": "root: add third AccountCredit5 promotion to account", "request": { - "uri": "users/test_user_small_promo/promos", + "uri": "users/account_credit_user@example.com/promos", "method": "put", "entity": { "name": "AccountCredit5" @@ -154,7 +153,7 @@ { "comment": "root: add fourth AccountCredit5 promotion to account", "request": { - "uri": "users/test_user_small_promo/promos", + "uri": "users/account_credit_user@example.com/promos", "method": "put", "entity": { "name": "AccountCredit5" @@ -167,7 +166,7 @@ { "comment": "root: list promos available, should be four credits", - "request": {"uri": "users/test_user_small_promo/promos"}, + "request": {"uri": "users/account_credit_user@example.com/promos"}, "response": { "check": [ {"condition": "json.length === 4"}, @@ -200,7 +199,7 @@ "comment": "root: remove last AccountCredit5 promotion", "request": { "session": "rootSession", - "uri": "users/test_user_small_promo/promos/{{promos.[0].uuid}}", + "uri": "users/account_credit_user@example.com/promos/{{promos.[0].uuid}}", "method": "delete" }, "response": { @@ -475,7 +474,7 @@ "comment": "root: apply another AccountCredit5 promotion to account", "request": { "session": "rootSession", - "uri": "users/test_user_small_promo/promos", + "uri": "users/account_credit_user@example.com/promos", "method": "put", "entity": { "name": "AccountCredit5" diff --git a/bubble-server/src/test/resources/models/tests/promo/first_month_free.json b/bubble-server/src/test/resources/models/tests/promo/first_month_free.json index 31d7dd71..2fab4834 100644 --- a/bubble-server/src/test/resources/models/tests/promo/first_month_free.json +++ b/bubble-server/src/test/resources/models/tests/promo/first_month_free.json @@ -5,10 +5,9 @@ "session": "new", "uri": "auth/register", "entity": { - "name": "test_user_1mo_free", + "name": "test_user_1mo_free@example.com", "password": "password1!", - "agreeToTerms": true, - "contact": {"type": "email", "info": "test_user_1mo_free@example.com"} + "agreeToTerms": true } }, "response": { diff --git a/bubble-server/src/test/resources/models/tests/promo/multi_promo.json b/bubble-server/src/test/resources/models/tests/promo/multi_promo.json index 86a2101a..787a8d5e 100644 --- a/bubble-server/src/test/resources/models/tests/promo/multi_promo.json +++ b/bubble-server/src/test/resources/models/tests/promo/multi_promo.json @@ -5,10 +5,9 @@ "session": "new", "uri": "auth/register", "entity": { - "name": "test_user_referring_multi", + "name": "test_user_referring_multi@example.com", "password": "password1!", - "agreeToTerms": true, - "contact": {"type": "email", "info": "test_user_referring_multi@example.com"} + "agreeToTerms": true } }, "response": { @@ -66,7 +65,7 @@ { "comment": "as root, grant some referral codes to the referring user", "request": { - "uri": "users/test_user_referring_multi/referralCodes", + "uri": "users/test_user_referring_multi@example.com/referralCodes", "method": "put", "entity": { "count": 3 } }, @@ -484,7 +483,7 @@ "comment": "root: apply another AccountCredit5 promotion to referring account", "request": { "session": "rootSession", - "uri": "users/test_user_referring_multi/promos", + "uri": "users/test_user_referring_multi@example.com/promos", "method": "put", "entity": { "name": "AccountCredit5" diff --git a/bubble-server/src/test/resources/models/tests/promo/referral_month_free.json b/bubble-server/src/test/resources/models/tests/promo/referral_month_free.json index ae1cca19..5deb1609 100644 --- a/bubble-server/src/test/resources/models/tests/promo/referral_month_free.json +++ b/bubble-server/src/test/resources/models/tests/promo/referral_month_free.json @@ -5,10 +5,9 @@ "uri": "users", "method": "put", "entity": { - "name": "test_user_referring_free", + "name": "test_user_referring_free@example.com", "password": "password1!", - "agreeToTerms": true, - "contact": {"type": "email", "info": "test_user_referring_free@example.com"} + "agreeToTerms": true } } }, @@ -20,7 +19,7 @@ "session": "new", "uri": "auth/login", "entity": { - "name": "test_user_referring_free", + "name": "test_user_referring_free@example.com", "password": "password1!" } }, @@ -61,7 +60,7 @@ { "comment": "as root, grant some referral codes to the referring user", "request": { - "uri": "users/test_user_referring_free/referralCodes", + "uri": "users/test_user_referring_free@example.com/referralCodes", "method": "put", "entity": { "count": 3 } }, @@ -102,10 +101,9 @@ "session": "new", "uri": "auth/register", "entity": { - "name": "test_user_referred_free", + "name": "test_user_referred_free@example.com", "password": "password1!", "agreeToTerms": true, - "contact": {"type": "email", "info": "test_user_referred_free@example.com"}, "promoCode": "{{referralCodes.[0].name}}" } }, diff --git a/bubble-web b/bubble-web index 4945a14a..e3a90cb7 160000 --- a/bubble-web +++ b/bubble-web @@ -1 +1 @@ -Subproject commit 4945a14a640935ebebc07811d7e7168b12a0b180 +Subproject commit e3a90cb72319d18639bdfda173bc8fde200609a7 diff --git a/utils/cobbzilla-wizard b/utils/cobbzilla-wizard index a07578cf..3b1649f0 160000 --- a/utils/cobbzilla-wizard +++ b/utils/cobbzilla-wizard @@ -1 +1 @@ -Subproject commit a07578cf1fde1cdaee0abc626fa4761fe8421446 +Subproject commit 3b1649f05991edaf82d117ecf3080e46a16b63b4