@@ -216,6 +216,7 @@ public class ApiConstants { | |||||
public static final String EP_NODES = "/nodes"; | public static final String EP_NODES = "/nodes"; | ||||
public static final String EP_DEVICES = "/devices"; | public static final String EP_DEVICES = "/devices"; | ||||
public static final String EP_DEVICE_TYPES = "/deviceTypes"; | public static final String EP_DEVICE_TYPES = "/deviceTypes"; | ||||
public static final String EP_DEFAULT_SECURITY_LEVEL = "/defaultSecurityLevel"; | |||||
public static final String EP_FLEX_ROUTERS = "/flexRouters"; | public static final String EP_FLEX_ROUTERS = "/flexRouters"; | ||||
public static final String EP_MODEL = "/model"; | public static final String EP_MODEL = "/model"; | ||||
public static final String EP_VPN = "/vpn"; | public static final String EP_VPN = "/vpn"; | ||||
@@ -4,6 +4,7 @@ | |||||
*/ | */ | ||||
package bubble.dao.account; | package bubble.dao.account; | ||||
import bubble.dao.device.HasDeviceDAO; | |||||
import bubble.model.account.TrustedClient; | import bubble.model.account.TrustedClient; | ||||
import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||
import org.cobbzilla.wizard.model.Identifiable; | import org.cobbzilla.wizard.model.Identifiable; | ||||
@@ -12,7 +13,7 @@ import org.springframework.stereotype.Repository; | |||||
import static java.util.UUID.randomUUID; | import static java.util.UUID.randomUUID; | ||||
@Repository @Slf4j | @Repository @Slf4j | ||||
public class TrustedClientDAO extends AccountOwnedEntityDAO<TrustedClient> { | |||||
public class TrustedClientDAO extends AccountOwnedEntityDAO<TrustedClient> implements HasDeviceDAO { | |||||
@Override protected String getNameField() { return Identifiable.UUID; } | @Override protected String getNameField() { return Identifiable.UUID; } | ||||
@@ -4,6 +4,7 @@ | |||||
*/ | */ | ||||
package bubble.dao.app; | package bubble.dao.app; | ||||
import bubble.dao.device.HasDeviceDAO; | |||||
import bubble.model.account.Account; | import bubble.model.account.Account; | ||||
import bubble.model.app.AppData; | import bubble.model.app.AppData; | ||||
import bubble.model.app.BubbleApp; | import bubble.model.app.BubbleApp; | ||||
@@ -26,7 +27,7 @@ import static org.hibernate.criterion.Restrictions.eq; | |||||
@SuppressWarnings("Duplicates") | @SuppressWarnings("Duplicates") | ||||
@Repository @Slf4j | @Repository @Slf4j | ||||
public class AppDataDAO extends AppTemplateEntityDAO<AppData> { | |||||
public class AppDataDAO extends AppTemplateEntityDAO<AppData> implements HasDeviceDAO { | |||||
public static final Order KEY_ASC = Order.asc("key"); | public static final Order KEY_ASC = Order.asc("key"); | ||||
@@ -46,6 +46,7 @@ public class DeviceDAO extends AccountOwnedEntityDAO<Device> { | |||||
@Autowired private BubbleConfiguration configuration; | @Autowired private BubbleConfiguration configuration; | ||||
@Autowired private AppDataDAO dataDAO; | @Autowired private AppDataDAO dataDAO; | ||||
@Autowired private TrustedClientDAO trustDAO; | @Autowired private TrustedClientDAO trustDAO; | ||||
@Autowired private FlexRouterDAO flexRouterDAO; | |||||
@Autowired private DeviceService deviceService; | @Autowired private DeviceService deviceService; | ||||
@Override public Order getDefaultSortOrder() { return ORDER_CTIME_ASC; } | @Override public Order getDefaultSortOrder() { return ORDER_CTIME_ASC; } | ||||
@@ -134,21 +135,24 @@ public class DeviceDAO extends AccountOwnedEntityDAO<Device> { | |||||
final Device device = findByUuid(uuid); | final Device device = findByUuid(uuid); | ||||
if (device != null) { | if (device != null) { | ||||
if (device.uninitialized()) die("Cannot delete special device: " + device.getName()); | if (device.uninitialized()) die("Cannot delete special device: " + device.getName()); | ||||
dataDAO.deleteDevice(uuid); | |||||
trustDAO.deleteDevice(uuid); | |||||
deleteDeviceDependencies(uuid); | |||||
super.delete(uuid); | super.delete(uuid); | ||||
refreshVpnUsers(); | refreshVpnUsers(); | ||||
} | } | ||||
} | } | ||||
@Override public void forceDelete(String uuid) { | @Override public void forceDelete(String uuid) { | ||||
dataDAO.deleteDevice(uuid); | |||||
trustDAO.deleteDevice(uuid); | |||||
deleteDeviceDependencies(uuid); | |||||
super.delete(uuid); | super.delete(uuid); | ||||
refreshVpnUsers(); | refreshVpnUsers(); | ||||
} | } | ||||
private void deleteDeviceDependencies(String uuid) { | |||||
dataDAO.deleteDevice(uuid); | |||||
trustDAO.deleteDevice(uuid); | |||||
flexRouterDAO.deleteDevice(uuid); | |||||
} | |||||
@Transactional | @Transactional | ||||
public synchronized boolean ensureAllSpareDevices(@NonNull final String account, @NonNull final String network) { | public synchronized boolean ensureAllSpareDevices(@NonNull final String account, @NonNull final String network) { | ||||
if (configuration.isSage()) return true; | if (configuration.isSage()) return true; | ||||
@@ -19,7 +19,7 @@ import static org.cobbzilla.wizard.resources.ResourceUtil.invalidEx; | |||||
import static org.hibernate.criterion.Restrictions.*; | import static org.hibernate.criterion.Restrictions.*; | ||||
@Repository @Slf4j | @Repository @Slf4j | ||||
public class FlexRouterDAO extends AccountOwnedEntityDAO<FlexRouter> { | |||||
public class FlexRouterDAO extends AccountOwnedEntityDAO<FlexRouter> implements HasDeviceDAO { | |||||
@Autowired private FlexRouterService flexRouterService; | @Autowired private FlexRouterService flexRouterService; | ||||
@@ -80,4 +80,13 @@ public class FlexRouterDAO extends AccountOwnedEntityDAO<FlexRouter> { | |||||
public FlexRouter findByKeyHash(String keyHash) { return findByUniqueField("keyHash", keyHash); } | public FlexRouter findByKeyHash(String keyHash) { return findByUniqueField("keyHash", keyHash); } | ||||
@Override public void deleteDevice(String uuid) { | |||||
final int count = bulkDelete("device", uuid); | |||||
if (count <= 1) { | |||||
log.info("deleteDevice: deleted "+count+" TrustedClient records for device "+uuid); | |||||
} else { | |||||
log.warn("deleteDevice: deleted "+count+" TrustedClient records (expected only 1) for device "+uuid); | |||||
} | |||||
} | |||||
} | } |
@@ -0,0 +1,7 @@ | |||||
package bubble.dao.device; | |||||
public interface HasDeviceDAO { | |||||
void deleteDevice(String uuid); | |||||
} |
@@ -15,10 +15,10 @@ import bubble.model.account.AuthenticatorRequest; | |||||
import bubble.model.account.message.AccountMessage; | import bubble.model.account.message.AccountMessage; | ||||
import bubble.model.account.message.AccountMessageType; | import bubble.model.account.message.AccountMessageType; | ||||
import bubble.model.account.message.ActionTarget; | import bubble.model.account.message.ActionTarget; | ||||
import bubble.model.device.BubbleDeviceType; | |||||
import bubble.resources.app.AppsResource; | import bubble.resources.app.AppsResource; | ||||
import bubble.resources.bill.*; | import bubble.resources.bill.*; | ||||
import bubble.resources.cloud.*; | import bubble.resources.cloud.*; | ||||
import bubble.resources.device.DeviceTypesResource; | |||||
import bubble.resources.device.DevicesResource; | import bubble.resources.device.DevicesResource; | ||||
import bubble.resources.device.FlexRoutersResource; | import bubble.resources.device.FlexRoutersResource; | ||||
import bubble.resources.driver.DriversResource; | import bubble.resources.driver.DriversResource; | ||||
@@ -28,10 +28,10 @@ import bubble.server.BubbleConfiguration; | |||||
import bubble.service.account.StandardAccountMessageService; | import bubble.service.account.StandardAccountMessageService; | ||||
import bubble.service.account.StandardAuthenticatorService; | import bubble.service.account.StandardAuthenticatorService; | ||||
import bubble.service.account.download.AccountDownloadService; | import bubble.service.account.download.AccountDownloadService; | ||||
import bubble.service.upgrade.BubbleJarUpgradeService; | |||||
import bubble.service.boot.BubbleModelSetupService; | import bubble.service.boot.BubbleModelSetupService; | ||||
import bubble.service.boot.SageHelloService; | import bubble.service.boot.SageHelloService; | ||||
import bubble.service.cloud.NodeLaunchMonitor; | import bubble.service.cloud.NodeLaunchMonitor; | ||||
import bubble.service.upgrade.BubbleJarUpgradeService; | |||||
import com.fasterxml.jackson.databind.JsonNode; | import com.fasterxml.jackson.databind.JsonNode; | ||||
import lombok.Cleanup; | import lombok.Cleanup; | ||||
import lombok.Getter; | import lombok.Getter; | ||||
@@ -368,9 +368,10 @@ public class MeResource { | |||||
return configuration.subResource(DevicesResource.class, caller); | return configuration.subResource(DevicesResource.class, caller); | ||||
} | } | ||||
@GET @Path(EP_DEVICE_TYPES) | |||||
public Response getDeviceTypes(@Context ContainerRequest ctx) { | |||||
return ok(BubbleDeviceType.getSelectableTypes()); | |||||
@Path(EP_DEVICE_TYPES) | |||||
public DeviceTypesResource getDeviceTypes(@Context ContainerRequest ctx) { | |||||
final Account caller = userPrincipal(ctx); | |||||
return configuration.subResource(DeviceTypesResource.class, caller); | |||||
} | } | ||||
@Path(EP_FLEX_ROUTERS) | @Path(EP_FLEX_ROUTERS) | ||||
@@ -0,0 +1,60 @@ | |||||
package bubble.resources.device; | |||||
import bubble.model.account.Account; | |||||
import bubble.model.device.BubbleDeviceType; | |||||
import bubble.model.device.DeviceSecurityLevel; | |||||
import bubble.service.device.DeviceService; | |||||
import lombok.extern.slf4j.Slf4j; | |||||
import org.glassfish.jersey.server.ContainerRequest; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import javax.ws.rs.*; | |||||
import javax.ws.rs.core.Context; | |||||
import javax.ws.rs.core.Response; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
import static bubble.ApiConstants.EP_DEFAULT_SECURITY_LEVEL; | |||||
import static org.cobbzilla.util.http.HttpContentTypes.APPLICATION_JSON; | |||||
import static org.cobbzilla.wizard.resources.ResourceUtil.*; | |||||
@Produces(APPLICATION_JSON) | |||||
@Consumes(APPLICATION_JSON) | |||||
@Slf4j | |||||
public class DeviceTypesResource { | |||||
@Autowired private DeviceService deviceService; | |||||
private Account account; | |||||
public DeviceTypesResource (Account account) { this.account = account; } | |||||
@GET | |||||
public Response getDeviceTypes (@Context ContainerRequest ctx) { | |||||
return ok(BubbleDeviceType.getSelectableTypes()); | |||||
} | |||||
@GET @Path(EP_DEFAULT_SECURITY_LEVEL) | |||||
public Response getDefaultSecurityLevels (@Context ContainerRequest ctx) { | |||||
return ok(getDefaultSecurityLevels()); | |||||
} | |||||
public Map<BubbleDeviceType, DeviceSecurityLevel> getDefaultSecurityLevels() { | |||||
final Map<BubbleDeviceType, DeviceSecurityLevel> levels = new HashMap<>(); | |||||
for (BubbleDeviceType type : BubbleDeviceType.getSelectableTypes()) { | |||||
levels.put(type, deviceService.getDefaultSecurityLevel(type)); | |||||
} | |||||
return levels; | |||||
} | |||||
@POST @Path(EP_DEFAULT_SECURITY_LEVEL+"/{deviceType}/{level}") | |||||
public Response setDefaultSecurityLevel (@Context ContainerRequest ctx, | |||||
@PathParam("deviceType") BubbleDeviceType type, | |||||
@PathParam("level") DeviceSecurityLevel level) { | |||||
final Account caller = userPrincipal(ctx); | |||||
if (!caller.admin()) return forbidden(); | |||||
deviceService.setDefaultSecurityLevel(type, level); | |||||
return ok(getDefaultSecurityLevels()); | |||||
} | |||||
} |
@@ -86,8 +86,9 @@ public class DevicesResource extends AccountOwnedResource<Device, DeviceDAO> { | |||||
device.initTotpKey(); | device.initTotpKey(); | ||||
} | } | ||||
log.info("setReferences: no securityLevel, setting to default for type "+device.getDeviceType()+": "+device.getDeviceType().getDefaultSecurityLevel()); | |||||
if (!device.hasSecurityLevel()) device.setSecurityLevel(device.getDeviceType().getDefaultSecurityLevel()); | |||||
final DeviceSecurityLevel defaultSecurityLevel = deviceService.getDefaultSecurityLevel(device.getDeviceType()); | |||||
log.info("setReferences: no securityLevel, setting to default for type "+device.getDeviceType()+": "+ defaultSecurityLevel); | |||||
if (!device.hasSecurityLevel()) device.setSecurityLevel(defaultSecurityLevel); | |||||
return super.setReferences(ctx, caller, device); | return super.setReferences(ctx, caller, device); | ||||
} | } | ||||
@@ -5,7 +5,9 @@ | |||||
package bubble.service.device; | package bubble.service.device; | ||||
import bubble.model.account.Account; | import bubble.model.account.Account; | ||||
import bubble.model.device.BubbleDeviceType; | |||||
import bubble.model.device.Device; | import bubble.model.device.Device; | ||||
import bubble.model.device.DeviceSecurityLevel; | |||||
import bubble.model.device.DeviceStatus; | import bubble.model.device.DeviceStatus; | ||||
import java.util.List; | import java.util.List; | ||||
@@ -28,4 +30,7 @@ public interface DeviceService { | |||||
DeviceStatus getDeviceStatus(String deviceUuid); | DeviceStatus getDeviceStatus(String deviceUuid); | ||||
DeviceStatus getLiveDeviceStatus(String deviceUuid); | DeviceStatus getLiveDeviceStatus(String deviceUuid); | ||||
default DeviceSecurityLevel getDefaultSecurityLevel(BubbleDeviceType type) { return type.getDefaultSecurityLevel(); } | |||||
default void setDefaultSecurityLevel(BubbleDeviceType type, DeviceSecurityLevel level) {} | |||||
} | } |
@@ -9,11 +9,14 @@ import bubble.dao.app.AppSiteDAO; | |||||
import bubble.dao.device.DeviceDAO; | import bubble.dao.device.DeviceDAO; | ||||
import bubble.model.account.Account; | import bubble.model.account.Account; | ||||
import bubble.model.app.AppSite; | import bubble.model.app.AppSite; | ||||
import bubble.model.device.BubbleDeviceType; | |||||
import bubble.model.device.Device; | import bubble.model.device.Device; | ||||
import bubble.model.device.DeviceSecurityLevel; | |||||
import bubble.model.device.DeviceStatus; | import bubble.model.device.DeviceStatus; | ||||
import bubble.server.BubbleConfiguration; | import bubble.server.BubbleConfiguration; | ||||
import bubble.service.cloud.GeoService; | import bubble.service.cloud.GeoService; | ||||
import bubble.service.stream.StandardRuleEngineService; | import bubble.service.stream.StandardRuleEngineService; | ||||
import lombok.Getter; | |||||
import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||
import org.cobbzilla.util.collection.ExpirationMap; | import org.cobbzilla.util.collection.ExpirationMap; | ||||
import org.cobbzilla.util.collection.SingletonList; | import org.cobbzilla.util.collection.SingletonList; | ||||
@@ -48,6 +51,8 @@ public class StandardDeviceService implements DeviceService { | |||||
public static final String DEVICE_FILE_PREFIX = "device_"; | public static final String DEVICE_FILE_PREFIX = "device_"; | ||||
public static final String REDIS_DEFAULT_SECURITY_LEVEL_PREFIX = "bubble_default_device_security_level"; | |||||
// used in dnscrypt-proxy and mitmproxy to check device security level | // used in dnscrypt-proxy and mitmproxy to check device security level | ||||
public static final String REDIS_KEY_DEVICE_SECURITY_LEVEL_PREFIX = "bubble_device_security_level_"; | public static final String REDIS_KEY_DEVICE_SECURITY_LEVEL_PREFIX = "bubble_device_security_level_"; | ||||
public static final String REDIS_KEY_DEVICE_SITE_MAX_SECURITY_LEVEL_PREFIX = "bubble_device_site_max_security_level_"; | public static final String REDIS_KEY_DEVICE_SITE_MAX_SECURITY_LEVEL_PREFIX = "bubble_device_site_max_security_level_"; | ||||
@@ -76,6 +81,25 @@ public class StandardDeviceService implements DeviceService { | |||||
private final Map<String, Device> deviceCache = new ExpirationMap<>(MINUTES.toMillis(10)); | private final Map<String, Device> deviceCache = new ExpirationMap<>(MINUTES.toMillis(10)); | ||||
@Getter(lazy=true) private final RedisService defaultSecurityLevelCache = redis.prefixNamespace(REDIS_DEFAULT_SECURITY_LEVEL_PREFIX); | |||||
@Override public DeviceSecurityLevel getDefaultSecurityLevel(BubbleDeviceType type) { | |||||
final String defaultLevel = getDefaultSecurityLevelCache().get(type.name()); | |||||
final DeviceSecurityLevel regularDefault = type.getDefaultSecurityLevel(); | |||||
if (defaultLevel != null) { | |||||
try { | |||||
return DeviceSecurityLevel.fromString(defaultLevel); | |||||
} catch (Exception e) { | |||||
log.error("getDefaultSecurityLevel("+type+") returned invalid value (using default="+regularDefault+"): "+defaultLevel); | |||||
} | |||||
} | |||||
return regularDefault; | |||||
} | |||||
@Override public void setDefaultSecurityLevel(BubbleDeviceType type, DeviceSecurityLevel level) { | |||||
getDefaultSecurityLevelCache().set(type.name(), level.name()); | |||||
} | |||||
@Override public Device findDeviceByIp (String ipAddr) { | @Override public Device findDeviceByIp (String ipAddr) { | ||||
if (!WG_DEVICES_DIR.exists()) { | if (!WG_DEVICES_DIR.exists()) { | ||||
@@ -1 +1 @@ | |||||
bubble.version=Adventure 1.2.5 | |||||
bubble.version=Adventure 1.2.6 |