Browse Source

Sync account plans if sync is set for account

pull/52/head
Kristijan Mitrovic 4 years ago
parent
commit
c22a049281
7 changed files with 152 additions and 44 deletions
  1. +23
    -1
      bubble-server/src/main/java/bubble/dao/bill/AccountPlanDAO.java
  2. +3
    -0
      bubble-server/src/main/java/bubble/model/bill/AccountPlan.java
  3. +68
    -26
      bubble-server/src/main/java/bubble/notify/NotificationHandler_sync_account.java
  4. +25
    -12
      bubble-server/src/main/java/bubble/service/account/StandardSyncAccountService.java
  5. +18
    -3
      bubble-server/src/main/java/bubble/service/account/SyncAccountNotification.java
  6. +5
    -1
      bubble-server/src/main/java/bubble/service/account/SyncAccountService.java
  7. +10
    -1
      bubble-server/src/main/java/bubble/service_dbfilter/DbFilterSyncAccountService.java

+ 23
- 1
bubble-server/src/main/java/bubble/dao/bill/AccountPlanDAO.java View File

@@ -9,6 +9,7 @@ import bubble.dao.account.AccountDAO;
import bubble.dao.account.AccountOwnedEntityDAO; import bubble.dao.account.AccountOwnedEntityDAO;
import bubble.dao.cloud.BubbleNetworkDAO; import bubble.dao.cloud.BubbleNetworkDAO;
import bubble.dao.cloud.CloudServiceDAO; import bubble.dao.cloud.CloudServiceDAO;
import bubble.model.account.Account;
import bubble.model.bill.AccountPlan; import bubble.model.bill.AccountPlan;
import bubble.model.bill.Bill; import bubble.model.bill.Bill;
import bubble.model.bill.BubblePlan; import bubble.model.bill.BubblePlan;
@@ -18,8 +19,10 @@ import bubble.model.cloud.CloudService;
import bubble.model.cloud.HostnameValidationResult; import bubble.model.cloud.HostnameValidationResult;
import bubble.notify.payment.PaymentValidationResult; import bubble.notify.payment.PaymentValidationResult;
import bubble.server.BubbleConfiguration; import bubble.server.BubbleConfiguration;
import bubble.service.account.SyncAccountService;
import bubble.service.bill.RefundService; import bubble.service.bill.RefundService;
import bubble.service.cloud.NetworkService; import bubble.service.cloud.NetworkService;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
@@ -166,9 +169,20 @@ public class AccountPlanDAO extends AccountOwnedEntityDAO<AccountPlan> {
paymentDriver.purchase(accountPlanUuid, paymentMethodUuid, billUuid); paymentDriver.purchase(accountPlanUuid, paymentMethodUuid, billUuid);
}); });
} }

final var account = accountDAO.findByUuid(accountPlan.getAccount());
configuration.getBean(SyncAccountService.class).syncPlan(account, accountPlan);

return super.postCreate(accountPlan, context); return super.postCreate(accountPlan, context);
} }


@Override public AccountPlan postUpdate(@NonNull final AccountPlan accountPlan, @NonNull final Object context) {
final var account = accountDAO.findByUuid(accountPlan.getAccount());
configuration.getBean(SyncAccountService.class).syncPlan(account, accountPlan);

return super.postUpdate(accountPlan, context);
}

@Override public void delete(String uuid) { @Override public void delete(String uuid) {
final AccountPlan accountPlan = findByUuid(uuid); final AccountPlan accountPlan = findByUuid(uuid);
if (accountPlan == null) return; if (accountPlan == null) return;
@@ -193,6 +207,14 @@ public class AccountPlanDAO extends AccountOwnedEntityDAO<AccountPlan> {
} }
} }


@Override public void forceDelete(String uuid) { super.delete(uuid); }
@Override public void forceDelete(String uuid) {
final AccountPlan accountPlan = findByUuid(uuid);
final Account account = accountDAO.findByUuid(accountPlan.getAccount());

super.delete(uuid);

configuration.getBean(SyncAccountService.class).syncForceDeletedPlan(account, accountPlan.getName());
}
public void forceDeleteWithoutSync(String uuid) { super.delete(uuid); }


} }

+ 3
- 0
bubble-server/src/main/java/bubble/model/bill/AccountPlan.java View File

