@@ -25,7 +25,7 @@ import bubble.resources.driver.DriversResource; | |||||
import bubble.resources.notify.ReceivedNotificationsResource; | import bubble.resources.notify.ReceivedNotificationsResource; | ||||
import bubble.resources.notify.SentNotificationsResource; | import bubble.resources.notify.SentNotificationsResource; | ||||
import bubble.server.BubbleConfiguration; | import bubble.server.BubbleConfiguration; | ||||
import bubble.service.account.AuthenticatorService; | |||||
import bubble.service.account.StandardAuthenticatorService; | |||||
import bubble.service.account.MitmControlService; | import bubble.service.account.MitmControlService; | ||||
import bubble.service.account.download.AccountDownloadService; | import bubble.service.account.download.AccountDownloadService; | ||||
import bubble.service.boot.SelfNodeService; | import bubble.service.boot.SelfNodeService; | ||||
@@ -66,7 +66,7 @@ public class AccountsResource { | |||||
@Autowired private AccountPolicyDAO policyDAO; | @Autowired private AccountPolicyDAO policyDAO; | ||||
@Autowired private AccountMessageDAO messageDAO; | @Autowired private AccountMessageDAO messageDAO; | ||||
@Autowired private AccountDownloadService downloadService; | @Autowired private AccountDownloadService downloadService; | ||||
@Autowired private AuthenticatorService authenticatorService; | |||||
@Autowired private StandardAuthenticatorService authenticatorService; | |||||
@Autowired private SelfNodeService selfNodeService; | @Autowired private SelfNodeService selfNodeService; | ||||
@Autowired private SessionDAO sessionDAO; | @Autowired private SessionDAO sessionDAO; | ||||
@@ -19,7 +19,7 @@ import bubble.model.cloud.BubbleNode; | |||||
import bubble.model.cloud.NetworkKeys; | import bubble.model.cloud.NetworkKeys; | ||||
import bubble.model.cloud.notify.NotificationReceipt; | import bubble.model.cloud.notify.NotificationReceipt; | ||||
import bubble.server.BubbleConfiguration; | import bubble.server.BubbleConfiguration; | ||||
import bubble.service.account.AuthenticatorService; | |||||
import bubble.service.account.StandardAuthenticatorService; | |||||
import bubble.service.account.StandardAccountMessageService; | import bubble.service.account.StandardAccountMessageService; | ||||
import bubble.service.backup.RestoreService; | import bubble.service.backup.RestoreService; | ||||
import bubble.service.bill.PromotionService; | import bubble.service.bill.PromotionService; | ||||
@@ -80,7 +80,7 @@ public class AuthResource { | |||||
@Autowired private BubblePlanDAO planDAO; | @Autowired private BubblePlanDAO planDAO; | ||||
@Autowired private BubbleNodeDAO nodeDAO; | @Autowired private BubbleNodeDAO nodeDAO; | ||||
@Autowired private BubbleConfiguration configuration; | @Autowired private BubbleConfiguration configuration; | ||||
@Autowired private AuthenticatorService authenticatorService; | |||||
@Autowired private StandardAuthenticatorService authenticatorService; | |||||
@Autowired private PromotionService promoService; | @Autowired private PromotionService promoService; | ||||
public Account updateLastLogin(Account account) { return accountDAO.update(account.setLastLogin()); } | public Account updateLastLogin(Account account) { return accountDAO.update(account.setLastLogin()); } | ||||
@@ -25,7 +25,7 @@ import bubble.resources.driver.DriversResource; | |||||
import bubble.resources.notify.ReceivedNotificationsResource; | import bubble.resources.notify.ReceivedNotificationsResource; | ||||
import bubble.resources.notify.SentNotificationsResource; | import bubble.resources.notify.SentNotificationsResource; | ||||
import bubble.server.BubbleConfiguration; | import bubble.server.BubbleConfiguration; | ||||
import bubble.service.account.AuthenticatorService; | |||||
import bubble.service.account.StandardAuthenticatorService; | |||||
import bubble.service.account.StandardAccountMessageService; | import bubble.service.account.StandardAccountMessageService; | ||||
import bubble.service.account.download.AccountDownloadService; | import bubble.service.account.download.AccountDownloadService; | ||||
import bubble.service.boot.BubbleModelSetupService; | import bubble.service.boot.BubbleModelSetupService; | ||||
@@ -80,7 +80,7 @@ public class MeResource { | |||||
@Autowired private SessionDAO sessionDAO; | @Autowired private SessionDAO sessionDAO; | ||||
@Autowired private AccountDownloadService downloadService; | @Autowired private AccountDownloadService downloadService; | ||||
@Autowired private BubbleConfiguration configuration; | @Autowired private BubbleConfiguration configuration; | ||||
@Autowired private AuthenticatorService authenticatorService; | |||||
@Autowired private StandardAuthenticatorService authenticatorService; | |||||
@Autowired private AccountMessageDAO messageDAO; | @Autowired private AccountMessageDAO messageDAO; | ||||
@GET | @GET | ||||
@@ -26,7 +26,7 @@ import bubble.model.cloud.BubbleNetwork; | |||||
import bubble.model.cloud.CloudService; | import bubble.model.cloud.CloudService; | ||||
import bubble.resources.account.AccountOwnedResource; | import bubble.resources.account.AccountOwnedResource; | ||||
import bubble.server.BubbleConfiguration; | import bubble.server.BubbleConfiguration; | ||||
import bubble.service.account.AuthenticatorService; | |||||
import bubble.service.account.StandardAuthenticatorService; | |||||
import bubble.service.cloud.GeoService; | import bubble.service.cloud.GeoService; | ||||
import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||
import org.cobbzilla.wizard.validation.ValidationResult; | import org.cobbzilla.wizard.validation.ValidationResult; | ||||
@@ -60,7 +60,7 @@ public class AccountPlansResource extends AccountOwnedResource<AccountPlan, Acco | |||||
@Autowired private CloudServiceDAO cloudDAO; | @Autowired private CloudServiceDAO cloudDAO; | ||||
@Autowired private AccountPaymentMethodDAO paymentMethodDAO; | @Autowired private AccountPaymentMethodDAO paymentMethodDAO; | ||||
@Autowired private BubbleConfiguration configuration; | @Autowired private BubbleConfiguration configuration; | ||||
@Autowired private AuthenticatorService authenticatorService; | |||||
@Autowired private StandardAuthenticatorService authenticatorService; | |||||
@Autowired private GeoService geoService; | @Autowired private GeoService geoService; | ||||
public AccountPlansResource(Account account) { super(account); } | public AccountPlansResource(Account account) { super(account); } | ||||
@@ -19,7 +19,7 @@ import bubble.model.account.message.ActionTarget; | |||||
import bubble.model.bill.AccountPlan; | import bubble.model.bill.AccountPlan; | ||||
import bubble.model.cloud.*; | import bubble.model.cloud.*; | ||||
import bubble.server.BubbleConfiguration; | import bubble.server.BubbleConfiguration; | ||||
import bubble.service.account.AuthenticatorService; | |||||
import bubble.service.account.StandardAuthenticatorService; | |||||
import bubble.service.backup.NetworkKeysService; | import bubble.service.backup.NetworkKeysService; | ||||
import bubble.service.cloud.NodeProgressMeterTick; | import bubble.service.cloud.NodeProgressMeterTick; | ||||
import bubble.service.cloud.StandardNetworkService; | import bubble.service.cloud.StandardNetworkService; | ||||
@@ -56,7 +56,7 @@ public class NetworkActionsResource { | |||||
@Autowired private BubbleNetworkDAO networkDAO; | @Autowired private BubbleNetworkDAO networkDAO; | ||||
@Autowired private AccountPlanDAO accountPlanDAO; | @Autowired private AccountPlanDAO accountPlanDAO; | ||||
@Autowired private BubbleConfiguration configuration; | @Autowired private BubbleConfiguration configuration; | ||||
@Autowired private AuthenticatorService authenticatorService; | |||||
@Autowired private StandardAuthenticatorService authenticatorService; | |||||
private Account account; | private Account account; | ||||
private BubbleNetwork network; | private BubbleNetwork network; | ||||
@@ -1,101 +1,9 @@ | |||||
/** | |||||
* Copyright (c) 2020 Bubble, Inc. All rights reserved. | |||||
* For personal (non-commercial) use, see license: https://bubblev.com/bubble-license/ | |||||
*/ | |||||
package bubble.service.account; | package bubble.service.account; | ||||
import bubble.dao.SessionDAO; | |||||
import bubble.dao.account.AccountPolicyDAO; | |||||
import bubble.model.account.Account; | import bubble.model.account.Account; | ||||
import bubble.model.account.AccountContact; | |||||
import bubble.model.account.AccountPolicy; | import bubble.model.account.AccountPolicy; | ||||
import bubble.model.account.AuthenticatorRequest; | import bubble.model.account.AuthenticatorRequest; | ||||
import bubble.model.account.message.ActionTarget; | |||||
import lombok.Getter; | |||||
import org.cobbzilla.wizard.cache.redis.RedisService; | |||||
import org.glassfish.jersey.server.ContainerRequest; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.stereotype.Service; | |||||
import static bubble.ApiConstants.G_AUTH; | |||||
import static org.cobbzilla.util.daemon.ZillaRuntime.now; | |||||
import static org.cobbzilla.wizard.cache.redis.RedisService.EX; | |||||
import static org.cobbzilla.wizard.resources.ResourceUtil.invalidEx; | |||||
import static org.cobbzilla.wizard.resources.ResourceUtil.userPrincipal; | |||||
@Service | |||||
public class AuthenticatorService { | |||||
@Autowired private SessionDAO sessionDAO; | |||||
@Autowired private AccountPolicyDAO policyDAO; | |||||
@Autowired private RedisService redis; | |||||
@Getter(lazy=true) private final RedisService authenticatorTimes = redis.prefixNamespace(getClass().getSimpleName()+"_authentications"); | |||||
public String authenticate (Account account, AccountPolicy policy, AuthenticatorRequest request) { | |||||
final AccountContact authenticator = policy.getAuthenticator(); | |||||
if (authenticator == null) throw invalidEx("err.authenticator.notConfigured"); | |||||
final Integer code = request.intToken(); | |||||
if (code == null) throw invalidEx("err.totpToken.invalid"); | |||||
final String secret = authenticator.totpInfo().getKey(); | |||||
if (G_AUTH.authorize(secret, code)) { | |||||
final String sessionToken = request.startSession() ? sessionDAO.create(account) : account.getToken(); | |||||
if (sessionToken == null && request.startSession()) throw invalidEx("err.totpToken.noSession"); | |||||
if (sessionToken != null) { | |||||
markAsAuthenticated(sessionToken, policy); | |||||
return sessionToken; | |||||
} | |||||
return null; | |||||
} else { | |||||
throw invalidEx("err.totpToken.invalid"); | |||||
} | |||||
} | |||||
public void markAsAuthenticated(String sessionToken, AccountPolicy policy) { | |||||
getAuthenticatorTimes().set(sessionToken, String.valueOf(now()), EX, policy.getAuthenticatorTimeout()/1000); | |||||
} | |||||
public boolean isAuthenticated (String sessionToken) { return getAuthenticatorTimes().get(sessionToken) != null; } | |||||
public void ensureAuthenticated(ContainerRequest ctx) { ensureAuthenticated(ctx, null); } | |||||
public void ensureAuthenticated(ContainerRequest ctx, ActionTarget target) { | |||||
final Account account = userPrincipal(ctx); | |||||
final AccountPolicy policy = policyDAO.findSingleByAccount(account.getUuid()); | |||||
ensureAuthenticated(account, policy, target); | |||||
} | |||||
public void ensureAuthenticated(ContainerRequest ctx, AccountPolicy policy, ActionTarget target) { | |||||
final Account account = userPrincipal(ctx); | |||||
ensureAuthenticated(account, policy, target); | |||||
} | |||||
public void ensureAuthenticated(Account account, AccountPolicy policy, ActionTarget target) { | |||||
if (policy == null || !policy.hasVerifiedAuthenticator()) return; | |||||
if (target != null) { | |||||
final AccountContact authenticator = policy.getAuthenticator(); | |||||
switch (target) { | |||||
case account: if (!authenticator.requiredForAccountOperations()) return; break; | |||||
case network: if (!authenticator.requiredForNetworkOperations()) return; break; | |||||
default: throw invalidEx("err.actionTarget.invalid"); | |||||
} | |||||
} | |||||
if (!isAuthenticated(account.getToken())) throw invalidEx("err.totpToken.invalid"); | |||||
} | |||||
public boolean flush(String sessionToken) { | |||||
final String exists = getAuthenticatorTimes().get(sessionToken); | |||||
getAuthenticatorTimes().del(sessionToken); | |||||
return exists != null; | |||||
} | |||||
public void updateExpiration(ContainerRequest ctx, AccountPolicy policy) { | |||||
final Account account = userPrincipal(ctx); | |||||
final String sessionToken = account.getToken(); | |||||
if (flush(sessionToken)) { | |||||
markAsAuthenticated(sessionToken, policy); | |||||
} | |||||
} | |||||
public interface AuthenticatorService { | |||||
String authenticate(Account account, AccountPolicy policy, AuthenticatorRequest setToken); | |||||
} | } |
@@ -0,0 +1,101 @@ | |||||
/** | |||||
* Copyright (c) 2020 Bubble, Inc. All rights reserved. | |||||
* For personal (non-commercial) use, see license: https://bubblev.com/bubble-license/ | |||||
*/ | |||||
package bubble.service.account; | |||||
import bubble.dao.SessionDAO; | |||||
import bubble.dao.account.AccountPolicyDAO; | |||||
import bubble.model.account.Account; | |||||
import bubble.model.account.AccountContact; | |||||
import bubble.model.account.AccountPolicy; | |||||
import bubble.model.account.AuthenticatorRequest; | |||||
import bubble.model.account.message.ActionTarget; | |||||
import lombok.Getter; | |||||
import org.cobbzilla.wizard.cache.redis.RedisService; | |||||
import org.glassfish.jersey.server.ContainerRequest; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.stereotype.Service; | |||||
import static bubble.ApiConstants.G_AUTH; | |||||
import static org.cobbzilla.util.daemon.ZillaRuntime.now; | |||||
import static org.cobbzilla.wizard.cache.redis.RedisService.EX; | |||||
import static org.cobbzilla.wizard.resources.ResourceUtil.invalidEx; | |||||
import static org.cobbzilla.wizard.resources.ResourceUtil.userPrincipal; | |||||
@Service | |||||
public class StandardAuthenticatorService implements AuthenticatorService { | |||||
@Autowired private SessionDAO sessionDAO; | |||||
@Autowired private AccountPolicyDAO policyDAO; | |||||
@Autowired private RedisService redis; | |||||
@Getter(lazy=true) private final RedisService authenticatorTimes = redis.prefixNamespace(getClass().getSimpleName()+"_authentications"); | |||||
public String authenticate (Account account, AccountPolicy policy, AuthenticatorRequest request) { | |||||
final AccountContact authenticator = policy.getAuthenticator(); | |||||
if (authenticator == null) throw invalidEx("err.authenticator.notConfigured"); | |||||
final Integer code = request.intToken(); | |||||
if (code == null) throw invalidEx("err.totpToken.invalid"); | |||||
final String secret = authenticator.totpInfo().getKey(); | |||||
if (G_AUTH.authorize(secret, code)) { | |||||
final String sessionToken = request.startSession() ? sessionDAO.create(account) : account.getToken(); | |||||
if (sessionToken == null && request.startSession()) throw invalidEx("err.totpToken.noSession"); | |||||
if (sessionToken != null) { | |||||
markAsAuthenticated(sessionToken, policy); | |||||
return sessionToken; | |||||
} | |||||
return null; | |||||
} else { | |||||
throw invalidEx("err.totpToken.invalid"); | |||||
} | |||||
} | |||||
public void markAsAuthenticated(String sessionToken, AccountPolicy policy) { | |||||
getAuthenticatorTimes().set(sessionToken, String.valueOf(now()), EX, policy.getAuthenticatorTimeout()/1000); | |||||
} | |||||
public boolean isAuthenticated (String sessionToken) { return getAuthenticatorTimes().get(sessionToken) != null; } | |||||
public void ensureAuthenticated(ContainerRequest ctx) { ensureAuthenticated(ctx, null); } | |||||
public void ensureAuthenticated(ContainerRequest ctx, ActionTarget target) { | |||||
final Account account = userPrincipal(ctx); | |||||
final AccountPolicy policy = policyDAO.findSingleByAccount(account.getUuid()); | |||||
ensureAuthenticated(account, policy, target); | |||||
} | |||||
public void ensureAuthenticated(ContainerRequest ctx, AccountPolicy policy, ActionTarget target) { | |||||
final Account account = userPrincipal(ctx); | |||||
ensureAuthenticated(account, policy, target); | |||||
} | |||||
public void ensureAuthenticated(Account account, AccountPolicy policy, ActionTarget target) { | |||||
if (policy == null || !policy.hasVerifiedAuthenticator()) return; | |||||
if (target != null) { | |||||
final AccountContact authenticator = policy.getAuthenticator(); | |||||
switch (target) { | |||||
case account: if (!authenticator.requiredForAccountOperations()) return; break; | |||||
case network: if (!authenticator.requiredForNetworkOperations()) return; break; | |||||
default: throw invalidEx("err.actionTarget.invalid"); | |||||
} | |||||
} | |||||
if (!isAuthenticated(account.getToken())) throw invalidEx("err.totpToken.invalid"); | |||||
} | |||||
public boolean flush(String sessionToken) { | |||||
final String exists = getAuthenticatorTimes().get(sessionToken); | |||||
getAuthenticatorTimes().del(sessionToken); | |||||
return exists != null; | |||||
} | |||||
public void updateExpiration(ContainerRequest ctx, AccountPolicy policy) { | |||||
final Account account = userPrincipal(ctx); | |||||
final String sessionToken = account.getToken(); | |||||
if (flush(sessionToken)) { | |||||
markAsAuthenticated(sessionToken, policy); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,18 @@ | |||||
package bubble.service_dbfilter; | |||||
import bubble.model.account.Account; | |||||
import bubble.model.account.AccountPolicy; | |||||
import bubble.model.account.AuthenticatorRequest; | |||||
import bubble.service.account.AuthenticatorService; | |||||
import org.springframework.stereotype.Service; | |||||
import static org.cobbzilla.util.daemon.ZillaRuntime.notSupported; | |||||
@Service | |||||
public class DbFilterAuthenticatorService implements AuthenticatorService { | |||||
@Override public String authenticate(Account account, AccountPolicy policy, AuthenticatorRequest setToken) { | |||||
return notSupported("authenticate"); | |||||
} | |||||
} |