@@ -216,6 +216,7 @@ public class ApiConstants { | |||
public static final String EP_NODES = "/nodes"; | |||
public static final String EP_DEVICES = "/devices"; | |||
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_MODEL = "/model"; | |||
public static final String EP_VPN = "/vpn"; | |||
@@ -4,6 +4,7 @@ | |||
*/ | |||
package bubble.dao.account; | |||
import bubble.dao.device.HasDeviceDAO; | |||
import bubble.model.account.TrustedClient; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.cobbzilla.wizard.model.Identifiable; | |||
@@ -12,7 +13,7 @@ import org.springframework.stereotype.Repository; | |||
import static java.util.UUID.randomUUID; | |||
@Repository @Slf4j | |||
public class TrustedClientDAO extends AccountOwnedEntityDAO<TrustedClient> { | |||
public class TrustedClientDAO extends AccountOwnedEntityDAO<TrustedClient> implements HasDeviceDAO { | |||
@Override protected String getNameField() { return Identifiable.UUID; } | |||
@@ -4,6 +4,7 @@ | |||
*/ | |||
package bubble.dao.app; | |||
import bubble.dao.device.HasDeviceDAO; | |||
import bubble.model.account.Account; | |||
import bubble.model.app.AppData; | |||
import bubble.model.app.BubbleApp; | |||
@@ -26,7 +27,7 @@ import static org.hibernate.criterion.Restrictions.eq; | |||
@SuppressWarnings("Duplicates") | |||
@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"); | |||
@@ -46,6 +46,7 @@ public class DeviceDAO extends AccountOwnedEntityDAO<Device> { | |||
@Autowired private BubbleConfiguration configuration; | |||
@Autowired private AppDataDAO dataDAO; | |||
@Autowired private TrustedClientDAO trustDAO; | |||
@Autowired private FlexRouterDAO flexRouterDAO; | |||
@Autowired private DeviceService deviceService; | |||
@Override public Order getDefaultSortOrder() { return ORDER_CTIME_ASC; } | |||
@@ -134,21 +135,24 @@ public class DeviceDAO extends AccountOwnedEntityDAO<Device> { | |||
final Device device = findByUuid(uuid); | |||
if (device != null) { | |||
if (device.uninitialized()) die("Cannot delete special device: " + device.getName()); | |||
dataDAO.deleteDevice(uuid); | |||
trustDAO.deleteDevice(uuid); | |||
deleteDeviceDependencies(uuid); | |||
super.delete(uuid); | |||
refreshVpnUsers(); | |||
} | |||
} | |||
@Override public void forceDelete(String uuid) { | |||
dataDAO.deleteDevice(uuid); | |||
trustDAO.deleteDevice(uuid); | |||
deleteDeviceDependencies(uuid); | |||
super.delete(uuid); | |||
refreshVpnUsers(); | |||
} | |||
private void deleteDeviceDependencies(String uuid) { | |||
dataDAO.deleteDevice(uuid); | |||
trustDAO.deleteDevice(uuid); | |||
flexRouterDAO.deleteDevice(uuid); | |||
} | |||
@Transactional | |||
public synchronized boolean ensureAllSpareDevices(@NonNull final String account, @NonNull final String network) { | |||
if (configuration.isSage()) return true; | |||
@@ -19,7 +19,7 @@ import static org.cobbzilla.wizard.resources.ResourceUtil.invalidEx; | |||
import static org.hibernate.criterion.Restrictions.*; | |||
@Repository @Slf4j | |||
public class FlexRouterDAO extends AccountOwnedEntityDAO<FlexRouter> { | |||
public class FlexRouterDAO extends AccountOwnedEntityDAO<FlexRouter> implements HasDeviceDAO { | |||
@Autowired private FlexRouterService flexRouterService; | |||
@@ -80,4 +80,13 @@ public class FlexRouterDAO extends AccountOwnedEntityDAO<FlexRouter> { | |||
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.AccountMessageType; | |||
import bubble.model.account.message.ActionTarget; | |||
import bubble.model.device.BubbleDeviceType; | |||
import bubble.resources.app.AppsResource; | |||
import bubble.resources.bill.*; | |||
import bubble.resources.cloud.*; | |||
import bubble.resources.device.DeviceTypesResource; | |||
import bubble.resources.device.DevicesResource; | |||
import bubble.resources.device.FlexRoutersResource; | |||
import bubble.resources.driver.DriversResource; | |||
@@ -28,10 +28,10 @@ import bubble.server.BubbleConfiguration; | |||
import bubble.service.account.StandardAccountMessageService; | |||
import bubble.service.account.StandardAuthenticatorService; | |||
import bubble.service.account.download.AccountDownloadService; | |||
import bubble.service.upgrade.BubbleJarUpgradeService; | |||
import bubble.service.boot.BubbleModelSetupService; | |||
import bubble.service.boot.SageHelloService; | |||
import bubble.service.cloud.NodeLaunchMonitor; | |||
import bubble.service.upgrade.BubbleJarUpgradeService; | |||
import com.fasterxml.jackson.databind.JsonNode; | |||
import lombok.Cleanup; | |||
import lombok.Getter; | |||
@@ -368,9 +368,10 @@ public class MeResource { | |||
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) | |||
@@ -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(); | |||
} | |||
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); | |||
} | |||
@@ -5,7 +5,9 @@ | |||
package bubble.service.device; | |||
import bubble.model.account.Account; | |||
import bubble.model.device.BubbleDeviceType; | |||
import bubble.model.device.Device; | |||
import bubble.model.device.DeviceSecurityLevel; | |||
import bubble.model.device.DeviceStatus; | |||
import java.util.List; | |||
@@ -28,4 +30,7 @@ public interface DeviceService { | |||
DeviceStatus getDeviceStatus(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.model.account.Account; | |||
import bubble.model.app.AppSite; | |||
import bubble.model.device.BubbleDeviceType; | |||
import bubble.model.device.Device; | |||
import bubble.model.device.DeviceSecurityLevel; | |||
import bubble.model.device.DeviceStatus; | |||
import bubble.server.BubbleConfiguration; | |||
import bubble.service.cloud.GeoService; | |||
import bubble.service.stream.StandardRuleEngineService; | |||
import lombok.Getter; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.cobbzilla.util.collection.ExpirationMap; | |||
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 REDIS_DEFAULT_SECURITY_LEVEL_PREFIX = "bubble_default_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_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)); | |||
@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) { | |||
if (!WG_DEVICES_DIR.exists()) { | |||
@@ -1 +1 @@ | |||
bubble.version=Adventure 1.2.5 | |||
bubble.version=Adventure 1.2.6 |