@@ -18,4 +18,8 @@ public class FlexRouterDAO extends AccountOwnedEntityDAO<FlexRouter> { | |||
isNotNull("token")))); | |||
} | |||
public FlexRouter findByAccountAndIp(String accountUuid, String ip) { | |||
return findByUniqueFields("account", accountUuid, "ip", ip); | |||
} | |||
} |
@@ -10,6 +10,7 @@ import lombok.ToString; | |||
import lombok.experimental.Accessors; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.apache.commons.collections.map.SingletonMap; | |||
import org.cobbzilla.util.collection.ArrayUtil; | |||
import org.cobbzilla.wizard.model.Identifiable; | |||
import org.cobbzilla.wizard.model.IdentifiableBase; | |||
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"}) }) | |||
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); } | |||
@@ -75,10 +76,11 @@ public class FlexRouter extends IdentifiableBase implements HasAccountNoName { | |||
@JsonIgnore @Getter @Setter private String 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 pingObject() { return json(new SingletonMap("token", getToken())); } | |||
} |
@@ -91,16 +91,26 @@ public class AccountOwnedResource<E extends HasAccount, DAO extends AccountOwned | |||
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) { | |||
return getDao().findByAccountAndId(getAccountUuid(ctx), id); | |||
} | |||
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(Request req, ContainerRequest ctx, String id) { return findAlternate(ctx, id); } | |||
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(Request req, ContainerRequest ctx, String id) { return findAlternateForUpdate(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) { | |||
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; } | |||
@GET @Path("/{id}") | |||
public Response view(@Context ContainerRequest ctx, | |||
public Response view(@Context Request req, | |||
@Context ContainerRequest ctx, | |||
@PathParam("id") String id) { | |||
final Account caller = getAccountForViewById(ctx); | |||
E found = find(ctx, id); | |||
E found = find(req, ctx, id); | |||
if (found == null) { | |||
found = findAlternate(ctx, id); | |||
found = findAlternate(req, ctx, id); | |||
if (found == null) 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) { | |||
if (request == null) return invalid("err.request.invalid"); | |||
final Account caller = checkEditable(ctx); | |||
E found = find(ctx, request.getName()); | |||
E found = find(req, ctx, request.getName()); | |||
if (found == null) { | |||
found = findAlternateForCreate(ctx, request); | |||
found = findAlternateForCreate(req, ctx, request); | |||
} | |||
if (found != null) { | |||
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); } | |||
@POST @Path("/{id}") | |||
public Response update(@Context ContainerRequest ctx, | |||
public Response update(@Context Request req, | |||
@Context ContainerRequest ctx, | |||
@PathParam("id") String id, | |||
E request) { | |||
if (request == null) return invalid("err.request.invalid"); | |||
final Account caller = checkEditable(ctx); | |||
E found = find(ctx, id); | |||
E found = find(req, ctx, id); | |||
if (found == null) { | |||
found = findAlternateForUpdate(ctx, id); | |||
found = findAlternateForUpdate(req, ctx, id); | |||
if (found == null) return notFound(id); | |||
} | |||
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; } | |||
@DELETE @Path("/{id}") | |||
public Response delete(@Context ContainerRequest ctx, | |||
public Response delete(@Context Request req, | |||
@Context ContainerRequest ctx, | |||
@PathParam("id") String id) { | |||
final Account caller = checkEditable(ctx); | |||
E found = find(ctx, id); | |||
E found = find(req, ctx, id); | |||
if (found == null) { | |||
found = findAlternateForDelete(ctx, id); | |||
found = findAlternateForDelete(req, ctx, id); | |||
if (found == null) return notFound(id); | |||
} | |||
@@ -63,8 +63,9 @@ public abstract class AppsResourceBase extends AccountOwnedTemplateResource<Bubb | |||
} | |||
@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(); | |||
@@ -16,6 +16,7 @@ import bubble.model.device.Device; | |||
import bubble.resources.account.AccountOwnedTemplateResource; | |||
import bubble.server.BubbleConfiguration; | |||
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; | |||
@@ -86,14 +87,15 @@ public abstract class DataResourceBase extends AccountOwnedTemplateResource<AppD | |||
} | |||
@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("action") String action) { | |||
if (isReadOnly(ctx)) return forbidden(); | |||
switch (action) { | |||
case "enable": return enable(ctx, id); | |||
case "disable": return disable(ctx, id); | |||
case "delete": return delete(ctx, id); | |||
case "delete": return delete(req, ctx, id); | |||
default: | |||
app.getDataConfig().getDataDriver(configuration).takeAction(id, action); | |||
return ok(); | |||
@@ -306,7 +306,8 @@ public class AccountPlansResource extends AccountOwnedResource<AccountPlan, Acco | |||
// If the accountPlan is not found, look for an orphaned network | |||
@DELETE @Path("/{id}") | |||
@Override public Response delete(@Context ContainerRequest ctx, | |||
@Override public Response delete(@Context Request req, | |||
@Context ContainerRequest ctx, | |||
@PathParam("id") String id) { | |||
final Account caller = checkEditable(ctx); | |||
AccountPlan found = find(ctx, id); | |||
@@ -11,16 +11,9 @@ import org.glassfish.grizzly.http.server.Request; | |||
import org.glassfish.jersey.server.ContainerRequest; | |||
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 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 | |||
public class FlexRoutersResource extends AccountOwnedResource<FlexRouter, FlexRouterDAO> { | |||
@@ -34,29 +27,22 @@ public class FlexRoutersResource extends AccountOwnedResource<FlexRouter, FlexRo | |||
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 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 @@ | |||
Subproject commit 2b4074ab37b1e3caa0cb6382352b3391f5bb7139 | |||
Subproject commit 7692b6b6daa62e4f384db5320a30e95b5e3f71ab |
@@ -24,16 +24,16 @@ public class AuthTest extends ActivatedBubbleModelTestBase { | |||
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 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"); } | |||
} |