From 11a9bcc7af430f76cd17fffb0e2b2828b627d415 Mon Sep 17 00:00:00 2001 From: Jonathan Cobb Date: Tue, 14 Jan 2020 15:22:02 -0500 Subject: [PATCH] add support for downloading VPN configs from ui --- .../resources/account/VpnConfigResource.java | 65 +++++++++++++++---- .../post_auth/ResourceMessages.properties | 8 +++ bubble-web | 2 +- 3 files changed, 61 insertions(+), 14 deletions(-) diff --git a/bubble-server/src/main/java/bubble/resources/account/VpnConfigResource.java b/bubble-server/src/main/java/bubble/resources/account/VpnConfigResource.java index 9b7596c0..6a09a1c1 100644 --- a/bubble-server/src/main/java/bubble/resources/account/VpnConfigResource.java +++ b/bubble-server/src/main/java/bubble/resources/account/VpnConfigResource.java @@ -4,6 +4,8 @@ import bubble.ApiConstants; import bubble.model.account.Account; import bubble.model.device.Device; import lombok.extern.slf4j.Slf4j; +import org.cobbzilla.util.io.FileUtil; +import org.cobbzilla.util.string.Base64; import org.cobbzilla.wizard.stream.FileSendableResource; import org.glassfish.jersey.server.ContainerRequest; @@ -14,13 +16,13 @@ import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; import java.io.File; +import java.io.IOException; import static org.cobbzilla.util.http.HttpContentTypes.*; import static org.cobbzilla.util.io.FileUtil.abs; import static org.cobbzilla.wizard.resources.ResourceUtil.*; @Consumes(APPLICATION_JSON) -@Produces(APPLICATION_JSON) @Slf4j public class VpnConfigResource { @@ -30,19 +32,46 @@ public class VpnConfigResource { public VpnConfigResource(Device device) { this.device = device; } + public File getQRfile() { + final File qrFile = new File(VPN_CONFIG_PATH+device.getUuid()+".png"); + if (!qrFile.exists()) { + // todo: try to regenerate algo users? + log.error("qrCode: file not found: "+abs(qrFile)); + throw invalidEx("err.deviceQRcode.qrCodeFileNotFound"); + } + return qrFile; + } + + public File getVpnConfFile() { + final File confFile = new File(VPN_CONFIG_PATH+device.getUuid()+".conf"); + if (!confFile.exists()) { + // todo: try to regenerate algo users? + log.error("confFile: file not found: "+abs(confFile)); + throw invalidEx("err.deviceVpnConf.confFileNotFound"); + } + return confFile; + } + @GET @Path("/QR.png") @Produces(IMAGE_PNG) public Response qrCode(@Context ContainerRequest ctx) { final Account caller = userPrincipal(ctx); if (!caller.admin() && !caller.getUuid().equals(device.getAccount())) return forbidden(); + return send(new FileSendableResource(getQRfile())); + } - final File qrFile = new File(VPN_CONFIG_PATH+device.getUuid()+".png"); - if (!qrFile.exists()) { - // todo: try to regenerate algo users? - log.error("qrCode: file not found: "+abs(qrFile)); - return notFound(); + @GET @Path("/QR.png.base64") + @Produces(TEXT_PLAIN) + public Response qrCodeBase64(@Context ContainerRequest ctx) { + final Account caller = userPrincipal(ctx); + if (!caller.admin() && !caller.getUuid().equals(device.getAccount())) return forbidden(); + final String data; + try { + data = Base64.encodeBytes(FileUtil.toBytes(getQRfile())); + } catch (IOException e) { + return invalid("err.deviceQRcode.qrCodeError"); } - return send(new FileSendableResource(qrFile)); + return ok(data); } @GET @Path("/vpn.conf") @@ -51,15 +80,25 @@ public class VpnConfigResource { final Account caller = userPrincipal(ctx); if (!caller.admin() && !caller.getUuid().equals(device.getAccount())) return forbidden(); - final File confFile = new File(VPN_CONFIG_PATH+device.getUuid()+".conf"); - if (!confFile.exists()) { - // todo: try to regenerate algo users? - log.error("confFile: file not found: "+abs(confFile)); - return notFound(); - } + final File confFile = getVpnConfFile(); return send(new FileSendableResource(confFile) .setContentType(APPLICATION_OCTET_STREAM) .setForceDownload(true)); } + @GET @Path("/vpn.conf.base64") + @Produces(TEXT_PLAIN) + public Response confFileBase64(@Context ContainerRequest ctx) { + final Account caller = userPrincipal(ctx); + if (!caller.admin() && !caller.getUuid().equals(device.getAccount())) return forbidden(); + + final String data; + try { + data = Base64.encodeBytes(FileUtil.toBytes(getVpnConfFile())); + } catch (IOException e) { + return invalid("err.deviceVpnConf.confError"); + } + return ok(data); + } + } diff --git a/bubble-server/src/main/resources/message_templates/en_US/server/post_auth/ResourceMessages.properties b/bubble-server/src/main/resources/message_templates/en_US/server/post_auth/ResourceMessages.properties index 9a0757c8..460573b2 100644 --- a/bubble-server/src/main/resources/message_templates/en_US/server/post_auth/ResourceMessages.properties +++ b/bubble-server/src/main/resources/message_templates/en_US/server/post_auth/ResourceMessages.properties @@ -339,8 +339,12 @@ loading_devices=Loading devices... table_title_devices=Devices label_field_device_name=Name label_field_device_enabled=Enabled? +label_field_device_vpn_config=VPN label_field_device_ctime=Added label_device_ctime_format={{MMM}} {{d}}, {{YYYY}} / {{h}}:{{m}} {{a}} +message_device_vpn_show_config=Show VPN connection info +message_device_vpn_download_conf=Download vpn.conf file +button_label_close_device_vpn_config=Close message_no_devices=No Devices form_title_add_device=Add Device button_label_add_device=Add @@ -395,6 +399,10 @@ err.delete.invalid=You cannot delete the account that owns the current network err.deletionPolicy.required=Deletion policy is required err.description.length=Description is too long err.deviceName.notUnique=Device name is already in use +err.deviceQRcode.qrCodeError=Error loading QR code +err.deviceQRcode.qrCodeFileNotFound=QR image file not found +err.deviceVpnConf.confError=Error loading VPN conf file +err.deviceVpnConf.confFileNotFound=VPN conf file not found err.disallowedCountriesJson.length=Disallowed countries list is too long err.dns.notFound=DNS cloud service not found err.dns.required=DNS cloud service is required diff --git a/bubble-web b/bubble-web index f6cb4e83..10a000ca 160000 --- a/bubble-web +++ b/bubble-web @@ -1 +1 @@ -Subproject commit f6cb4e830debd5b69941cf7c52809e0c34491fd4 +Subproject commit 10a000cac0bda587ddd16c402672cc464689c372