@@ -174,6 +174,9 @@ public class AccountPlan extends IdentifiableBase implements HasNetwork {
@Transient @Getter @Setter private transient Boolean sendMetrics = null; @Transient @Getter @Setter private transient Boolean sendMetrics = null;
public boolean sendMetrics () { return sendMetrics == null || sendMetrics; } public boolean sendMetrics () { return sendMetrics == null || sendMetrics; }


@JsonIgnore @Transient @Getter @Setter private Boolean skipSync;
public boolean skipSync() { return bool(skipSync); }

public BubbleNetwork bubbleNetwork(Account account, public BubbleNetwork bubbleNetwork(Account account,
BubbleDomain domain, BubbleDomain domain,
BubblePlan plan, BubblePlan plan,


+ 68
- 26
bubble-server/src/main/java/bubble/notify/NotificationHandler_sync_account.java View File

@@ -5,12 +5,14 @@
package bubble.notify; package bubble.notify;


import bubble.dao.account.AccountDAO; import bubble.dao.account.AccountDAO;
import bubble.dao.bill.AccountPlanDAO;
import bubble.dao.cloud.BubbleNodeDAO; import bubble.dao.cloud.BubbleNodeDAO;
import bubble.model.account.Account; import bubble.model.account.Account;
import bubble.model.bill.AccountPlan;
import bubble.model.cloud.AnsibleInstallType; import bubble.model.cloud.AnsibleInstallType;
import bubble.model.cloud.BubbleNode;
import bubble.model.cloud.notify.ReceivedNotification; import bubble.model.cloud.notify.ReceivedNotification;
import bubble.service.account.SyncAccountNotification; import bubble.service.account.SyncAccountNotification;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;


@@ -22,34 +24,74 @@ public class NotificationHandler_sync_account extends ReceivedNotificationHandle


@Autowired private BubbleNodeDAO nodeDAO; @Autowired private BubbleNodeDAO nodeDAO;
@Autowired private AccountDAO accountDAO; @Autowired private AccountDAO accountDAO;
@Autowired private AccountPlanDAO accountPlanDAO;


@Override public void handleNotification(ReceivedNotification n) {
final BubbleNode node = nodeDAO.findByUuid(n.getFromNode());
@Override
public void handleNotification(ReceivedNotification n) {
final var node = nodeDAO.findByUuid(n.getFromNode());
if (node == null) { if (node == null) {
log.warn("sync_account: node not found: "+n.getFromNode());
log.warn("sync_account: node not found: " + n.getFromNode());
return;
}

final var notification = json(n.getPayloadJson(), SyncAccountNotification.class);

final var localAccount = accountDAO.findByUuid(notification.getAccountUuid());
if (localAccount == null) {
reportError("sync_account: localAccount not found: " + notification.getAccountUuid());
return;
}
if (!localAccount.sync()) {
log.info("sync_account: localAccount " + localAccount.getName() + " has sync disabled, no sync done");
return;
}

if (notification.getHashedPassword().isPresent()) {
syncAccountPassword(localAccount, notification.getHashedPassword().get());
}
if (notification.getAccountPlan().isPresent()) {
syncAccountPlan(localAccount.getUuid(), notification.getAccountPlan().get());
}
if (notification.getForceDeletedAccountPlanName().isPresent()) {
syncForceDelAccountPlan(localAccount.getUuid(), notification.getForceDeletedAccountPlanName().get());
}
}

private void syncAccountPassword(@NonNull final Account localAccount,
@NonNull final String incomingHashedPassword) {
localAccount.getHashedPassword().setHashedPassword(incomingHashedPassword);

// if we are a node, set skipSync so we don't get caught in an infinite loop
// (the node would notify the sage, which would notify the node, ad infinitum)
localAccount.setSkipSync(configuration.getThisNetwork().getInstallType() == AnsibleInstallType.node);

// update password, if we are a sage, this will notify all networks of password change
accountDAO.update(localAccount);
}

private void syncAccountPlan(@NonNull final String accountUuid, @NonNull final AccountPlan incomingPlan) {
final var localPlan = accountPlanDAO.findByAccountAndId(accountUuid, incomingPlan.getName());

if (localPlan == null) {
// create new localPlan
final var newPlan = new AccountPlan(incomingPlan);
newPlan.setSkipSync(configuration.getThisNetwork().getInstallType() == AnsibleInstallType.node);
accountPlanDAO.create(newPlan);
} else {
// update localPlan
localPlan.update(incomingPlan);
localPlan.setSkipSync(configuration.getThisNetwork().getInstallType() == AnsibleInstallType.node);
accountPlanDAO.update(localPlan);
}
}

private void syncForceDelAccountPlan(@NonNull final String accountUuid, @NonNull final String planName) {
final var localPlan = accountPlanDAO.findByAccountAndId(accountUuid, planName);

if (configuration.getThisNetwork().getInstallType() == AnsibleInstallType.node) {
accountPlanDAO.forceDeleteWithoutSync(localPlan.getUuid());
} else { } else {
final SyncAccountNotification notification = json(n.getPayloadJson(), SyncAccountNotification.class);

final Account account = accountDAO.findByUuid(notification.getAccountUuid());
if (account == null) {
reportError("sync_account: account not found: "+notification.getAccountUuid());
return;
}
if (!account.sync()) {
log.info("sync_account: account "+account.getName()+" has sync disabled, not synchronizing");
return;
}

account.getHashedPassword().setHashedPassword(notification.getHashedPassword());

// if we are a node, set skipSync so we don't get caught in an infinite loop
// (the node would notify the sage, which would notify the node, ad infinitum)
if (configuration.getThisNetwork().getInstallType() == AnsibleInstallType.node) {
account.setSkipSync(true);
}

// update password, if we are a sage, this will notify all networks of password change
accountDAO.update(account);
accountPlanDAO.forceDelete(localPlan.getUuid());
} }
} }




+ 25
- 12
bubble-server/src/main/java/bubble/service/account/StandardSyncAccountService.java View File

@@ -7,12 +7,14 @@ package bubble.service.account;
import bubble.dao.cloud.BubbleNetworkDAO; import bubble.dao.cloud.BubbleNetworkDAO;
import bubble.dao.cloud.BubbleNodeDAO; import bubble.dao.cloud.BubbleNodeDAO;
import bubble.model.account.Account; import bubble.model.account.Account;
import bubble.model.bill.AccountPlan;
import bubble.model.cloud.AnsibleInstallType; import bubble.model.cloud.AnsibleInstallType;
import bubble.model.cloud.BubbleNetwork; import bubble.model.cloud.BubbleNetwork;
import bubble.model.cloud.BubbleNetworkState; import bubble.model.cloud.BubbleNetworkState;
import bubble.model.cloud.BubbleNode; import bubble.model.cloud.BubbleNode;
import bubble.server.BubbleConfiguration; import bubble.server.BubbleConfiguration;
import bubble.service.notify.NotificationService; import bubble.service.notify.NotificationService;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -28,51 +30,62 @@ public class StandardSyncAccountService implements SyncAccountService {
@Autowired private NotificationService notificationService; @Autowired private NotificationService notificationService;
@Autowired private BubbleConfiguration configuration; @Autowired private BubbleConfiguration configuration;


public void syncAccount(Account account) {
public void syncAccount(@NonNull final Account account) {
sync(account, new SyncAccountNotification(account));
}

public void syncPlan(@NonNull final Account account, @NonNull final AccountPlan accountPlan) {
sync(account, new SyncAccountNotification(account.getUuid(), accountPlan));
}

public void syncForceDeletedPlan(@NonNull final Account account, @NonNull final String accountPlanName) {
sync(account, new SyncAccountNotification(account.getUuid(), accountPlanName));
}

private void sync(@NonNull final Account account, @NonNull final SyncAccountNotification notification) {
final BubbleNetwork thisNetwork = configuration.getThisNetwork(); final BubbleNetwork thisNetwork = configuration.getThisNetwork();
if (thisNetwork == null) { if (thisNetwork == null) {
// should never happen // should never happen
log.warn("syncAccount: thisNetwork was null, sync_account is impossible");
log.warn("sync: thisNetwork was null, sync_account is impossible");
return; return;
} }
if (!account.admin()) { if (!account.admin()) {
log.info("syncAccount: not syncing non-admin password");
log.info("sync: not syncing non-admin account");
return; return;
} }
if (!account.sync()) { if (!account.sync()) {
log.info("syncAccount: password sync disabled for account: "+account.getName());
log.info("sync: account sync disabled for account: "+account.getName());
return; return;
} }
final AnsibleInstallType installType = thisNetwork.getInstallType(); final AnsibleInstallType installType = thisNetwork.getInstallType();
final SyncAccountNotification notification = new SyncAccountNotification(account);
if (installType == AnsibleInstallType.sage) { if (installType == AnsibleInstallType.sage) {
// changing password on sage, notify all bubbles launched by user that have syncAccount == true
// changing account on sage, notify all bubbles launched by user that have syncAccount == true


for (BubbleNetwork network : networkDAO.findByAccount(account.getUuid())) { for (BubbleNetwork network : networkDAO.findByAccount(account.getUuid())) {
if (network.getState() != BubbleNetworkState.running) continue; if (network.getState() != BubbleNetworkState.running) continue;
if (!network.syncAccount()) continue; if (!network.syncAccount()) continue;
for (BubbleNode node : nodeDAO.findByNetwork(network.getUuid())) { for (BubbleNode node : nodeDAO.findByNetwork(network.getUuid())) {
if (node.getUuid().equals(configuration.getThisNode().getUuid())) { if (node.getUuid().equals(configuration.getThisNode().getUuid())) {
log.info("syncAccount: not notifying self");
log.info("sync: not notifying self");
continue; continue;
} }
log.info("syncAccount: sending sync_account notification from sage to node: "+node.id());
log.info("sync: sending sync_account notification from sage to node: "+node.id());
notificationService.notify(node, sync_account, notification); notificationService.notify(node, sync_account, notification);
} }
} }


} else if (installType == AnsibleInstallType.node) { } else if (installType == AnsibleInstallType.node) {
if (!thisNetwork.syncAccount()) { if (!thisNetwork.syncAccount()) {
log.info("syncAccount: disabled for node, not sending sync_account notification");
log.info("sync: disabled for node, not sending sync_account notification");
return; return;
} }
// changing password on node, notify sage, which will then notify all bubbles launched by user that have
// changing account on node, notify sage, which will then notify all bubbles launched by user that have
// syncAccount == true // syncAccount == true
log.info("syncAccount: sending sync_account notification from node to sage: "+configuration.getSageNode());
log.info("sync: sending sync_account notification from node to sage: "+configuration.getSageNode());
notificationService.notify(configuration.getSageNode(), sync_account, notification); notificationService.notify(configuration.getSageNode(), sync_account, notification);


} else { } else {
reportError("syncAccount("+account.getEmail()+"/"+account.getUuid()+"): invalid installType: "+installType);
reportError("sync("+account.getEmail()+"/"+account.getUuid()+"): invalid installType: "+installType);
} }
} }




+ 18
- 3
bubble-server/src/main/java/bubble/service/account/SyncAccountNotification.java View File

@@ -5,20 +5,35 @@
package bubble.service.account; package bubble.service.account;


import bubble.model.account.Account; import bubble.model.account.Account;
import bubble.model.bill.AccountPlan;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.Setter; import lombok.Setter;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;


import java.util.Optional;

@NoArgsConstructor @Accessors(chain=true) @NoArgsConstructor @Accessors(chain=true)
public class SyncAccountNotification { public class SyncAccountNotification {


@Getter @Setter private String accountUuid; @Getter @Setter private String accountUuid;
@Getter @Setter private String hashedPassword;
@Getter @Setter private Optional<String> hashedPassword = Optional.empty();
@Getter @Setter private Optional<AccountPlan> accountPlan = Optional.empty();
@Getter @Setter private Optional<String> forceDeletedAccountPlanName = Optional.empty();


public SyncAccountNotification(Account account) {
public SyncAccountNotification(@NonNull final Account account) {
this.accountUuid = account.getUuid(); this.accountUuid = account.getUuid();
this.hashedPassword = account.getHashedPassword().getHashedPassword();
this.hashedPassword = Optional.of(account.getHashedPassword().getHashedPassword());
}

public SyncAccountNotification(@NonNull final String accountUuid, @NonNull final AccountPlan accountPlan) {
this.accountUuid = accountUuid;
this.accountPlan = Optional.of(accountPlan);
} }


public SyncAccountNotification(@NonNull final String accountUuid, @NonNull final String forceDeletedPlanUuid) {
this.accountUuid = accountUuid;
this.forceDeletedAccountPlanName = Optional.of(forceDeletedPlanUuid);
}
} }

+ 5
- 1
bubble-server/src/main/java/bubble/service/account/SyncAccountService.java View File

@@ -5,9 +5,13 @@
package bubble.service.account; package bubble.service.account;


import bubble.model.account.Account; import bubble.model.account.Account;
import bubble.model.bill.AccountPlan;
import lombok.NonNull;


public interface SyncAccountService { public interface SyncAccountService {


void syncAccount(Account account);
void syncAccount(@NonNull final Account account);
void syncPlan(@NonNull final Account account, @NonNull final AccountPlan accountPlan);
void syncForceDeletedPlan(@NonNull final Account account, @NonNull final String accountPlanName);


} }

+ 10
- 1
bubble-server/src/main/java/bubble/service_dbfilter/DbFilterSyncAccountService.java View File

@@ -5,7 +5,9 @@
package bubble.service_dbfilter; package bubble.service_dbfilter;


import bubble.model.account.Account; import bubble.model.account.Account;
import bubble.model.bill.AccountPlan;
import bubble.service.account.SyncAccountService; import bubble.service.account.SyncAccountService;
import lombok.NonNull;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;


import static org.cobbzilla.util.daemon.ZillaRuntime.notSupported; import static org.cobbzilla.util.daemon.ZillaRuntime.notSupported;
@@ -13,6 +15,13 @@ import static org.cobbzilla.util.daemon.ZillaRuntime.notSupported;
@Service @Service
public class DbFilterSyncAccountService implements SyncAccountService { public class DbFilterSyncAccountService implements SyncAccountService {


@Override public void syncAccount(Account account) { notSupported("syncAccount"); }
@Override public void syncAccount(@NonNull final Account account) { notSupported("syncAccount"); }
@Override public void syncPlan(@NonNull final Account account, @NonNull final AccountPlan accountPlan) {
notSupported("syncPlan");
}

@Override public void syncForceDeletedPlan(@NonNull final Account account, @NonNull final String accountPlanName) {
notSupported("syncForceDeletedPlan");
}


} }

Loading…
Cancel
Save