From b13673c26e2f02563f01928c28135bf2fa722092 Mon Sep 17 00:00:00 2001 From: Jonathan Cobb Date: Wed, 19 Feb 2020 16:39:04 -0500 Subject: [PATCH] add terms to account --- .../java/bubble/model/account/Account.java | 19 ++++++++----- .../model/account/AccountRegistration.java | 3 +++ .../resources/account/AccountsResource.java | 5 ++++ .../resources/account/AuthResource.java | 6 +++++ .../META-INF/bubble/bubble.properties | 2 +- .../pre_auth/ResourceMessages.properties | 2 ++ .../resources/models/include/new_account.json | 1 + .../resources/models/include/new_bubble.json | 1 + .../models/include/referral_signup.json | 1 + .../tests/auth/account_registration.json | 27 ++++++++++++++++--- .../models/tests/live/backup_and_restore.json | 1 + .../models/tests/network/simple_network.json | 2 ++ .../models/tests/payment/pay_code.json | 1 + .../models/tests/payment/pay_credit.json | 1 + .../pay_credit_refund_and_restart.json | 1 + .../models/tests/payment/pay_free.json | 1 + .../models/tests/payment/plan_apps.json | 1 + .../tests/payment/recurring_billing.json | 1 + .../models/tests/promo/account_credit.json | 1 + .../models/tests/promo/first_month_free.json | 1 + .../models/tests/promo/multi_promo.json | 1 + .../tests/promo/referral_month_free.json | 2 ++ bubble-web | 2 +- 23 files changed, 72 insertions(+), 11 deletions(-) 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 bcdf1124..e5cce58c 100644 --- a/bubble-server/src/main/java/bubble/model/account/Account.java +++ b/bubble-server/src/main/java/bubble/model/account/Account.java @@ -23,8 +23,6 @@ import org.cobbzilla.util.collection.ArrayUtil; import org.cobbzilla.wizard.filters.auth.TokenPrincipal; import org.cobbzilla.wizard.model.HashedPassword; import org.cobbzilla.wizard.model.Identifiable; -import org.cobbzilla.wizard.model.entityconfig.EntityFieldMode; -import org.cobbzilla.wizard.model.entityconfig.EntityFieldType; import org.cobbzilla.wizard.model.entityconfig.IdentifiableBaseParentEntity; import org.cobbzilla.wizard.model.entityconfig.annotations.*; import org.cobbzilla.wizard.model.search.SqlViewSearchResult; @@ -52,6 +50,8 @@ 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; import static org.cobbzilla.wizard.model.crypto.EncryptedTypes.ENC_PAD; +import static org.cobbzilla.wizard.model.entityconfig.EntityFieldMode.readOnly; +import static org.cobbzilla.wizard.model.entityconfig.EntityFieldType.epoch_time; import static org.cobbzilla.wizard.model.entityconfig.annotations.ECForeignKeySearchDepth.none; import static org.cobbzilla.wizard.resources.ResourceUtil.invalidEx; @@ -82,7 +82,7 @@ public class Account extends IdentifiableBaseParentEntity implements TokenPrinci public static final String[] UPDATE_FIELDS = {"url", "description", "autoUpdatePolicy"}; public static final String[] ADMIN_UPDATE_FIELDS = ArrayUtil.append(UPDATE_FIELDS, "suspended", "admin"); - public static final String[] CREATE_FIELDS = ArrayUtil.append(ADMIN_UPDATE_FIELDS, "name", "referralCode"); + public static final String[] CREATE_FIELDS = ArrayUtil.append(ADMIN_UPDATE_FIELDS, "name", "referralCode", "termsAgreed"); public static final String ROOT_USERNAME = "root"; public static final int NAME_MIN_LENGTH = 4; @@ -115,7 +115,7 @@ public class Account extends IdentifiableBaseParentEntity implements TokenPrinci // 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?) - @ECForeignKey(entity=Account.class) @ECField(index=20, mode=EntityFieldMode.readOnly) + @ECForeignKey(entity=Account.class) @ECField(index=20, mode=readOnly) @Column(length=UUID_MAXLEN, updatable=false) @Getter @Setter private String parent; public boolean hasParent () { return parent != null; } @@ -148,15 +148,20 @@ public class Account extends IdentifiableBaseParentEntity implements TokenPrinci @Getter @Setter private Boolean locked = false; public boolean locked () { return bool(locked); } - @ECIndex @ECSearchable @ECField(index=90, type=EntityFieldType.epoch_time, mode=EntityFieldMode.readOnly) + @ECIndex @ECSearchable @ECField(index=90, type=epoch_time, mode=readOnly) @Getter @Setter private Long deleted; public boolean deleted () { return deleted != null; } public Account setDeleted() { return setDeleted(now()); } - @ECIndex @ECSearchable @ECField(index=100, type=EntityFieldType.epoch_time, mode=EntityFieldMode.readOnly) + @ECIndex @ECSearchable @ECField(index=100, type=epoch_time, mode=readOnly) @Getter @Setter private Long lastLogin; public Account setLastLogin() { return setLastLogin(now()); } + @ECIndex @ECSearchable @ECField(index=110, type=epoch_time, mode=readOnly) + @Column(nullable=false) + @Getter @Setter private Long termsAgreed; + public Account setTermsAgreed() { return setTermsAgreed(now()); } + @JsonIgnore @Embedded @Getter @Setter private HashedPassword hashedPassword; public static final int MIN_PASSWORD_LENGTH = 8; @@ -221,11 +226,13 @@ public class Account extends IdentifiableBaseParentEntity implements TokenPrinci setAdmin(true); setDescription(request.hasDescription() ? request.getDescription() : "root user"); setLocale(getDEFAULT_LOCALE()); + setTermsAgreed(now()); } public Account(AccountRegistration request) { setName(request.getName()); setHashedPassword(new HashedPassword(request.getPassword())); + setTermsAgreed(request.getTermsAgreed()); } @Override public Identifiable update(Identifiable other) { 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 94ccc098..640807c1 100644 --- a/bubble-server/src/main/java/bubble/model/account/AccountRegistration.java +++ b/bubble-server/src/main/java/bubble/model/account/AccountRegistration.java @@ -15,4 +15,7 @@ public class AccountRegistration extends Account { @Getter @Setter private AccountContact contact; public boolean hasContact () { return contact != null; } + @Getter @Setter private Boolean agreeToTerms = null; + public boolean agreeToTerms () { return agreeToTerms != null && agreeToTerms; } + } 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 e12a3b4e..6fb3a3b2 100644 --- a/bubble-server/src/main/java/bubble/resources/account/AccountsResource.java +++ b/bubble-server/src/main/java/bubble/resources/account/AccountsResource.java @@ -100,6 +100,11 @@ public class AccountsResource { } 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 { + request.setTermsAgreed(); + } if (errors.isInvalid()) return invalid(errors); final String parentUuid; 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 74ab571f..ec109ef8 100644 --- a/bubble-server/src/main/java/bubble/resources/account/AuthResource.java +++ b/bubble-server/src/main/java/bubble/resources/account/AuthResource.java @@ -206,6 +206,12 @@ public class AuthResource { request.getContact().validate(errors); } + if (!request.agreeToTerms()) { + errors.addViolation("err.terms.required", "You must agree to the legal terms to use this service"); + } else { + request.setTermsAgreed(); + } + String currency = null; if (configuration.paymentsEnabled()) { currency = currencyForLocale(request.getLocale(), getDEFAULT_LOCALE()); diff --git a/bubble-server/src/main/resources/META-INF/bubble/bubble.properties b/bubble-server/src/main/resources/META-INF/bubble/bubble.properties index 939cdecd..e8423402 100644 --- a/bubble-server/src/main/resources/META-INF/bubble/bubble.properties +++ b/bubble-server/src/main/resources/META-INF/bubble/bubble.properties @@ -1 +1 @@ -bubble.version=0.7.2 +bubble.version=0.8.0 diff --git a/bubble-server/src/main/resources/message_templates/en_US/server/pre_auth/ResourceMessages.properties b/bubble-server/src/main/resources/message_templates/en_US/server/pre_auth/ResourceMessages.properties index a368f8a4..4a85034e 100644 --- a/bubble-server/src/main/resources/message_templates/en_US/server/pre_auth/ResourceMessages.properties +++ b/bubble-server/src/main/resources/message_templates/en_US/server/pre_auth/ResourceMessages.properties @@ -178,6 +178,7 @@ err.email.invalid=Email is invalid err.phone.required=SMS Phone is required err.phone.invalid=SMS Phone is invalid err.phone.length=SMS Phone is too long +err.terms.required=You must agree to the legal terms to use this service err.country.invalid=Country is invalid err.parent.notFound=Parent account does not exist err.accountInit.timeout=Timeout initializing new account @@ -241,6 +242,7 @@ field_label_contactType=Contact Type field_label_email=Email field_label_promoCode=Promo Code field_label_sms=SMS Phone +field_label_agreeToTerms=By checking this box, you agree to our Privacy Policy and Terms of Service. field_label_receiveInformationalMessages=Receive informational messages about your Bubble field_label_receivePromotionalMessages=Receive news about Bubble, including new releases and new features button_label_login=Login diff --git a/bubble-server/src/test/resources/models/include/new_account.json b/bubble-server/src/test/resources/models/include/new_account.json index 23ca7491..ba736863 100644 --- a/bubble-server/src/test/resources/models/include/new_account.json +++ b/bubble-server/src/test/resources/models/include/new_account.json @@ -24,6 +24,7 @@ "name": "<>", "password": "<>", "admin": "<>", + "agreeToTerms": true, "contact": {"type": "email", "info": "<>"} } }, diff --git a/bubble-server/src/test/resources/models/include/new_bubble.json b/bubble-server/src/test/resources/models/include/new_bubble.json index 96902406..2065b76a 100644 --- a/bubble-server/src/test/resources/models/include/new_bubble.json +++ b/bubble-server/src/test/resources/models/include/new_bubble.json @@ -67,6 +67,7 @@ "entity": { "name": "<>", "password": "<>", + "agreeToTerms": true, "contact": {"type": "email", "info": "<>"} } }, 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 9b758f0a..9e12cdce 100644 --- a/bubble-server/src/test/resources/models/include/referral_signup.json +++ b/bubble-server/src/test/resources/models/include/referral_signup.json @@ -21,6 +21,7 @@ "entity": { "name": "<>", "password": "password1!", + "agreeToTerms": true, "contact": {"type": "email", "info": "<>@example.com"}, "promoCode": "<>" } 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 01667d43..16752110 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 @@ -9,7 +9,7 @@ }, { - "comment": "new session, register a user account", + "comment": "new session, register a user account, do not agree to terms, fails", "request": { "session": "new", "uri": "auth/register", @@ -19,6 +19,24 @@ "contact": { "type": "email", "info": "user-{{rand 5}}@example.com" } } }, + "response": { + "status": 422, + "check": [ {"condition": "json.has('err.terms.required')"} ] + } + }, + + { + "comment": "new session, register a user account, agree to terms, succeeds", + "request": { + "session": "new", + "uri": "auth/register", + "entity": { + "name": "foobar1", + "password": "password1!", + "agreeToTerms": true, + "contact": { "type": "email", "info": "user-{{rand 5}}@example.com" } + } + }, "response": { "sessionName": "user1session", "session": "token" @@ -141,7 +159,8 @@ "uri": "auth/register", "entity": { "name": "foobar1", - "password": "password1!" + "password": "password1!", + "agreeToTerms": true } }, "response": { @@ -184,7 +203,8 @@ "uri": "auth/register", "entity": { "name": "foobar1", - "password": "password1!" + "password": "password1!", + "agreeToTerms": true } }, "response": { @@ -201,6 +221,7 @@ "entity": { "name": "foobar2", "password": "password2!", + "agreeToTerms": true, "contact": { "type": "email", "info": "user2-{{rand 5}}@example.com" } } }, 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 835b8d52..91e33d64 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 @@ -101,6 +101,7 @@ "entity": { "name": "user1", "password": "password1!", + "agreeToTerms": true, "contact": {"type": "email", "info": "user-{{rand 5}}@example.com"} } }, 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 de5c779d..df622f92 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 @@ -15,6 +15,7 @@ "entity": { "name": "test_user_simple_net", "password": "password1!", + "agreeToTerms": true, "contact": {"type": "email", "info": "test-user@example.com"} } }, @@ -342,6 +343,7 @@ "entity": { "name": "bubble_user", "password": "password1!", + "agreeToTerms": true, "contact": {"type": "email", "info": "bubble-user@example.com"} } } 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 b0959551..f4b6495b 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 @@ -7,6 +7,7 @@ "entity": { "name": "test_user_code", "password": "password1!", + "agreeToTerms": true, "contact": {"type": "email", "info": "test-user@example.com"} } } 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 6f345211..a291a849 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 @@ -7,6 +7,7 @@ "entity": { "name": "test_user_credit", "password": "password1!", + "agreeToTerms": true, "contact": {"type": "email", "info": "test-user@example.com"} } } 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 9bbf53eb..a0b3bfd9 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 @@ -7,6 +7,7 @@ "entity": { "name": "test_user_credit_refund_restart", "password": "password1!", + "agreeToTerms": true, "contact": {"type": "email", "info": "test-user@example.com"} } } 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 5ef241fa..9328bfb4 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 @@ -7,6 +7,7 @@ "entity": { "name": "test_user_free", "password": "password1!", + "agreeToTerms": true, "contact": {"type": "email", "info": "test-user@example.com"} } } 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 cf80c3eb..8f36ff5a 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 @@ -7,6 +7,7 @@ "entity": { "name": "test_plan_apps_user", "password": "password1!", + "agreeToTerms": true, "contact": {"type": "email", "info": "test-user@example.com"} } } 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 28113d7e..7a2de13a 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 @@ -7,6 +7,7 @@ "entity": { "name": "test_user_recurring", "password": "password1!", + "agreeToTerms": true, "contact": {"type": "email", "info": "test-user@example.com"} } } 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 255dba75..6d17d51f 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 @@ -7,6 +7,7 @@ "entity": { "name": "test_user_small_promo", "password": "password1!", + "agreeToTerms": true, "contact": {"type": "email", "info": "test_user_small_promo@example.com"} } }, 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 39e78edb..31d7dd71 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 @@ -7,6 +7,7 @@ "entity": { "name": "test_user_1mo_free", "password": "password1!", + "agreeToTerms": true, "contact": {"type": "email", "info": "test_user_1mo_free@example.com"} } }, 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 fef92cc2..86a2101a 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 @@ -7,6 +7,7 @@ "entity": { "name": "test_user_referring_multi", "password": "password1!", + "agreeToTerms": true, "contact": {"type": "email", "info": "test_user_referring_multi@example.com"} } }, 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 57d9b0cc..ae1cca19 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 @@ -7,6 +7,7 @@ "entity": { "name": "test_user_referring_free", "password": "password1!", + "agreeToTerms": true, "contact": {"type": "email", "info": "test_user_referring_free@example.com"} } } @@ -103,6 +104,7 @@ "entity": { "name": "test_user_referred_free", "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 3713e3ef..ace5fcc0 160000 --- a/bubble-web +++ b/bubble-web @@ -1 +1 @@ -Subproject commit 3713e3ef8cc63e1d311ec42ba84083afe4366cd8 +Subproject commit ace5fcc0889d4b89af2686c88f783050ff913aad