Browse Source

WIP. flex router registration

pull/51/head
Jonathan Cobb 4 years ago
parent
commit
ed7fb0fdc7
9 changed files with 69 additions and 60 deletions
  1. +4
    -0
      bubble-server/src/main/java/bubble/dao/device/FlexRouterDAO.java
  2. +6
    -4
      bubble-server/src/main/java/bubble/model/device/FlexRouter.java
  3. +24
    -11
      bubble-server/src/main/java/bubble/resources/account/AccountOwnedResource.java
  4. +3
    -2
      bubble-server/src/main/java/bubble/resources/app/AppsResourceBase.java
  5. +4
    -2
      bubble-server/src/main/java/bubble/resources/app/DataResourceBase.java
  6. +2
    -1
      bubble-server/src/main/java/bubble/resources/bill/AccountPlansResource.java
  7. +15
    -29
      bubble-server/src/main/java/bubble/resources/device/FlexRoutersResource.java
  8. +1
    -1
      bubble-server/src/main/resources/messages
  9. +10
    -10
      bubble-server/src/test/java/bubble/test/system/AuthTest.java

+ 4
- 0
bubble-server/src/main/java/bubble/dao/device/FlexRouterDAO.java View File

@@ -18,4 +18,8 @@ public class FlexRouterDAO extends AccountOwnedEntityDAO<FlexRouter> {
isNotNull("token")))); isNotNull("token"))));
} }


public FlexRouter findByAccountAndIp(String accountUuid, String ip) {
return findByUniqueFields("account", accountUuid, "ip", ip);
}

} }

+ 6
- 4
bubble-server/src/main/java/bubble/model/device/FlexRouter.java View File

@@ -10,6 +10,7 @@ import lombok.ToString;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.map.SingletonMap; import org.apache.commons.collections.map.SingletonMap;
import org.cobbzilla.util.collection.ArrayUtil;
import org.cobbzilla.wizard.model.Identifiable; import org.cobbzilla.wizard.model.Identifiable;
import org.cobbzilla.wizard.model.IdentifiableBase; import org.cobbzilla.wizard.model.IdentifiableBase;
import org.cobbzilla.wizard.model.entityconfig.EntityFieldMode; import org.cobbzilla.wizard.model.entityconfig.EntityFieldMode;
@@ -32,8 +33,8 @@ import static org.cobbzilla.util.reflect.ReflectionUtil.copy;
@ECIndexes({ @ECIndex(unique=true, of={"account", "ip"}) }) @ECIndexes({ @ECIndex(unique=true, of={"account", "ip"}) })
public class FlexRouter extends IdentifiableBase implements HasAccountNoName { public class FlexRouter extends IdentifiableBase implements HasAccountNoName {


public static final String[] CREATE_FIELDS = { "ip", "enabled" };
public static final String[] UPDATE_FIELDS = { "enabled" };
public static final String[] UPDATE_FIELDS = { "enabled", "active" };
public static final String[] CREATE_FIELDS = ArrayUtil.append(UPDATE_FIELDS, "ip");


public FlexRouter (FlexRouter other) { copy(this, other, CREATE_FIELDS); } public FlexRouter (FlexRouter other) { copy(this, other, CREATE_FIELDS); }


@@ -75,10 +76,11 @@ public class FlexRouter extends IdentifiableBase implements HasAccountNoName {
@JsonIgnore @Getter @Setter private String token; @JsonIgnore @Getter @Setter private String token;
public boolean hasToken () { return !empty(token); } public boolean hasToken () { return !empty(token); }


@Transient @Getter @Setter private String serverToken;
// used for sending the token, we never send it back
@Transient @Getter @Setter private String auth_token;
public boolean hasAuthToken () { return !empty(auth_token); }


public String pingUrl() { return "http://" + getIp() + ":" + getPort() + "/ping"; } public String pingUrl() { return "http://" + getIp() + ":" + getPort() + "/ping"; }

public String pingObject() { return json(new SingletonMap("token", getToken())); } public String pingObject() { return json(new SingletonMap("token", getToken())); }


} }

+ 24
- 11
bubble-server/src/main/java/bubble/resources/account/AccountOwnedResource.java View File

@@ -91,16 +91,26 @@ public class AccountOwnedResource<E extends HasAccount, DAO extends AccountOwned
return getDao().findByAccount(getAccountUuid(ctx)); return getDao().findByAccount(getAccountUuid(ctx));
} }


protected E find(Request req, ContainerRequest ctx, String id) { return find(ctx, id); }

protected E find(ContainerRequest ctx, String id) { protected E find(ContainerRequest ctx, String id) {
return getDao().findByAccountAndId(getAccountUuid(ctx), id); return getDao().findByAccountAndId(getAccountUuid(ctx), id);
} }


