diff options
Diffstat (limited to 'central/src/main/java/moe/yuuta/dn42peering/manage/ManageHandler.java')
-rw-r--r-- | central/src/main/java/moe/yuuta/dn42peering/manage/ManageHandler.java | 745 |
1 files changed, 24 insertions, 721 deletions
diff --git a/central/src/main/java/moe/yuuta/dn42peering/manage/ManageHandler.java b/central/src/main/java/moe/yuuta/dn42peering/manage/ManageHandler.java index 052f97d..b64bb13 100644 --- a/central/src/main/java/moe/yuuta/dn42peering/manage/ManageHandler.java +++ b/central/src/main/java/moe/yuuta/dn42peering/manage/ManageHandler.java @@ -1,14 +1,7 @@ package moe.yuuta.dn42peering.manage; -import com.wireguard.crypto.Key; -import com.wireguard.crypto.KeyFormatException; -import edazdarevic.commons.net.CIDRUtils; -import io.vertx.core.AsyncResult; import io.vertx.core.Future; -import io.vertx.core.Handler; import io.vertx.core.Vertx; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.http.HttpHeaders; import io.vertx.core.impl.logging.Logger; import io.vertx.core.impl.logging.LoggerFactory; import io.vertx.core.json.JsonObject; @@ -27,32 +20,25 @@ import io.vertx.json.schema.SchemaParser; import io.vertx.json.schema.SchemaRouter; import io.vertx.json.schema.SchemaRouterOptions; import io.vertx.json.schema.common.dsl.ObjectSchemaBuilder; -import moe.yuuta.dn42peering.provision.BGPRequestCommon; -import moe.yuuta.dn42peering.provision.WGRequestCommon; import moe.yuuta.dn42peering.asn.IASNService; import moe.yuuta.dn42peering.jaba.Pair; import moe.yuuta.dn42peering.node.INodeService; -import moe.yuuta.dn42peering.node.Node; import moe.yuuta.dn42peering.peer.IPeerService; import moe.yuuta.dn42peering.peer.Peer; -import moe.yuuta.dn42peering.peer.ProvisionStatus; import moe.yuuta.dn42peering.portal.FormException; import moe.yuuta.dn42peering.portal.HTTPException; import moe.yuuta.dn42peering.portal.ISubRouter; import moe.yuuta.dn42peering.provision.IProvisionRemoteService; import moe.yuuta.dn42peering.whois.IWhoisService; -import moe.yuuta.dn42peering.whois.WhoisObject; -import org.apache.commons.validator.routines.InetAddressValidator; import javax.annotation.Nonnull; -import javax.annotation.Nullable; import java.io.IOException; -import java.net.Inet6Address; import java.util.*; -import java.util.stream.Collectors; import static io.vertx.ext.web.validation.builder.Parameters.param; import static io.vertx.json.schema.common.dsl.Schemas.*; +import static moe.yuuta.dn42peering.manage.ManagementUI.*; +import static moe.yuuta.dn42peering.manage.ManagementProvision.*; public class ManageHandler implements ISubRouter { private final Logger logger = LoggerFactory.getLogger(getClass().getSimpleName()); @@ -88,32 +74,15 @@ public class ManageHandler implements ISubRouter { final String asn = ctx.user().principal().getString("username"); Future.<List<moe.yuuta.dn42peering.peer.Peer>>future(f -> peerService.listUnderASN(asn, f)) - .<Buffer>compose(peers -> Future.future(f -> renderIndex(engine, asn, peers, f))) - .onComplete(res -> { - if (res.succeeded()) { - ctx.response() - .putHeader(HttpHeaders.CONTENT_TYPE, "text/html") - .end(res.result()); - } else { - ctx.fail(res.cause()); - } - }); + .onSuccess(peers -> renderIndex(engine, asn, peers, ctx)) + .onFailure(ctx::fail); }); router.get("/new") .produces("text/html") .handler(ctx -> { final String asn = ctx.user().principal().getString("username"); - renderForm(engine, nodeService, true, asn, null, null, res -> { - if (res.succeeded()) { - ctx.response() - .putHeader(HttpHeaders.CONTENT_TYPE, "text/html") - .end(res.result()); - } else { - res.cause().printStackTrace(); - ctx.fail(res.cause()); - } - }); + renderForm(engine, nodeService, true, asn, null, null, ctx); }); final ObjectSchemaBuilder registerSchema = objectSchema() @@ -147,36 +116,7 @@ public class ManageHandler implements ISubRouter { peer.setAsn(asn); return Future.succeededFuture(peer); }).compose(peer -> - Future.<WhoisObject>future(f -> whoisService.query(peer.getIpv4(), f)) - .compose(whoisObject -> { - if (whoisObject == null || - !whoisObject.containsKey("origin") || - !whoisObject.get("origin").contains(asn)) { - return Future.failedFuture(new FormException(peer, - "The IPv4 address you specified does not have a route with your ASN.")); - } - // Verify IPv6 - try { - if (peer.getIpv6() != null && !peer.isIPv6LinkLocal()) { - return Future.<WhoisObject>future(f -> whoisService.query(peer.getIpv6(), f)) - .compose(ipv6Whois -> { - if (ipv6Whois == null || - !ipv6Whois.containsKey("origin") || - !ipv6Whois.get("origin").contains(asn)) { - return Future.failedFuture(new FormException(peer, - "The IPv6 address you specified does not have a route with your ASN.")); - } else { - return Future.succeededFuture(peer); - } - }); - } else { - // Do not check for IPv6 address. - return Future.succeededFuture(peer); - } - } catch (IOException e) { - return Future.failedFuture(e); - } - }) + ManagementVerify.verifyAllOrThrow(whoisService, peer, asn) ).<Peer>compose(peer -> Future.future(f -> { boolean needCheckIP6 = true; try { @@ -211,7 +151,7 @@ public class ManageHandler implements ISubRouter { .putHeader("Location", "/manage") .end(); provisionPeer(nodeService, provisionService, peer).onComplete(ar -> - this.handleProvisionResult(peerService, peer, ar)); + handleProvisionResult(peerService, peer, ar)); }) .onFailure(err -> { if (err instanceof FormException) { @@ -219,15 +159,7 @@ public class ManageHandler implements ISubRouter { true, asn, ((Peer) ((FormException) err).data), Arrays.asList(((FormException) err).errors), - res -> { - if (res.succeeded()) { - ctx.response() - .putHeader(HttpHeaders.CONTENT_TYPE, "text/html") - .end(res.result()); - } else { - ctx.fail(res.cause()); - } - }); + ctx); } else { if(!(err instanceof HTTPException)) logger.error("Cannot add peer.", err); ctx.fail(err); @@ -252,19 +184,9 @@ public class ManageHandler implements ISubRouter { } return Future.succeededFuture(peer); }) - .<Buffer>compose(peer -> Future.future(f -> renderForm(engine, nodeService, false, - asn, peer, null, f))) - .onComplete(res -> { - if (res.succeeded()) { - ctx.response() - .putHeader(HttpHeaders.CONTENT_TYPE, "text/html") - .end(res.result()); - } else { - if(!(res.cause() instanceof HTTPException)) logger.error(String.format("Cannot show peer %s", id), - res.cause()); - ctx.fail(res.cause()); - } - }); + .onSuccess(peer -> Future.future(f -> renderForm(engine, nodeService, false, + asn, peer, null, ctx))) + .onFailure(ctx::fail); }); router.post("/edit") @@ -300,63 +222,17 @@ public class ManageHandler implements ISubRouter { }) ).compose(peer -> { final Peer inPeer = peer.b; - return Future.<WhoisObject>future(f -> whoisService.query(inPeer.getIpv4(), f)) - .compose(whoisObject -> { - if (whoisObject == null || - !whoisObject.containsKey("origin") || - !whoisObject.get("origin").contains(asn)) { - return Future.failedFuture(new FormException(inPeer, - "The IPv4 address you specified does not have a route with your ASN.")); - } - // Verify IPv6 - try { - if (inPeer.getIpv6() != null && !inPeer.isIPv6LinkLocal()) { - return Future.<WhoisObject>future(f -> whoisService.query(inPeer.getIpv6(), f)) - .compose(ipv6Whois -> { - if (ipv6Whois == null || - !ipv6Whois.containsKey("origin") || - !ipv6Whois.get("origin").contains(asn)) { - return Future.failedFuture(new FormException(inPeer, - "The IPv6 address you specified does not have a route with your ASN.")); - } else { - return Future.succeededFuture(peer); - } - }); - } else { - // Do not check for IPv6 address. - return Future.succeededFuture(peer); - } - } catch (IOException e) { - return Future.failedFuture(e); - } - }); + return ManagementVerify.verifyAllOrThrow(whoisService, inPeer, asn) + .compose(_peer -> Future.succeededFuture(peer)); } ).<Pair<Peer /* Existing */, Peer /* Input */>>compose(peer -> { final Peer existingPeer = peer.a; final Peer inPeer = peer.b; - boolean needCheckIPv4Conflict; - boolean needCheckIPv6Conflict; - - if (existingPeer.getType() != inPeer.getType()) { - needCheckIPv4Conflict = true; - needCheckIPv6Conflict = true; - } else { - needCheckIPv4Conflict = - !Objects.equals(existingPeer.getIpv4(), inPeer.getIpv4()); - needCheckIPv6Conflict = - !Objects.equals(existingPeer.getIpv6(), inPeer.getIpv6()); - if (inPeer.getIpv6() == null) needCheckIPv6Conflict = false; - else { - try { - if(inPeer.isIPv6LinkLocal()) - needCheckIPv6Conflict = false; - } catch (IOException ignored) {} - } - } - final boolean nc6 = needCheckIPv6Conflict; + final boolean needCheckIPv4Conflict = ManagementVerify.determineIfNeedCheckIPv4(existingPeer, inPeer); + final boolean needCheckIPv6Conflict = ManagementVerify.determineIfNeedCheckIPv6(existingPeer, inPeer); return Future.future(f -> peerService.isIPConflict(inPeer.getType(), needCheckIPv4Conflict ? inPeer.getIpv4() : null, - nc6 ? inPeer.getIpv6() : null, + needCheckIPv6Conflict ? inPeer.getIpv6() : null, ar -> { if (ar.succeeded()) { if (ar.result()) { @@ -383,7 +259,7 @@ public class ManageHandler implements ISubRouter { final Peer existingPeer = pair.a; final Peer inPeer = pair.b; reloadPeer(nodeService, provisionService, existingPeer, inPeer).onComplete(ar -> - this.handleProvisionResult(peerService, inPeer, ar)); + handleProvisionResult(peerService, inPeer, ar)); }) .onFailure(err -> { if (err instanceof FormException) { @@ -401,15 +277,7 @@ public class ManageHandler implements ISubRouter { asn, peer, Arrays.asList(((FormException) err).errors), - res -> { - if (res.succeeded()) { - ctx.response() - .putHeader(HttpHeaders.CONTENT_TYPE, "text/html") - .end(res.result()); - } else { - ctx.fail(res.cause()); - } - }); + ctx); } else { if(!(err instanceof HTTPException)) logger.error("Cannot edit peer.", err); ctx.fail(err); @@ -449,16 +317,7 @@ public class ManageHandler implements ISubRouter { .produces("text/html") .handler(ctx -> { final String asn = ctx.user().principal().getString("username"); - renderChangepw(engine, asn, null, res -> { - if (res.succeeded()) { - ctx.response() - .putHeader(HttpHeaders.CONTENT_TYPE, "text/html") - .end(res.result()); - } else { - res.cause().printStackTrace(); - ctx.fail(res.cause()); - } - }); + renderChangepw(engine, asn, null, ctx); }); router.post("/change-password") @@ -499,16 +358,7 @@ public class ManageHandler implements ISubRouter { if (err instanceof FormException) { renderChangepw(engine, asn, Arrays.asList(((FormException) err).errors), - res -> { - if (res.succeeded()) { - ctx.response() - .putHeader(HttpHeaders.CONTENT_TYPE, "text/html") - .end(res.result()); - } else { - res.cause().printStackTrace(); - ctx.fail(res.cause()); - } - }); + ctx); } else { if(!(err instanceof HTTPException)) logger.error("Cannot change password.", err); ctx.fail(err); @@ -520,16 +370,7 @@ public class ManageHandler implements ISubRouter { .produces("text/html") .handler(ctx -> { final String asn = ctx.user().principal().getString("username"); - renderDA(engine, asn, null, res -> { - if (res.succeeded()) { - ctx.response() - .putHeader(HttpHeaders.CONTENT_TYPE, "text/html") - .end(res.result()); - } else { - res.cause().printStackTrace(); - ctx.fail(res.cause()); - } - }); + renderDA(engine, asn, null, ctx); }); router.post("/delete-account") @@ -558,16 +399,7 @@ public class ManageHandler implements ISubRouter { if (err instanceof FormException) { renderDA(engine, asn, Arrays.asList(((FormException) err).errors), - res -> { - if (res.succeeded()) { - ctx.response() - .putHeader(HttpHeaders.CONTENT_TYPE, "text/html") - .end(res.result()); - } else { - res.cause().printStackTrace(); - ctx.fail(res.cause()); - } - }); + ctx); } else { logger.error("Cannot delete account.", err); ctx.fail(err); @@ -592,539 +424,10 @@ public class ManageHandler implements ISubRouter { } return Future.succeededFuture(peer); }) - .compose(peer -> renderShowConfig(nodeService, engine, peer)) - .onComplete(res -> { - if (res.succeeded()) { - ctx.response() - .putHeader(HttpHeaders.CONTENT_TYPE, "text/html") - .end(res.result()); - } else { - if(!(res.cause() instanceof HTTPException)) - logger.error(String.format("Cannot render show configuration %s", id), - res.cause()); - ctx.fail(res.cause()); - } - }); + .onSuccess(peer -> renderShowConfig(nodeService, engine, peer, ctx)) + .onFailure(ctx::fail); }); return router; } - - @Nonnull - private Future<Peer> parseForm(@Nonnull INodeService nodeService, - @Nonnull JsonObject form) { - // Parse form - int nodeId = -1; - if (form.containsKey("node")) { - try { - nodeId = Integer.parseInt(form.getString("node")); - } catch (NumberFormatException ignored) { - } - if(nodeId == -1) { - return Future.failedFuture(new FormException("The node selection is invalid.")); - } - } - - int n = nodeId; - return Future.<Node>future(f -> nodeService.getNode(n, f)) - .compose(node -> { - try { - final List<String> errors = new ArrayList<>(10); - if(node == null) { - errors.add("The node selection is invalid."); - } - Peer.VPNType type = null; - if (form.containsKey("vpn")) { - final String rawVPN = form.getString("vpn"); - if (rawVPN == null) { - errors.add("Tunneling type is not specified."); - } else - switch (rawVPN) { - case "wg": - type = Peer.VPNType.WIREGUARD; - break; - default: - errors.add("Tunneling type is unexpected."); - break; - } - } else { - errors.add("Tunneling type is not specified."); - } - - String ipv4 = null; - if (form.containsKey("ipv4")) { - ipv4 = form.getString("ipv4"); - if (ipv4 == null || ipv4.isEmpty()) { - errors.add("IPv4 address is not specified."); - ipv4 = null; // Non-null but empty values could cause problems. - } else { - if (InetAddressValidator.getInstance().isValidInet4Address(ipv4)) { - if (!new CIDRUtils("172.20.0.0/14").isInRange(ipv4)) { - errors.add("IPv4 address is illegal. It must be a dn42 IPv4 address (172.20.x.x to 172.23.x.x)."); - } - } else - errors.add("IPv4 address is illegal. Cannot parse your address."); - } - } else { - errors.add("IPv4 address is not specified."); - } - - String ipv6 = null; - if (form.containsKey("ipv6")) { - ipv6 = form.getString("ipv6"); - if (ipv6 != null && !ipv6.isEmpty()) { - if (InetAddressValidator.getInstance().isValidInet6Address(ipv6)) { - if (!new CIDRUtils("fd00::/8").isInRange(ipv6) && - !Inet6Address.getByName(ipv6).isLinkLocalAddress()) { - errors.add("IPv6 address is illegal. It must be a dn42 or link-local IPv6 address."); - } - ipv6 = ipv6.replaceAll("((?::0\\b){2,}):?(?!\\S*\\b\\1:0\\b)(\\S*)", "::$2"); - } else - errors.add("IPv6 address is illegal. Cannot parse your address."); - } else { - ipv6 = null; // Non-null but empty values could cause problems. - } - } - - boolean mpbgp = false; - if (form.containsKey("mpbgp")) { - if (ipv6 == null) { - errors.add("MP-BGP cannot be enabled if you do not have a valid IPv6 address."); - } else { - mpbgp = true; - } - } - - String wgEndpoint = null; - boolean wgEndpointCorrect = false; - if (form.containsKey("wg_endpoint")) { - if (type == Peer.VPNType.WIREGUARD) { - wgEndpoint = form.getString("wg_endpoint"); - if (wgEndpoint != null && !wgEndpoint.isEmpty()) { - if (InetAddressValidator.getInstance().isValidInet4Address(wgEndpoint)) { - if (new CIDRUtils("10.0.0.0/8").isInRange(wgEndpoint) || - new CIDRUtils("192.168.0.0/16").isInRange(wgEndpoint) || - new CIDRUtils("172.16.0.0/23").isInRange(wgEndpoint)) { - errors.add("WireGuard EndPoint is illegal. It must not be an internal address."); - } else { - wgEndpointCorrect = true; - } - } else - errors.add("WireGuard EndPoint is illegal. Cannot parse your address."); - } else { - wgEndpoint = null; // Non-null but empty values could cause problems. - } - } else { - errors.add("WireGuard tunneling is not selected but WireGuard Endpoint configuration appears."); - } - } - - Integer wgEndpointPort = null; - if (form.containsKey("wg_endpoint_port")) { - if (type == Peer.VPNType.WIREGUARD) { - final String rawPort = form.getString("wg_endpoint_port"); - if(rawPort != null && !rawPort.isEmpty()) { - if (wgEndpointCorrect) { - try { - wgEndpointPort = Integer.parseInt(rawPort); - if (wgEndpointPort < 0 || wgEndpointPort > 65535) { - errors.add("WireGuard EndPoint port must be in UDP port range."); - } - } catch (NumberFormatException | NullPointerException ignored) { - errors.add("WireGuard EndPoint port is not valid. It must be a number."); - } - } else { - errors.add("WireGuard EndPoint IP is not specified or invalid, but port is specified."); - } - } - } else { - errors.add("WireGuard tunneling is not selected but WireGuard Endpoint configuration appears."); - } - } - - // When user specified the endpoint without the port. - if(type == Peer.VPNType.WIREGUARD && - wgEndpointCorrect && - wgEndpointPort == null) { - errors.add("WireGuard EndPoint IP is specified, but the port is missing."); - } - - String wgPubKey = null; - if (form.containsKey("wg_pubkey")) { - if (type == Peer.VPNType.WIREGUARD) { - wgPubKey = form.getString("wg_pubkey"); - if (wgPubKey == null || wgPubKey.isEmpty()) { - errors.add("WireGuard public key is not specified."); - wgPubKey = null; // Non-null but empty values could cause problems. - } else { - try { - Key.fromBase64(wgPubKey); - } catch (KeyFormatException e) { - errors.add("WireGuard public key is not valid."); - } - } - } else { - errors.add("WireGuard tunneling is not selected but WireGuard public key appears."); - } - } else { - if (type == Peer.VPNType.WIREGUARD) { - errors.add("WireGuard public key is not specified."); - } - } - - if(node != null && !node.getSupportedVPNTypes().contains(type)) { - errors.add(String.format("Node %s does not support VPN type %s.", node.getName(), - type)); - } - - Peer peer; - if (type == Peer.VPNType.WIREGUARD) { - peer = new Peer(ipv4, ipv6, wgEndpoint, wgEndpointPort, wgPubKey, mpbgp, n); - } else { - peer = new Peer( - -1, - type, - null, /* ASN: To be filled later */ - ipv4, - ipv6, - wgEndpoint, - wgEndpointPort, - null, /* Self public key: Generate later if needed */ - null, /* Self private key: Generate later if needed */ - wgPubKey, - null /* Preshared Secret: Generate later if needed */, - ProvisionStatus.NOT_PROVISIONED, - mpbgp, - n - ); - } - if(errors.isEmpty()) { - return Future.succeededFuture(peer); - } else { - return Future.failedFuture(new FormException(peer, errors.toArray(new String[]{}))); - } - } catch (IOException e) { - return Future.failedFuture(e); - } - }); - } - - private void renderIndex(@Nonnull TemplateEngine engine, - @Nonnull String asn, @Nonnull List<Peer> peers, - @Nonnull Handler<AsyncResult<Buffer>> handler) { - final Map<String, Object> root = new HashMap<>(); - root.put("asn", asn); - root.put("peers", peers.stream() - .map(peer -> { - final Map<String, Object> map = new HashMap<>(); - map.put("id", peer.getId()); - map.put("ipv4", peer.getIpv4()); - map.put("ipv6", peer.getIpv6()); - map.put("type", peer.getType()); - map.put("provisionStatus", peer.getProvisionStatus()); - return map; - }) - .collect(Collectors.toList())); - engine.render(root, "manage/index.ftlh", handler); - } - - @SuppressWarnings("unchecked") - private void renderForm(@Nonnull TemplateEngine engine, - @Nonnull INodeService nodeService, - boolean newForm, - @Nonnull String asn, @Nullable Peer peer, @Nullable List<String> errors, - @Nonnull Handler<AsyncResult<Buffer>> handler) { - Future.future(nodeService::listNodes) - .compose(list -> { - final Map<String, Object> root = new HashMap<>(); - root.put("asn", asn); - root.put("nodes", list.stream() - .map(node -> { - final Map<String, Object> map = new HashMap<>(10); - map.put("id", node.getId()); - map.put("name", node.getName()); - map.put("public_ip", node.getPublicIp()); - map.put("notice", node.getNotice()); - map.put("asn", node.getAsn()); - map.put("vpn_types", node.getSupportedVPNTypes()); - return map; - }) - .collect(Collectors.toList())); - if (peer != null) { - root.put("ipv4", peer.getIpv4()); - root.put("ipv6", peer.getIpv6()); - switch (peer.getType()) { - case WIREGUARD: - root.put("typeWireguard", true); - break; - } - root.put("wgEndpoint", peer.getWgEndpoint()); - root.put("wgEndpointPort", peer.getWgEndpointPort()); - root.put("wgPubkey", peer.getWgPeerPubkey()); - root.put("mpbgp", peer.isMpbgp()); - root.put("node_checked", peer.getNode()); - root.put("id", peer.getId()); - } else { - root.put("typeWireguard", true); - root.put("mpbgp", false); - root.put("node_checked", ((List<Map<String, Object>>)root.get("nodes")).get(0).get("id")); - } - if(!newForm && peer != null) - root.put("action", "/manage/edit?id=" + peer.getId()); - else - root.put("action", "/manage/new"); - root.put("errors", errors); - return engine.render(root, newForm ? "manage/new.ftlh" : "manage/edit.ftlh"); - }) - .onComplete(handler); - } - - private void renderChangepw(@Nonnull TemplateEngine engine, - @Nonnull String asn, @Nullable List<String> errors, - @Nonnull Handler<AsyncResult<Buffer>> handler) { - final Map<String, Object> root = new HashMap<>(); - root.put("asn", asn); - root.put("errors", errors); - engine.render(root, "manage/changepw.ftlh", handler); - } - - private void renderDA(@Nonnull TemplateEngine engine, - @Nonnull String asn, @Nullable List<String> errors, - @Nonnull Handler<AsyncResult<Buffer>> handler) { - final Map<String, Object> root = new HashMap<>(); - root.put("asn", asn); - root.put("errors", errors); - engine.render(root, "manage/delete.ftlh", handler); - } - - @Nonnull - private Future<Buffer> renderShowConfig(@Nonnull INodeService nodeService, - @Nonnull TemplateEngine engine, - @Nonnull Peer peer) { - return Future.<Node>future(f -> nodeService.getNode(peer.getNode(), f)) - .compose(node -> { - final Map<String, Object> root = new HashMap<>(); - root.put("ipv4", peer.getIpv4()); - root.put("ipv6", peer.getIpv6()); - switch (peer.getType()) { - case WIREGUARD: - root.put("typeWireguard", true); - break; - } - root.put("wgPort", peer.calcWireGuardPort()); - root.put("wgEndpoint", peer.getWgEndpoint()); - root.put("wgEndpointPort", peer.getWgEndpointPort()); - root.put("wgPresharedSecret", peer.getWgPresharedSecret()); - root.put("wgSelfPubkey", peer.getWgSelfPubkey()); - root.put("mpbgp", peer.isMpbgp()); - root.put("peer_type", peer.getType()); - root.put("peer_ipv4", peer.getIpv4()); - root.put("peer_ipv6", peer.getIpv6()); - if(peer.getWgEndpoint() != null) { - root.put("peer_wg_listen_port", peer.getWgEndpointPort()); - } - - if(node == null) { - root.put("ipv4", "This node is currently down! Edit the peer to choose another one."); - root.put("ipv6", "This node is currently down! Edit the peer to choose another one."); - root.put("asn", "This node is currently down! Edit the peer to choose another one."); - root.put("endpoint", "This node is currently down! Edit the peer to choose another one."); - root.put("show_example_config", false); - } else { - root.put("show_example_config", true); - root.put("ipv4", node.getDn42Ip4()); - try { - if(peer.isIPv6LinkLocal()) { - root.put("ipv6", node.getDn42Ip6()); - root.put("peer_link_local", true); - } else { - root.put("ipv6", node.getDn42Ip6NonLL()); - root.put("peer_link_local", false); - } - } catch (IOException e) { - return Future.failedFuture(e); - } - root.put("asn", node.getAsn().substring(2)); - root.put("endpoint", node.getPublicIp()); - } - - return engine.render(root, "manage/showconf.ftlh"); - }); - - } - - @Nonnull - private Future<Void> reloadPeer(@Nonnull INodeService nodeService, - @Nonnull IProvisionRemoteService provisionService, - @Nonnull Peer existingPeer, @Nonnull Peer inPeer) { - // Check if we can reload on the fly. - // Otherwise, we can only deprovision and provision. - // This will cause unnecessary wastes. - boolean canReload = inPeer.getType() == existingPeer.getType() && - inPeer.getNode() == existingPeer.getNode(); - // wg-quick does not support switching local IP addresses. - // However, switch between link local addresses and real IPv6 addresses require the change of - // local v6 address. Therefore, in such cases, we have to do a full re-provision. - if(canReload && // Only check if no other factors prevent us from reloading. - inPeer.getType() == Peer.VPNType.WIREGUARD && - existingPeer.getType() == Peer.VPNType.WIREGUARD) { - try { - final boolean existingLL = existingPeer.isIPv6LinkLocal(); - final boolean newLL = inPeer.isIPv6LinkLocal(); - if(existingLL != newLL) { - canReload = false; - } - } catch (IOException e) { - return Future.failedFuture(e); - } - } - // wg-quick will also not clear EndPoint setting if we just reload it. - if(canReload && // Only check if no other factors prevent us from reloading. - inPeer.getType() == Peer.VPNType.WIREGUARD && - existingPeer.getType() == Peer.VPNType.WIREGUARD) { - if(inPeer.getWgEndpoint() == null && - existingPeer.getWgEndpoint() != null) { - canReload = false; - } - } - Future<Void> future; - if (canReload) { - future = Future.<Node>future(f -> nodeService.getNode(inPeer.getNode(), f)) - .compose(node -> { - if(node == null || !node.getSupportedVPNTypes().contains(inPeer.getType())) { - return Future.failedFuture("The node does not exist"); - } - return Future.succeededFuture(node); - }) - .compose(node -> { - switch (existingPeer.getType()) { - case WIREGUARD: - final WGRequestCommon wgReq = inPeer.toWGRequest(); - wgReq.setNode(node.toRPCNode()); - return Future.<String>future(f -> provisionService.reloadWG( - node.toRPCNode(), - wgReq, - f) - ).compose(device -> Future.succeededFuture(new Pair<>(node, device))); - default: - throw new UnsupportedOperationException("Bug: Unknown type."); - } - }) - .compose(pair -> { - final BGPRequestCommon bgpReq = inPeer.toBGPRequest(); - bgpReq.setNode(pair.a.toRPCNode()); - bgpReq.setDevice(pair.b); - return Future.future(f -> provisionService.reloadBGP( - pair.a.toRPCNode(), - bgpReq, - f)); - }); - } else { - future = unprovisionPeer(nodeService, provisionService, existingPeer) - .compose(f -> provisionPeer(nodeService, provisionService, inPeer)); - } - return future; - } - - private Future<Void> unprovisionPeer(@Nonnull INodeService nodeService, - @Nonnull IProvisionRemoteService provisionService, - @Nonnull Peer existingPeer) { - return Future.<Node>future(f -> nodeService.getNode(existingPeer.getNode(), f)) - .compose(node -> { - if(node == null) { - return Future.failedFuture("The node does not exist"); - } - return Future.succeededFuture(node); - }) - .compose(node -> { - switch (existingPeer.getType()) { - case WIREGUARD: - return Future.<Void>future(f -> provisionService.deleteWG( - node.toRPCNode(), - new WGRequestCommon(null, - (long)existingPeer.getId(), - null, - null, - null, - null, - null, - null, - null), - f)) - .compose(res -> Future.succeededFuture(node)); - default: - throw new UnsupportedOperationException("Bug: Unknown type."); - } - }) - .compose(node -> { - return Future.future(f -> provisionService.deleteBGP( - node.toRPCNode(), - new BGPRequestCommon(null, - (long)existingPeer.getId(), - null, - null, - null, - null, - null), - f)); - }) - ; - } - - @Nonnull - private Future<Void> provisionPeer(@Nonnull INodeService nodeService, - @Nonnull IProvisionRemoteService provisionService, - @Nonnull Peer inPeer) { - return Future.<Node>future(f -> nodeService.getNode(inPeer.getNode(), f)) - .compose(node -> { - if(node == null || !node.getSupportedVPNTypes().contains(inPeer.getType())) { - return Future.failedFuture("The node does not exist"); - } - return Future.succeededFuture(node); - }) - .compose(node -> { - switch (inPeer.getType()) { - case WIREGUARD: - final WGRequestCommon wgReq = inPeer.toWGRequest(); - wgReq.setNode(node.toRPCNode()); - return Future.<String>future(f -> provisionService.provisionWG( - node.toRPCNode(), - wgReq, - f) - ).compose(device -> Future.succeededFuture(new Pair<>(node, device))); - default: - throw new UnsupportedOperationException("Bug: Unknown type."); - } - }) - .compose(pair -> { - final BGPRequestCommon bgpReq = inPeer.toBGPRequest(); - bgpReq.setNode(pair.a.toRPCNode()); - bgpReq.setDevice(pair.b); - return Future.future(f -> provisionService.provisionBGP( - pair.a.toRPCNode(), - bgpReq, - f)); - }); - } - - private void handleProvisionResult(@Nonnull IPeerService peerService, - @Nonnull Peer inPeer, - @Nonnull AsyncResult<Void> res) { - if(res.succeeded()) { - peerService.changeProvisionStatus(inPeer.getId(), - ProvisionStatus.PROVISIONED, ar -> { - if (ar.failed()) { - logger.error(String.format("Cannot update %d to provisioned.", inPeer.getId()), ar.cause()); - } - }); - } else { - logger.error(String.format("Cannot provision %d.", inPeer.getId()), res.cause()); - peerService.changeProvisionStatus(inPeer.getId(), - ProvisionStatus.FAIL, ar -> { - if (ar.failed()) { - logger.error(String.format("Cannot update %d to failed.", inPeer.getId()), ar.cause()); - } - }); - } - } } |