Quellcode durchsuchen

refactor FilterDataResource from FilterHttpResource, add FilterAssetsResource

tags/v0.7.1
Jonathan Cobb vor 4 Jahren
Ursprung
Commit
ad247a2458
5 geänderte Dateien mit 205 neuen und 95 gelöschten Zeilen
  1. +1
    -1
      bubble-server/src/main/java/bubble/ApiConstants.java
  2. +41
    -0
      bubble-server/src/main/java/bubble/resources/stream/FilterAssetsResource.java
  3. +122
    -0
      bubble-server/src/main/java/bubble/resources/stream/FilterDataResource.java
  4. +7
    -0
      bubble-server/src/main/java/bubble/resources/stream/FilterHttpRequest.java
  5. +34
    -94
      bubble-server/src/main/java/bubble/resources/stream/FilterHttpResource.java

+ 1
- 1
bubble-server/src/main/java/bubble/ApiConstants.java Datei anzeigen

@@ -181,10 +181,10 @@ public class ApiConstants {
public static final String SEARCH_ENDPOINT = "/search";
public static final String DEBUG_ENDPOINT = "/debug";
public static final String BUBBLE_MAGIC_ENDPOINT = "/.bubble";
public static final String EP_ASSETS = "/assets";

public static final String FILTER_HTTP_ENDPOINT = "/filter";
public static final String EP_APPLY = "/apply";
public static final String EP_ASSETS = "/assets";

// requests to a first-party host with this prefix will be forwarded to bubble
public static final String BUBBLE_FILTER_PASSTHRU = "/__bubble";


+ 41
- 0
bubble-server/src/main/java/bubble/resources/stream/FilterAssetsResource.java Datei anzeigen

@@ -0,0 +1,41 @@
package bubble.resources.stream;

import bubble.dao.app.AppMessageDAO;
import bubble.model.account.Account;
import bubble.model.app.BubbleApp;
import org.glassfish.grizzly.http.server.Request;
import org.glassfish.jersey.server.ContainerRequest;
import org.springframework.beans.factory.annotation.Autowired;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import static org.cobbzilla.wizard.resources.ResourceUtil.ok;

public class FilterAssetsResource {

private Account account;
private BubbleApp app;

public FilterAssetsResource (Account account, BubbleApp app) {
this.account = account;
this.app = app;
}

@Autowired private AppMessageDAO appMessageDAO;

@GET @Path("/{assetId}")
@Produces(MediaType.WILDCARD)
public Response filterHttp(@Context Request req,
@Context ContainerRequest request,
@PathParam("assetId") String assetId) {
// todo: locate and send asset
return ok();
}

}

+ 122
- 0
bubble-server/src/main/java/bubble/resources/stream/FilterDataResource.java Datei anzeigen

@@ -0,0 +1,122 @@
package bubble.resources.stream;

import bubble.dao.app.AppDataDAO;
import bubble.model.account.Account;
import bubble.model.app.AppData;
import bubble.model.app.AppDataFormat;
import bubble.model.app.AppMatcher;
import bubble.model.device.Device;
import lombok.extern.slf4j.Slf4j;
import org.glassfish.grizzly.http.server.Request;
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.List;
import java.util.stream.Collectors;

import static bubble.ApiConstants.*;
import static org.cobbzilla.util.daemon.ZillaRuntime.empty;
import static org.cobbzilla.util.daemon.ZillaRuntime.shortError;
import static org.cobbzilla.util.http.HttpContentTypes.APPLICATION_JSON;
import static org.cobbzilla.util.json.JsonUtil.COMPACT_MAPPER;
import static org.cobbzilla.util.json.JsonUtil.json;
import static org.cobbzilla.wizard.resources.ResourceUtil.*;

@Slf4j
public class FilterDataResource {

private Account account;
private Device device;
private AppMatcher matcher;

public FilterDataResource (Account account, Device device, AppMatcher matcher) {
this.account = account;
this.device = device;
this.matcher = matcher;
}

@Autowired private AppDataDAO dataDAO;

@GET @Path(EP_READ)
@Produces(APPLICATION_JSON)
public Response readData(@Context Request req,
@Context ContainerRequest ctx,
@QueryParam("format") AppDataFormat format) {

final List<AppData> data = dataDAO.findEnabledByAccountAndAppAndSite(account.getUuid(), matcher.getApp(), matcher.getSite());

if (log.isDebugEnabled()) log.debug("readData: found "+data.size()+" AppData records");

if (format == null) format = AppDataFormat.key;
switch (format) {
case key:
return ok(data.stream().map(AppData::getKey).collect(Collectors.toList()));
case value:
return ok(data.stream().map(AppData::getData).collect(Collectors.toList()));
case key_value:
return ok(data.stream().collect(Collectors.toMap(AppData::getKey, AppData::getData)));
case full:
return ok(data);
default:
throw notFoundEx(format.name());
}
}

@POST @Path(EP_WRITE)
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
public Response writeData(@Context Request req,
@Context ContainerRequest ctx,
AppData data) {
if (data == null || !data.hasKey()) throw invalidEx("err.key.required");
return ok(writeData(data));
}

@GET @Path(EP_WRITE)
@Produces(APPLICATION_JSON)
public Response writeData(@Context Request req,
@Context ContainerRequest ctx,
@QueryParam(Q_DATA) String dataJson,
@QueryParam(Q_REDIRECT) String redirectLocation) {
if (empty(dataJson)) throw invalidEx("err.data.required");
AppData data;
try {
data = json(dataJson, AppData.class);
} catch (Exception e) {
if (log.isDebugEnabled()) log.debug("writeData: invalid data="+dataJson+": "+shortError(e));
throw invalidEx("err.data.invalid");
}
if (!data.hasKey()) throw invalidEx("err.key.required");

data = writeData(data);

if (!empty(redirectLocation)) {
if (redirectLocation.trim().equalsIgnoreCase(Boolean.FALSE.toString())) {
return ok(data);
} else {
return redirect(redirectLocation);
}
} else {
final String referer = req.getHeader("Referer");
if (referer != null) return redirect(referer);
return redirect(".");
}
}

private AppData writeData(AppData data) {
if (log.isDebugEnabled()) log.debug("writeData: received data=" + json(data, COMPACT_MAPPER));

data.setAccount(account.getUuid());
data.setDevice(device.getUuid());
data.setApp(matcher.getApp());
data.setSite(matcher.getSite());
data.setMatcher(matcher.getUuid());

if (log.isDebugEnabled()) log.debug("writeData: recording data=" + json(data, COMPACT_MAPPER));
return dataDAO.set(data);
}

}

+ 7
- 0
bubble-server/src/main/java/bubble/resources/stream/FilterHttpRequest.java Datei anzeigen

@@ -39,4 +39,11 @@ public class FilterHttpRequest {
@JsonIgnore public String getUrl() {
return !hasMatchers() || !matchersResponse.hasRequest() ? null : matchersResponse.getRequest().getUrl();
}

public boolean hasApp(String appId) {
if (!hasMatchers()) return false;
for (AppMatcher m : getMatchers()) if (m.getApp().equals(appId)) return true;
return false;
}

}

+ 34
- 94
bubble-server/src/main/java/bubble/resources/stream/FilterHttpResource.java Datei anzeigen

@@ -1,12 +1,19 @@
package bubble.resources.stream;

import bubble.dao.account.AccountDAO;
import bubble.dao.app.*;
import bubble.dao.app.AppMatcherDAO;
import bubble.dao.app.AppRuleDAO;
import bubble.dao.app.AppSiteDAO;
import bubble.dao.app.BubbleAppDAO;
import bubble.dao.device.DeviceDAO;
import bubble.model.account.Account;
import bubble.model.app.*;
import bubble.model.app.AppMatcher;
import bubble.model.app.AppRule;
import bubble.model.app.AppSite;
import bubble.model.app.BubbleApp;
import bubble.model.device.Device;
import bubble.rule.FilterMatchDecision;
import bubble.server.BubbleConfiguration;
import bubble.service.cloud.DeviceIdService;
import bubble.service.stream.StandardRuleEngineService;
import lombok.Getter;
@@ -59,9 +66,8 @@ public class FilterHttpResource {
@Autowired private AppRuleDAO ruleDAO;
@Autowired private DeviceDAO deviceDAO;
@Autowired private DeviceIdService deviceIdService;
@Autowired private AppDataDAO dataDAO;

@Autowired private RedisService redis;
@Autowired private BubbleConfiguration configuration;

private static final long ACTIVE_REQUEST_TIMEOUT = HOURS.toSeconds(4);

@@ -402,113 +408,47 @@ public class FilterHttpResource {

public Response passthru(@Context ContainerRequest request) { return ruleEngine.passthru(request); }

@GET @Path(EP_DATA+"/{requestId}/{matcherId}"+EP_READ)
@Produces(APPLICATION_JSON)
public Response readData(@Context Request req,
@Context ContainerRequest ctx,
@PathParam("requestId") String requestId,
@PathParam("matcherId") String matcherId,
@QueryParam("format") AppDataFormat format) {

final FilterDataContext fdc = new FilterDataContext(req, requestId, matcherId);
final List<AppData> data = dataDAO.findEnabledByAccountAndAppAndSite
(fdc.request.getAccount().getUuid(), fdc.matcher.getApp(), fdc.matcher.getSite());

if (log.isDebugEnabled()) log.debug("readData: found "+data.size()+" AppData records");

if (format == null) format = AppDataFormat.key;
switch (format) {
case key:
return ok(data.stream().map(AppData::getKey).collect(Collectors.toList()));
case value:
return ok(data.stream().map(AppData::getData).collect(Collectors.toList()));
case key_value:
return ok(data.stream().collect(Collectors.toMap(AppData::getKey, AppData::getData)));
case full:
return ok(data);
default:
throw notFoundEx(format.name());
}
}
@Path(EP_DATA+"/{requestId}/{matcherId}")
public FilterDataResource getMatcherDataResource(@Context Request req,
@Context ContainerRequest ctx,
@PathParam("requestId") String requestId,
@PathParam("matcherId") String matcherId) {
final FilterSubContext filterCtx = new FilterSubContext(req, requestId);
if (!filterCtx.request.hasMatcher(matcherId)) throw notFoundEx(matcherId);

@POST @Path(EP_DATA+"/{requestId}/{matcherId}"+EP_WRITE)
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
public Response writeData(@Context Request req,
@Context ContainerRequest ctx,
@PathParam("requestId") String requestId,
@PathParam("matcherId") String matcherId,
AppData data) {
if (data == null || !data.hasKey()) throw invalidEx("err.key.required");
return ok(writeData(req, requestId, matcherId, data));
}
final AppMatcher matcher = matcherDAO.findByAccountAndId(filterCtx.request.getAccount().getUuid(), matcherId);
if (matcher == null) throw notFoundEx(matcherId);

@GET @Path(EP_DATA+"/{requestId}/{matcherId}"+EP_WRITE)
@Produces(APPLICATION_JSON)
public Response writeData(@Context Request req,
@Context ContainerRequest ctx,
@PathParam("requestId") String requestId,
@PathParam("matcherId") String matcherId,
@QueryParam(Q_DATA) String dataJson,
@QueryParam(Q_REDIRECT) String redirectLocation) {
if (empty(dataJson)) throw invalidEx("err.data.required");
final AppData data;
try {
data = json(dataJson, AppData.class);
} catch (Exception e) {
if (log.isDebugEnabled()) log.debug("writeData: invalid data="+dataJson+": "+shortError(e));
throw invalidEx("err.data.invalid");
}
if (!data.hasKey()) throw invalidEx("err.key.required");

final FilterDataContext fdc = writeData(req, requestId, matcherId, data);

if (!empty(redirectLocation)) {
if (redirectLocation.trim().equalsIgnoreCase(Boolean.FALSE.toString())) {
return ok(data);
} else {
return redirect(redirectLocation);
}
} else {
final String referer = req.getHeader("Referer");
if (referer != null) return redirect(referer);
return redirect(".");
}
return configuration.subResource(FilterDataResource.class, filterCtx.request.getAccount(), filterCtx.request.getDevice(), matcher);
}

private FilterDataContext writeData(Request req, String requestId, String matcherId, AppData data) {
if (log.isDebugEnabled()) log.debug("writeData: received data=" + json(data, COMPACT_MAPPER));
final FilterDataContext fdc = new FilterDataContext(req, requestId, matcherId);
@Path(EP_ASSETS+"/{requestId}/{appId}")
public FilterAssetsResource getAppAssetsResource(@Context Request req,
@Context ContainerRequest ctx,
@PathParam("requestId") String requestId,
@PathParam("appId") String appId) {
final FilterSubContext filterCtx = new FilterSubContext(req, requestId);
if (!filterCtx.request.hasApp(appId)) throw notFoundEx(appId);

data.setAccount(fdc.request.getAccount().getUuid());
data.setDevice(fdc.request.getDevice().getUuid());
data.setApp(fdc.matcher.getApp());
data.setSite(fdc.matcher.getSite());
data.setMatcher(fdc.matcher.getUuid());
final BubbleApp app = appDAO.findByAccountAndId(filterCtx.request.getAccount().getUuid(), appId);
if (app == null) throw notFoundEx(appId);

if (log.isDebugEnabled()) log.debug("writeData: recording data=" + json(data, COMPACT_MAPPER));
fdc.data = dataDAO.set(data);
return fdc;
return configuration.subResource(FilterAssetsResource.class, filterCtx.request.getAccount(), app);
}

private class FilterDataContext {
private class FilterSubContext {
public FilterHttpRequest request;
public AppMatcher matcher;
public AppData data;

public FilterDataContext(Request req, String requestId, String matcherId) {
public FilterSubContext(Request req, String requestId) {
// only mitmproxy is allowed to call us, and this should always be a local address
final String mitmAddr = req.getRemoteAddr();
if (!isLocalIpv4(mitmAddr)) throw forbiddenEx();

if (empty(requestId) || empty(matcherId)) throw notFoundEx();
if (empty(requestId)) throw notFoundEx();

request = getActiveRequest(requestId);
if (request == null) throw notFoundEx(requestId);
if (!request.hasMatcher(matcherId)) throw notFoundEx(matcherId);

matcher = matcherDAO.findByAccountAndId(request.getAccount().getUuid(), matcherId);
if (matcher == null) throw notFoundEx(matcherId);
}
}

}

Laden…
Abbrechen
Speichern