diff --git a/hal-core/build.gradle b/hal-core/build.gradle index 8433b860..a0811c12 100644 --- a/hal-core/build.gradle +++ b/hal-core/build.gradle @@ -4,4 +4,6 @@ dependencies { implementation 'org.xerial:sqlite-jdbc:3.32.3.2' + implementation 'org.shredzone.acme4j:acme4j-client:2.12' + implementation 'org.shredzone.acme4j:acme4j-utils:2.12' } diff --git a/hal-core/src/se/hal/HalContext.java b/hal-core/src/se/hal/HalContext.java index 4ec181c6..6dac5dbd 100644 --- a/hal-core/src/se/hal/HalContext.java +++ b/hal-core/src/se/hal/HalContext.java @@ -22,6 +22,8 @@ public class HalContext { // Constants public static final String CONFIG_HTTP_PORT = "hal_core.http_port"; + public static final String CONFIG_HTTP_EXTERNAL_PORT = "hal_core.http_external_port"; + public static final String CONFIG_HTTP_EXTERNAL_DOMAIN = "hal_core.http_external_domain"; public static final String CONFIG_MAP_BACKGROUND_IMAGE = "hal_core.map_bgimage"; public static final String RESOURCE_ROOT; diff --git a/hal-core/src/se/hal/HalServer.java b/hal-core/src/se/hal/HalServer.java index 2bb7a771..ef21bf88 100644 --- a/hal-core/src/se/hal/HalServer.java +++ b/hal-core/src/se/hal/HalServer.java @@ -4,15 +4,19 @@ package se.hal; import se.hal.intf.*; import se.hal.page.HalAlertManager; import se.hal.struct.PluginConfig; +import se.hal.util.HalAcmeDataStore; import zutil.db.DBConnection; import zutil.io.file.FileUtil; import zutil.log.LogUtil; +import zutil.net.acme.AcmeClient; +import zutil.net.http.HttpPage; import zutil.net.http.HttpServer; import zutil.net.http.page.HttpFilePage; import zutil.net.http.page.HttpRedirectPage; import zutil.plugin.PluginData; import zutil.plugin.PluginManager; +import java.security.cert.Certificate; import java.sql.SQLException; import java.util.ArrayList; import java.util.Iterator; @@ -22,6 +26,9 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.logging.Level; import java.util.logging.Logger; +import static se.hal.HalContext.CONFIG_HTTP_EXTERNAL_DOMAIN; +import static se.hal.HalContext.CONFIG_HTTP_EXTERNAL_PORT; + /** * Main class for Hal */ @@ -34,7 +41,7 @@ public class HalServer { private static List daemons = new ArrayList<>(); private static HttpServer http; - private static List pages = new ArrayList<>(); + private static HttpServer httpExternal; private static PluginManager pluginManager; @@ -51,8 +58,9 @@ public class HalServer { // init variables pluginManager = new PluginManager(); - daemonExecutor = Executors.newScheduledThreadPool(1); // We set only one thread for easier troubleshooting + daemonExecutor = Executors.newScheduledThreadPool(1); // We set only one thread for easier troubleshooting for now http = new HttpServer(HalContext.getIntegerProperty(HalContext.CONFIG_HTTP_PORT)); + http.start(); // Upgrade database HalDatabaseUpgradeManager.initialize(pluginManager); @@ -65,6 +73,27 @@ public class HalServer { logger.info("Working directory: " + FileUtil.find(".").getAbsolutePath()); + // ------------------------------------ + // Initialize External HttpServer + // ------------------------------------ + + if (HalContext.containsProperty(CONFIG_HTTP_EXTERNAL_PORT) && + HalContext.containsProperty(CONFIG_HTTP_EXTERNAL_DOMAIN)) { + // Start a non secure server to retrieve handle the ACME protocol challenge + HttpServer tmpHttpExternal = new HttpServer(HalContext.getIntegerProperty(CONFIG_HTTP_EXTERNAL_PORT)); + tmpHttpExternal.start(); + + AcmeClient acme = new AcmeClient(new HalAcmeDataStore()); + Certificate certificate = acme.fetchCertificate(tmpHttpExternal, HalContext.getStringProperty(CONFIG_HTTP_EXTERNAL_DOMAIN)); + + tmpHttpExternal.close(); + httpExternal = new HttpServer(HalContext.getIntegerProperty(CONFIG_HTTP_EXTERNAL_PORT)); + httpExternal.start(); + } else { + logger.warning("Missing '" + CONFIG_HTTP_EXTERNAL_PORT + "' and '" + CONFIG_HTTP_EXTERNAL_DOMAIN + "' configuration, will not setup external http server."); + return; + } + // ------------------------------------ // Initialize Plugins // ------------------------------------ @@ -124,7 +153,6 @@ public class HalServer { registerPage(it.next()); for (Iterator it = pluginManager.getSingletonIterator(HalWebPage.class); it.hasNext(); ) registerPage(it.next()); - http.start(); } catch (Exception e) { logger.log(Level.SEVERE, "Startup failed.", e); @@ -172,11 +200,45 @@ public class HalServer { daemon.initiate(daemonExecutor); } + /** - * @param page registers the given page with the intranet Hal web server. + * Registers the given page with the intranet Hal web server. + * + * @param url is the web path to the page. + * @param page is the page to register with the server. + */ + public static void registerPage(String url, HttpPage page){ + http.setPage(url, page); + } + + /** + * Registers the given page with the intranet Hal web server. + * + * @param page is the page to register with the server. */ public static void registerPage(HalWebPage page){ - pages.add(page); - http.setPage(page.getId(), page); + registerPage(page.getId(), page); + } + + /** + * Registers the given page with the external Hal web server. + * Note: as this page will most likely be accessible trough the internet it needs to be robust and secure. + * + * @param url is the web path to the page. + * @param page is the page to register with the server. + */ + public static void registerExternalPage(String url, HttpPage page){ + if (httpExternal != null) + httpExternal.setPage(url, page); + } + + /** + * Registers the given page with the external Hal web server. + * Note: as this page will most likely be accessible trough the internet it needs to be robust and secure. + * + * @param page is the page to register with the server. + */ + public static void registerExternalPage(HalWebPage page){ + registerExternalPage(page.getId(), page); } } diff --git a/hal-core/src/se/hal/util/HalAcmeDataStore.java b/hal-core/src/se/hal/util/HalAcmeDataStore.java new file mode 100644 index 00000000..a686d2b9 --- /dev/null +++ b/hal-core/src/se/hal/util/HalAcmeDataStore.java @@ -0,0 +1,30 @@ +package se.hal.util; + +import zutil.net.acme.AcmeDataStore; + +import java.security.KeyPair; + +public class HalAcmeDataStore implements AcmeDataStore { + private static final String CONFIG_HTTP_EXTERNAL_USER_KEY = "hal_core.http_external_user_key"; + private static final String CONFIG_HTTP_EXTERNAL_DOMAIN_KEY = "hal_core.http_external_domain_key"; + + @Override + public KeyPair loadUserKeyPair() { + return null; + } + + @Override + public void storeUserKeyPair(KeyPair keyPair) { + + } + + @Override + public KeyPair loadDomainKeyPair() { + return null; + } + + @Override + public void storeDomainKeyPair(KeyPair keyPair) { + + } +} diff --git a/hal.conf.example b/hal.conf.example index 57b84471..6eff45dc 100644 --- a/hal.conf.example +++ b/hal.conf.example @@ -3,6 +3,8 @@ # ------------------------------------ hal_core.http_port=8080 +hal_core.http_external_port=8081 +#hal_core.http_external_domain=example.com # ------------------------------------------------------------------------ # Plugin configurations @@ -13,16 +15,15 @@ hal_core.http_port=8080 # ------------------------------------ ## Tellstick plugin -#hal_tellstick.com_port=COM5 -#hal_tellstick.com_port=/dev/serial/by-id/usb-Telldus_TellStick_Duo_A6XNNE6Z-if00-port0 +#hal_tellstick.com_port=COM5|/dev/serial/by-id/usb-Telldus_TellStick_Duo_A6XNNE6Z-if00-port0 ## NetUPS plugin #hal_nutups.host= #hal_nutups.port= ## NetScan plugin -# Network scanning should probably be disabled in some networks (default on) -#hal_netscan.ipscan=false +# Network scanning should probably be disabled in some networks +#hal_netscan.ipscan=true ## Zigbee plugin #hal_zigbee.com_port=COM4 @@ -39,5 +40,4 @@ hal_powerchallenge.sync_port=6666 # ------------------------------------ ## Google Assistant -hal_assistant.google.port=8081 hal_assistant.google.client_id=https://oauth-redirect.googleusercontent.com/r/optimal-comfort-XXXXX diff --git a/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/SmartHomeDaemon.java b/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/SmartHomeDaemon.java index 30ef44bc..b7a9ec8b 100644 --- a/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/SmartHomeDaemon.java +++ b/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/SmartHomeDaemon.java @@ -25,9 +25,9 @@ package se.hal.plugin.assistant.google; import se.hal.HalContext; +import se.hal.HalServer; import se.hal.intf.HalDaemon; import zutil.log.LogUtil; -import zutil.net.http.HttpServer; import zutil.net.http.page.oauth.OAuth2AuthorizationPage; import zutil.net.http.page.oauth.OAuth2Registry; import zutil.net.http.page.oauth.OAuth2TokenPage; @@ -43,19 +43,16 @@ public class SmartHomeDaemon implements HalDaemon { public static final String ENDPOINT_TOKEN = "api/assistant/google/auth/token"; public static final String ENDPOINT_SMARTHOME = "api/assistant/google/smarthome"; - private static final String CONFIG_PORT = "hal_assistant.google.port"; private static final String CONFIG_CLIENT_ID = "hal_assistant.google.client_id"; private SmartHomeImpl smartHome; private OAuth2Registry oAuth2Registry; - private HttpServer httpServer; @Override public void initiate(ScheduledExecutorService executor) { if (smartHome == null) { - if (!HalContext.containsProperty(CONFIG_PORT) || - !HalContext.containsProperty(CONFIG_CLIENT_ID)) { - logger.severe("Missing configuration, abort initializations."); + if (!HalContext.containsProperty(CONFIG_CLIENT_ID)) { + logger.severe("Missing configuration, aborting initializations."); return; } @@ -65,11 +62,9 @@ public class SmartHomeDaemon implements HalDaemon { oAuth2Registry.addWhitelist(HalContext.getStringProperty(CONFIG_CLIENT_ID)); oAuth2Registry.setTokenListener(smartHome); - httpServer = new HttpServer(HalContext.getIntegerProperty(CONFIG_PORT)); - httpServer.setPage(ENDPOINT_AUTH, new OAuth2AuthorizationPage(oAuth2Registry)); - httpServer.setPage(ENDPOINT_TOKEN, new OAuth2TokenPage(oAuth2Registry)); - httpServer.setPage(ENDPOINT_SMARTHOME, new SmartHomePage(smartHome)); - httpServer.start(); + HalServer.registerExternalPage(ENDPOINT_AUTH, new OAuth2AuthorizationPage(oAuth2Registry)); + HalServer.registerExternalPage(ENDPOINT_TOKEN, new OAuth2TokenPage(oAuth2Registry)); + HalServer.registerExternalPage(ENDPOINT_SMARTHOME, new SmartHomePage(smartHome)); } }