protected E findAlternate(ContainerRequest ctx, E request) { return null; } protected E findAlternate(ContainerRequest ctx, E request) { return null; }
protected E findAlternate(Request req, ContainerRequest ctx, E request) { return findAlternate(ctx, request); }

protected E findAlternate(ContainerRequest ctx, String id) { return null; } protected E findAlternate(ContainerRequest ctx, String id) { return null; }
protected E findAlternate(Request req, ContainerRequest ctx, String id) { return findAlternate(ctx, id); }


protected E findAlternateForCreate(ContainerRequest ctx, E request) { return findAlternate(ctx, request); } protected E findAlternateForCreate(ContainerRequest ctx, E request) { return findAlternate(ctx, request); }
protected E findAlternateForCreate(Request req, ContainerRequest ctx, E request) { return findAlternateForCreate(ctx, request); }

protected E findAlternateForUpdate(ContainerRequest ctx, String id) { return findAlternate(ctx, id); } protected E findAlternateForUpdate(ContainerRequest ctx, String id) { return findAlternate(ctx, id); }
protected E findAlternateForUpdate(Request req, ContainerRequest ctx, String id) { return findAlternateForUpdate(ctx, id); }

protected E findAlternateForDelete(ContainerRequest ctx, String id) { return findAlternate(ctx, id); } protected E findAlternateForDelete(ContainerRequest ctx, String id) { return findAlternate(ctx, id); }
protected E findAlternateForDelete(Request req, ContainerRequest ctx, String id) { return findAlternateForDelete(ctx, id); }


