Browse Source

defensive coding to avoid lockouts

tags/v0.5.2
Jonathan Cobb 5 years ago
parent
commit
f645975956
5 changed files with 42 additions and 19 deletions
  1. +17
    -7
      bubble-server/src/main/java/bubble/dao/account/AccountDAO.java
  2. +17
    -9
      bubble-server/src/main/java/bubble/resources/account/AuthResource.java
  3. +6
    -1
      bubble-server/src/main/java/bubble/server/listener/BubbleFirstTimeListener.java
  4. +1
    -1
      bubble-server/src/main/java/bubble/service/dbfilter/DatabaseFilterService.java
  5. +1
    -1
      bubble-web

+ 17
- 7
bubble-server/src/main/java/bubble/dao/account/AccountDAO.java View File

@@ -319,11 +319,13 @@ public class AccountDAO extends AbstractCRUDDAO<Account> implements SqlViewSearc
private static final AtomicBoolean unlocked = new AtomicBoolean(false);
@Transactional(Transactional.TxType.REQUIRES_NEW)
public boolean locked() {
if (unlocked.get()) return false;
// if any admins are unlocked, the bubble is unlocked
final boolean anyAdminUnlocked = !findByFields("admin", true, "locked", false).isEmpty();
if (anyAdminUnlocked) unlocked.set(true);
return !anyAdminUnlocked;
synchronized (unlocked) {
if (unlocked.get()) return false;
// if any admins are unlocked, the bubble is unlocked
final boolean anyAdminUnlocked = !findByFields("admin", true, "locked", false).isEmpty();
if (anyAdminUnlocked) unlocked.set(true);
return !anyAdminUnlocked;
}
}

// The admin with the lowest ctime is 'root'
@@ -342,9 +344,17 @@ public class AccountDAO extends AbstractCRUDDAO<Account> implements SqlViewSearc
return admins.get(0);
}

@Transactional
public void unlock() {
findAll().forEach(a -> update(a.setLocked(false)));
unlocked.set(true);
synchronized (unlocked) {
final List<Account> all = findAll();
for (Account account : all) {
update(account.setLocked(false));
}
log.info("unlock: " + all.size() + " accounts unlocked");
unlocked.set(true);
configuration.refreshPublicSystemConfigs();
}
}

}

+ 17
- 9
bubble-server/src/main/java/bubble/resources/account/AuthResource.java View File

@@ -219,15 +219,23 @@ public class AuthResource {

boolean isUnlock = false;
if (account.locked()) {
if (empty(unlockKey)) return invalid("err.account.locked");
if (!unlockKey.equals(configuration.getUnlockKey())) return invalid("err.unlockKey.invalid");
// unlock all accounts
isUnlock = true;
log.info("Unlock key was valid, unlocking accounts");
accountDAO.unlock();

// refresh system configs, we are now unlocked
configuration.refreshPublicSystemConfigs();
if (!accountDAO.locked()) {
log.info("login: account "+account.getName()+" was locked, but system is unlocked, unlocking again");
accountDAO.unlock();
final Account unlockedAccount = accountDAO.findByUuid(account.getUuid());
if (unlockedAccount.locked()) {
log.info("login: account "+account.getName()+" was still locked after unlocking system, cannot proceed");
return invalid("err.account.locked");
}

} else {
if (empty(unlockKey)) return invalid("err.account.locked");
if (!unlockKey.equals(configuration.getUnlockKey())) return invalid("err.unlockKey.invalid");
// unlock all accounts
isUnlock = true;
log.info("login: Unlock key was valid, unlocking accounts");
accountDAO.unlock();
}
}

if (!isUnlock) {


+ 6
- 1
bubble-server/src/main/java/bubble/server/listener/BubbleFirstTimeListener.java View File

@@ -44,10 +44,10 @@ public class BubbleFirstTimeListener extends RestServerLifecycleListenerBase<Bub
final BubbleConfiguration configuration = (BubbleConfiguration) server.getConfiguration();
redis.set(configuration.getBean(RedisService.class));

final AccountDAO accountDAO = configuration.getBean(AccountDAO.class);
if (FIRST_TIME_FILE.exists()) {
try {
// final FirstTimeType firstTimeType = FirstTimeType.fromString(FileUtil.toStringOrDie(FIRST_TIME_FILE));
final AccountDAO accountDAO = configuration.getBean(AccountDAO.class);
final Account adminAccount = accountDAO.getFirstAdmin();
if (adminAccount == null) {
log.error("onStart: no admin account found, cannot send first time install message, unlocking now");
@@ -79,6 +79,11 @@ public class BubbleFirstTimeListener extends RestServerLifecycleListenerBase<Bub
log.error("onStart: error deleting: "+abs(FIRST_TIME_FILE));
}
}
} else {
if (!accountDAO.locked()) {
log.info("onStart: system is not locked, ensuring all accounts are unlocked");
accountDAO.unlock();
}
}
}



+ 1
- 1
bubble-server/src/main/java/bubble/service/dbfilter/DatabaseFilterService.java View File

@@ -83,7 +83,7 @@ public class DatabaseFilterService {
if (!schemaResult.isZeroExitStatus()) return die("copyDatabase: error populating schema for "+dbName+": "+schemaResult);
}

log.info("copyDatabase: copying/filtering data into new database: "+dbName);
log.info("copyDatabase: copying/filtering data into new database: "+dbName+", "+planApps.size()+" apps enabled") ;
final int port = PortPicker.pickOrDie();

final Map<String, String> env = new HashMap<>(configuration.pgEnv());


+ 1
- 1
bubble-web

@@ -1 +1 @@
Subproject commit 239fdd0878a96e703e338377015a8a174afdfca1
Subproject commit 5c987a87cbb00e7b750300feeac19e293dbafa3c

Loading…
Cancel
Save