From 8975b83d7566a566a2545fe32f5b339a1c6c00ad Mon Sep 17 00:00:00 2001 From: Jonathan Cobb Date: Wed, 18 Dec 2019 16:56:14 -0500 Subject: [PATCH] add support for updating account policy --- .../bubble/dao/account/AccountPolicyDAO.java | 16 +++++++-- .../bubble/model/account/AccountPolicy.java | 35 +++++++++++++++++-- .../post_auth/ResourceMessages.properties | 25 +++++++++++-- bubble-web | 2 +- 4 files changed, 69 insertions(+), 9 deletions(-) diff --git a/bubble-server/src/main/java/bubble/dao/account/AccountPolicyDAO.java b/bubble-server/src/main/java/bubble/dao/account/AccountPolicyDAO.java index 8bb73a59..101d5b2a 100644 --- a/bubble-server/src/main/java/bubble/dao/account/AccountPolicyDAO.java +++ b/bubble-server/src/main/java/bubble/dao/account/AccountPolicyDAO.java @@ -1,18 +1,28 @@ package bubble.dao.account; import bubble.model.account.AccountPolicy; +import org.cobbzilla.wizard.validation.ValidationResult; import org.springframework.stereotype.Repository; import java.util.List; import static org.cobbzilla.util.daemon.ZillaRuntime.die; +import static org.cobbzilla.wizard.resources.ResourceUtil.invalidEx; @Repository public class AccountPolicyDAO extends AccountOwnedEntityDAO { - @Override public Object preCreate(AccountPolicy entity) { - entity.setUuid(entity.getAccount()); - return super.preCreate(entity); + @Override public Object preCreate(AccountPolicy policy) { + policy.setUuid(policy.getAccount()); + final ValidationResult result = policy.validate(); + if (result.isInvalid()) throw invalidEx(result); + return super.preCreate(policy); + } + + @Override public Object preUpdate(AccountPolicy policy) { + final ValidationResult result = policy.validate(); + if (result.isInvalid()) throw invalidEx(result); + return super.preUpdate(policy); } public AccountPolicy findSingleByAccount(String accountUuid) { 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 069248f3..c2717300 100644 --- a/bubble-server/src/main/java/bubble/model/account/AccountPolicy.java +++ b/bubble-server/src/main/java/bubble/model/account/AccountPolicy.java @@ -13,6 +13,7 @@ import org.cobbzilla.wizard.model.Identifiable; import org.cobbzilla.wizard.model.IdentifiableBase; import org.cobbzilla.wizard.model.entityconfig.annotations.ECForeignKey; import org.cobbzilla.wizard.model.entityconfig.annotations.ECType; +import org.cobbzilla.wizard.validation.ValidationResult; import org.hibernate.annotations.Type; import javax.persistence.*; @@ -20,6 +21,7 @@ import javax.validation.constraints.Size; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static bubble.model.account.AccountContact.contactMatch; @@ -35,9 +37,16 @@ import static org.cobbzilla.wizard.model.crypto.EncryptedTypes.ENC_PAD; @NoArgsConstructor @Accessors(chain=true) public class AccountPolicy extends IdentifiableBase implements HasAccount { + public static final Long MAX_ACCOUNT_OPERATION_TIMEOUT = TimeUnit.DAYS.toMillis(3); + public static final Long MAX_NODE_OPERATION_TIMEOUT = TimeUnit.DAYS.toMillis(3); + public static final Long MIN_ACCOUNT_OPERATION_TIMEOUT = MINUTES.toMillis(1); + public static final Long MIN_NODE_OPERATION_TIMEOUT = MINUTES.toMillis(1); + + public static final String[] UPDATE_FIELDS = {"deletionPolicy", "nodeOperationTimeout", "accountOperationTimeout"}; + public AccountPolicy(AccountPolicy policy) { copy(this, policy); } - @Override public Identifiable update(Identifiable thing) { copy(this, thing); return this; } + @Override public Identifiable update(Identifiable thing) { copy(this, thing, UPDATE_FIELDS); return this; } @ECForeignKey(entity=Account.class) @Column(length=UUID_MAXLEN, nullable=false, updatable=false) @@ -52,7 +61,7 @@ public class AccountPolicy extends IdentifiableBase implements HasAccount { @Getter @Setter private Long accountOperationTimeout = MINUTES.toMillis(10); @Enumerated(EnumType.STRING) @Column(length=20, nullable=false) - @Getter @Setter private AccountDeletionPolicy deletionPolicy = AccountDeletionPolicy.full_delete; + @Getter @Setter private AccountDeletionPolicy deletionPolicy = AccountDeletionPolicy.block_delete; @JsonIgnore @Transient public boolean isFullDelete () { return deletionPolicy == AccountDeletionPolicy.full_delete; } @JsonIgnore @Transient public boolean isBlockDelete () { return deletionPolicy == AccountDeletionPolicy.block_delete; } @@ -190,4 +199,26 @@ public class AccountPolicy extends IdentifiableBase implements HasAccount { } return this; } + + public ValidationResult validate() { + final ValidationResult result = new ValidationResult(); + if (deletionPolicy == null) { + result.addViolation("err.deletionPolicy.required"); + } + if (accountOperationTimeout == null) { + result.addViolation("err.accountOperationTimeout.required"); + } else if (accountOperationTimeout > MAX_ACCOUNT_OPERATION_TIMEOUT) { + result.addViolation("err.accountOperationTimeout.tooLong"); + } else if (accountOperationTimeout < MIN_ACCOUNT_OPERATION_TIMEOUT) { + result.addViolation("err.accountOperationTimeout.tooShort"); + } + if (nodeOperationTimeout == null) { + result.addViolation("err.nodeOperationTimeout.required"); + } else if (nodeOperationTimeout > MAX_NODE_OPERATION_TIMEOUT) { + result.addViolation("err.nodeOperationTimeout.tooLong"); + } else if (nodeOperationTimeout < MIN_NODE_OPERATION_TIMEOUT) { + result.addViolation("err.nodeOperationTimeout.tooShort"); + } + return result; + } } diff --git a/bubble-server/src/main/resources/message_templates/server/en_US/post_auth/ResourceMessages.properties b/bubble-server/src/main/resources/message_templates/server/en_US/post_auth/ResourceMessages.properties index 74bff078..839203cc 100644 --- a/bubble-server/src/main/resources/message_templates/server/en_US/post_auth/ResourceMessages.properties +++ b/bubble-server/src/main/resources/message_templates/server/en_US/post_auth/ResourceMessages.properties @@ -38,19 +38,31 @@ button_label_update_profile=Update link_label_change_password=Change Password link_label_account_policy=Account Policy and Contact Info +# Time duration fields +time_duration_options=minutes,hours,days +time_duration_minutes=minutes +time_duration_hours=hours +time_duration_days=days +time_duration_minutes_factor=60000 +time_duration_hours_factor=3600000 +time_duration_days_factor=86400000 + # Policy fields form_label_title_account_policy=Account Policy field_label_policy_account_deletion=Account Deletion Policy field_label_policy_account_deletion_options=full_delete,block_delete account_deletion_name_full_delete=Full delete -account_deletion_description_full_delete=Delete your account completely +account_deletion_description_full_delete=If you delete your account, it will be completely deleted. Another user could then register a new Account using your old username. account_deletion_name_block_delete=Delete all data, block future registrations -account_deletion_description_block_delete=Delete all your account data, but retain an empty stub Account so that no one can re-use your username +account_deletion_description_block_delete=If you delete your account, all your account data and information will be deleted, but an empty stub Account with your username will remain. This prevents anyone from reusing your username in the future. field_label_policy_account_operation_timeout=Account Operation Timeout field_label_policy_account_operation_timeout_description=To ensure your Account security, certain operations (like downloading your Account data) require your approval. If no response is received before this timeout, the operation will not be allowed to proceed. field_label_policy_node_operation_timeout=Bubble Operation Timeout field_label_policy_node_operation_timeout_description=To ensure your Bubble security, certain operations (like stopping your Bubble) require your approval. If no response is received before this timeout, the operation will not be allowed to proceed. -field_label_policy_contacts=Account Contact and Authorization Information +button_label_update_policy=Update + +# Policy Contact fields +form_label_title_account_contacts=Contacts and Authorization field_label_policy_contact_info=Contact Info field_label_policy_contact_nick=Nickname field_label_policy_contact_type=Type @@ -144,6 +156,9 @@ message_verified_card=Card Successfully Verified # Error messages from API server err.accountContactsJson.length=Account contacts length violation +err.accountOperationTimeout.required=Account operation timeout is required +err.accountOperationTimeout.tooLong=Account operation timeout cannot be longer than 3 days +err.accountOperationTimeout.tooShort=Account operation timeout cannot be shorter than 1 minute err.accountPlan.callerCountryDisallowed=Your country is not currently supported by our platform err.accountPlan.disabled=Account plan is not enabled err.accountPlan.notFound=Account plan not found @@ -178,6 +193,7 @@ err.delegatedPayment.notDelegated=Error locating payment service err.delegatedPayment.delegateNotFound=Error locating payment service err.delete.cannotDeleteSelf=You cannot delete yourself (you've still got a chance to win) err.delete.invalid=You cannot delete the account that owns the current network +err.deletionPolicy.required=Deletion policy is required err.description.length=Description is too long err.deviceName.notUnique=Device name is already in use err.disallowedCountriesJson.length=Disallowed countries list is too long @@ -216,6 +232,9 @@ err.locale.length=Locale is too long err.locale.required=Locale is required err.name.invalid=Name is invalid err.node.name.alreadyExists=A node already exists with the same FQDN +err.nodeOperationTimeout.required=Node operation timeout is required +err.nodeOperationTimeout.tooLong=Node operation timeout cannot be longer than 3 days +err.nodeOperationTimeout.tooShort=Node operation timeout cannot be shorter than 1 minute err.name.length=Name is too long err.name.mismatch=Name mismatch err.name.reserved=Name is reserved diff --git a/bubble-web b/bubble-web index 8761f67e..cd6467cb 160000 --- a/bubble-web +++ b/bubble-web @@ -1 +1 @@ -Subproject commit 8761f67e48f98bb51ead3a5153f09f5df39ced99 +Subproject commit cd6467cb5d641d9aee8c3f3e0f6672b39630ce2a