protected List<E> populate(ContainerRequest ctx, List<E> entities) { protected List<E> populate(ContainerRequest ctx, List<E> entities) {
for (E e : entities) populate(ctx, e); for (E e : entities) populate(ctx, e);
@@ -110,14 +120,15 @@ public class AccountOwnedResource<E extends HasAccount, DAO extends AccountOwned
protected E populate(ContainerRequest ctx, E entity) { return entity; } protected E populate(ContainerRequest ctx, E entity) { return entity; }


@GET @Path("/{id}") @GET @Path("/{id}")
public Response view(@Context ContainerRequest ctx,
public Response view(@Context Request req,
@Context ContainerRequest ctx,
@PathParam("id") String id) { @PathParam("id") String id) {


final Account caller = getAccountForViewById(ctx); final Account caller = getAccountForViewById(ctx);
E found = find(ctx, id);
E found = find(req, ctx, id);


if (found == null) { if (found == null) {
found = findAlternate(ctx, id);
found = findAlternate(req, ctx, id);
if (found == null) return notFound(id); if (found == null) return notFound(id);
} }
if (caller != null && !found.getAccount().equals(caller.getUuid()) && !caller.admin()) return notFound(id); if (caller != null && !found.getAccount().equals(caller.getUuid()) && !caller.admin()) return notFound(id);
@@ -138,9 +149,9 @@ public class AccountOwnedResource<E extends HasAccount, DAO extends AccountOwned
E request) { E request) {
if (request == null) return invalid("err.request.invalid"); if (request == null) return invalid("err.request.invalid");
final Account caller = checkEditable(ctx); final Account caller = checkEditable(ctx);
E found = find(ctx, request.getName());
E found = find(req, ctx, request.getName());
if (found == null) { if (found == null) {
found = findAlternateForCreate(ctx, request);
found = findAlternateForCreate(req, ctx, request);
} }
if (found != null) { if (found != null) {
if (!canUpdate(ctx, caller, found, request)) return ok(found); if (!canUpdate(ctx, caller, found, request)) return ok(found);
@@ -161,14 +172,15 @@ public class AccountOwnedResource<E extends HasAccount, DAO extends AccountOwned
protected E setReferences(ContainerRequest ctx, Request req, Account caller, E e) { return setReferences(ctx, caller, e); } protected E setReferences(ContainerRequest ctx, Request req, Account caller, E e) { return setReferences(ctx, caller, e); }


@POST @Path("/{id}") @POST @Path("/{id}")
public Response update(@Context ContainerRequest ctx,
public Response update(@Context Request req,
@Context ContainerRequest ctx,
@PathParam("id") String id, @PathParam("id") String id,
E request) { E request) {
if (request == null) return invalid("err.request.invalid"); if (request == null) return invalid("err.request.invalid");
final Account caller = checkEditable(ctx); final Account caller = checkEditable(ctx);
E found = find(ctx, id);
E found = find(req, ctx, id);
if (found == null) { if (found == null) {
found = findAlternateForUpdate(ctx, id);
found = findAlternateForUpdate(req, ctx, id);
if (found == null) return notFound(id); if (found == null) return notFound(id);
} }
if (!(found instanceof HasAccountNoName) && !canChangeName() && request.hasName() && !request.getName().equals(found.getName())) { if (!(found instanceof HasAccountNoName) && !canChangeName() && request.hasName() && !request.getName().equals(found.getName())) {
@@ -183,12 +195,13 @@ public class AccountOwnedResource<E extends HasAccount, DAO extends AccountOwned
protected boolean canChangeName() { return false; } protected boolean canChangeName() { return false; }


@DELETE @Path("/{id}") @DELETE @Path("/{id}")
public Response delete(@Context ContainerRequest ctx,
public Response delete(@Context Request req,
@Context ContainerRequest ctx,
@PathParam("id") String id) { @PathParam("id") String id) {
final Account caller = checkEditable(ctx); final Account caller = checkEditable(ctx);
E found = find(ctx, id);
E found = find(req, ctx, id);
if (found == null) { if (found == null) {
found = findAlternateForDelete(ctx, id);
found = findAlternateForDelete(req, ctx, id);
if (found == null) return notFound(id); if (found == null) return notFound(id);
} }




+ 3
- 2
bubble-server/src/main/java/bubble/resources/app/AppsResourceBase.java View File

@@ -63,8 +63,9 @@ public abstract class AppsResourceBase extends AccountOwnedTemplateResource<Bubb
} }


@DELETE @Path("/{id}") @DELETE @Path("/{id}")
public Response delete(@Context ContainerRequest ctx,
@PathParam("id") String id) {
@Override public Response delete(@Context Request req,
@Context ContainerRequest ctx,
@PathParam("id") String id) {


if (isReadOnly(ctx)) return forbidden(); if (isReadOnly(ctx)) return forbidden();




+ 4
- 2
bubble-server/src/main/java/bubble/resources/app/DataResourceBase.java View File

@@ -16,6 +16,7 @@ import bubble.model.device.Device;
import bubble.resources.account.AccountOwnedTemplateResource; import bubble.resources.account.AccountOwnedTemplateResource;
import bubble.server.BubbleConfiguration; import bubble.server.BubbleConfiguration;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.glassfish.grizzly.http.server.Request;
import org.glassfish.jersey.server.ContainerRequest; import org.glassfish.jersey.server.ContainerRequest;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;


@@ -86,14 +87,15 @@ public abstract class DataResourceBase extends AccountOwnedTemplateResource<AppD
} }


@POST @Path("/{id}"+EP_ACTIONS+"/{action}") @POST @Path("/{id}"+EP_ACTIONS+"/{action}")
public Response takeAction(@Context ContainerRequest ctx,
public Response takeAction(@Context Request req,
@Context ContainerRequest ctx,
@PathParam("id") String id, @PathParam("id") String id,
@PathParam("action") String action) { @PathParam("action") String action) {
if (isReadOnly(ctx)) return forbidden(); if (isReadOnly(ctx)) return forbidden();
switch (action) { switch (action) {
case "enable": return enable(ctx, id); case "enable": return enable(ctx, id);
case "disable": return disable(ctx, id); case "disable": return disable(ctx, id);
case "delete": return delete(ctx, id);
case "delete": return delete(req, ctx, id);
default: default:
app.getDataConfig().getDataDriver(configuration).takeAction(id, action); app.getDataConfig().getDataDriver(configuration).takeAction(id, action);
return ok(); return ok();


+ 2
- 1
bubble-server/src/main/java/bubble/resources/bill/AccountPlansResource.java View File

@@ -306,7 +306,8 @@ public class AccountPlansResource extends AccountOwnedResource<AccountPlan, Acco


// If the accountPlan is not found, look for an orphaned network // If the accountPlan is not found, look for an orphaned network
@DELETE @Path("/{id}") @DELETE @Path("/{id}")
@Override public Response delete(@Context ContainerRequest ctx,
@Override public Response delete(@Context Request req,
@Context ContainerRequest ctx,
@PathParam("id") String id) { @PathParam("id") String id) {
final Account caller = checkEditable(ctx); final Account caller = checkEditable(ctx);
AccountPlan found = find(ctx, id); AccountPlan found = find(ctx, id);


+ 15
- 29
bubble-server/src/main/java/bubble/resources/device/FlexRoutersResource.java View File

@@ -11,16 +11,9 @@ import org.glassfish.grizzly.http.server.Request;
import org.glassfish.jersey.server.ContainerRequest; import org.glassfish.jersey.server.ContainerRequest;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;


import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;

import static bubble.ApiConstants.EP_REGISTER;
import static bubble.ApiConstants.getRemoteAddr; import static bubble.ApiConstants.getRemoteAddr;
import static org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric;
import static org.cobbzilla.wizard.resources.ResourceUtil.*;
import static org.cobbzilla.wizard.resources.ResourceUtil.invalidEx;
import static org.cobbzilla.wizard.resources.ResourceUtil.userPrincipal;


@Slf4j @Slf4j
public class FlexRoutersResource extends AccountOwnedResource<FlexRouter, FlexRouterDAO> { public class FlexRoutersResource extends AccountOwnedResource<FlexRouter, FlexRouterDAO> {
@@ -34,29 +27,22 @@ public class FlexRoutersResource extends AccountOwnedResource<FlexRouter, FlexRo
return !caller.admin(); return !caller.admin();
} }


@POST @Path("{id}"+EP_REGISTER)
public Response register(@Context Request req,
@Context ContainerRequest ctx,
@PathParam("id") String id,
FlexRouter request) {
// caller must be admin
if (isReadOnly(ctx)) return forbidden();
@Override protected FlexRouter findAlternate(Request req, ContainerRequest ctx, FlexRouter request) {
final String remoteAddr = getRemoteAddr(req);
return getDao().findByAccountAndIp(account.getUuid(), remoteAddr);
}

@Override protected FlexRouter setReferences(ContainerRequest ctx, Request req, Account caller, FlexRouter router) {


// caller must come from a valid device
final String remoteAddr = getRemoteAddr(req); final String remoteAddr = getRemoteAddr(req);
final Device device = deviceService.findDeviceByIp(remoteAddr); final Device device = deviceService.findDeviceByIp(remoteAddr);
if (device == null) return invalid("err.device.notFound");

final FlexRouter flexRouter = getDao().findByUuid(id);
if (flexRouter == null) return notFound(id);

// set token and return
final String token = randomAlphanumeric(50);
final FlexRouter updated = getDao().update(flexRouter
.setPort(request.getPort())
.setLastSeen()
.setToken(token));
return ok(updated.setServerToken(token));
if (device == null) throw invalidEx("err.device.notFound");
router.setIp(remoteAddr);

if (!router.hasAuthToken()) throw invalidEx("err.token.required");
router.setToken(router.getAuth_token());

return super.setReferences(ctx, req, caller, router);
} }


} }

+ 1
- 1
bubble-server/src/main/resources/messages

@@ -1 +1 @@
Subproject commit 2b4074ab37b1e3caa0cb6382352b3391f5bb7139
Subproject commit 7692b6b6daa62e4f384db5320a30e95b5e3f71ab

+ 10
- 10
bubble-server/src/test/java/bubble/test/system/AuthTest.java View File

@@ -24,16 +24,16 @@ public class AuthTest extends ActivatedBubbleModelTestBase {
accountDAO.update(rootUser.setHashedPassword(new HashedPassword(ROOT_PASSWORD))); accountDAO.update(rootUser.setHashedPassword(new HashedPassword(ROOT_PASSWORD)));
} }


@Test public void testBasicAuth () throws Exception { modelTest("auth/basic_auth"); }
@Test public void testAccountCrud () throws Exception { modelTest("auth/account_crud"); }
@Test public void testDeviceCrud () throws Exception { modelTest("auth/device_crud"); }
@Test public void testRegistration () throws Exception { modelTest("auth/account_registration"); }
@Test public void testForgotPassword () throws Exception { modelTest("auth/forgot_password"); }
@Test public void testChangePassword () throws Exception { modelTest("auth/change_password"); }
@Test public void testBasicAuth () throws Exception { modelTest("auth/basic_auth"); }
@Test public void testAccountCrud () throws Exception { modelTest("auth/account_crud"); }
@Test public void testDeviceCrud () throws Exception { modelTest("auth/device_crud"); }
@Test public void testRegistration () throws Exception { modelTest("auth/account_registration"); }
@Test public void testForgotPassword () throws Exception { modelTest("auth/forgot_password"); }
@Test public void testChangePassword () throws Exception { modelTest("auth/change_password"); }
@Test public void testChangeAdminPassword () throws Exception { modelTest("auth/change_admin_password"); } @Test public void testChangeAdminPassword () throws Exception { modelTest("auth/change_admin_password"); }
@Test public void testTotpAuth () throws Exception { modelTest("auth/totp_auth"); }
@Test public void testMultifactorAuth () throws Exception { modelTest("auth/multifactor_auth"); }
@Test public void testDownloadAccount () throws Exception { modelTest("auth/download_account"); }
@Test public void testNetworkAuth () throws Exception { modelTest("auth/network_auth"); }
@Test public void testTotpAuth () throws Exception { modelTest("auth/totp_auth"); }
@Test public void testMultifactorAuth () throws Exception { modelTest("auth/multifactor_auth"); }
@Test public void testDownloadAccount () throws Exception { modelTest("auth/download_account"); }
@Test public void testNetworkAuth () throws Exception { modelTest("auth/network_auth"); }


} }

Loading…
Cancel
Save