From 7c20a4804aab19e137bc2be00375148acf2ac21d Mon Sep 17 00:00:00 2001 From: mlobstein Date: Tue, 21 Feb 2023 08:58:48 -0600 Subject: [PATCH] [vizio] Improve handling of TV's self-signed certificate (#14429) Signed-off-by: Michael Lobstein --- bundles/org.openhab.binding.vizio/README.md | 2 +- .../vizio/internal/VizioHandlerFactory.java | 7 +-- .../communication/VizioCommunicator.java | 2 +- .../VizioTlsTrustManagerProvider.java | 59 ------------------- .../console/VizioCommandExtension.java | 2 +- .../vizio/internal/handler/VizioHandler.java | 53 ++++++++++------- 6 files changed, 38 insertions(+), 87 deletions(-) delete mode 100644 bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/communication/VizioTlsTrustManagerProvider.java diff --git a/bundles/org.openhab.binding.vizio/README.md b/bundles/org.openhab.binding.vizio/README.md index 7c72f7b0ffa..008072c0663 100644 --- a/bundles/org.openhab.binding.vizio/README.md +++ b/bundles/org.openhab.binding.vizio/README.md @@ -134,7 +134,7 @@ If an app that is in the JSON database fails to start when selected, try adjusti A current list of `APP_ID`'s can be found at http://hometest.buddytv.netdna-cdn.com/appservice/vizio_apps_prod.json and `NAME_SPACE` & `MESSAGE` values needed can be found at http://hometest.buddytv.netdna-cdn.com/appservice/app_availability_prod.json -If there is an error in the user supplied `appListJson`, the thing will fail to start and display a CONFIGURATION_PENDING message. +If there is an error in the user supplied `appListJson`, the thing will fail to start and display a CONFIGURATION_ERROR message. If all text in `appListJson` is removed (set to null) and the thing configuration saved, the binding will restore `appListJson` from the binding's JSON db. ## Full Example diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/VizioHandlerFactory.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/VizioHandlerFactory.java index 1967fb4f947..2246218deea 100644 --- a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/VizioHandlerFactory.java +++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/VizioHandlerFactory.java @@ -16,7 +16,6 @@ import static org.openhab.binding.vizio.internal.VizioBindingConstants.SUPPORTED import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.vizio.internal.appdb.VizioAppDbService; import org.openhab.binding.vizio.internal.handler.VizioHandler; import org.openhab.core.io.net.http.HttpClientFactory; @@ -39,7 +38,7 @@ import org.osgi.service.component.annotations.Reference; @Component(service = ThingHandlerFactory.class, configurationPid = "binding.vizio") public class VizioHandlerFactory extends BaseThingHandlerFactory { - private final HttpClient httpClient; + private final HttpClientFactory httpClientFactory; private final VizioStateDescriptionOptionProvider stateDescriptionProvider; private final String vizioAppsJson; @@ -47,7 +46,7 @@ public class VizioHandlerFactory extends BaseThingHandlerFactory { public VizioHandlerFactory(final @Reference HttpClientFactory httpClientFactory, final @Reference VizioStateDescriptionOptionProvider provider, final @Reference VizioAppDbService vizioAppDbService) { - this.httpClient = httpClientFactory.getCommonHttpClient(); + this.httpClientFactory = httpClientFactory; this.stateDescriptionProvider = provider; this.vizioAppsJson = vizioAppDbService.getVizioAppsJson(); } @@ -62,7 +61,7 @@ public class VizioHandlerFactory extends BaseThingHandlerFactory { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); if (SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID)) { - VizioHandler handler = new VizioHandler(thing, httpClient, stateDescriptionProvider, vizioAppsJson); + VizioHandler handler = new VizioHandler(thing, httpClientFactory, stateDescriptionProvider, vizioAppsJson); return handler; } diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/communication/VizioCommunicator.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/communication/VizioCommunicator.java index 34ff83de38f..632aa573ed9 100644 --- a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/communication/VizioCommunicator.java +++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/communication/VizioCommunicator.java @@ -196,7 +196,7 @@ public class VizioCommunicator { * @throws VizioException * */ - public PairingStart starPairing(String deviceName, int deviceId) throws VizioException { + public PairingStart startPairing(String deviceName, int deviceId) throws VizioException { return fromJson( putCommand(urlStartPairing, String.format("{ \"DEVICE_NAME\": \"%s\", \"DEVICE_ID\": \"%d\" }", deviceName, deviceId)), diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/communication/VizioTlsTrustManagerProvider.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/communication/VizioTlsTrustManagerProvider.java deleted file mode 100644 index e825c190e37..00000000000 --- a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/communication/VizioTlsTrustManagerProvider.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright (c) 2010-2023 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.vizio.internal.communication; - -import java.net.MalformedURLException; -import java.security.cert.CertificateException; - -import javax.net.ssl.X509ExtendedTrustManager; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.core.io.net.http.PEMTrustManager; -import org.openhab.core.io.net.http.TlsTrustManagerProvider; -import org.openhab.core.io.net.http.TrustAllTrustManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Provides a {@link PEMTrustManager} to allow secure connections to a Vizio TV that uses self signed - * certificates. - * - * @author Christoph Weitkamp - Initial Contribution - * @author Michael Lobstein - Adapted for Vizio binding - */ -@NonNullByDefault -public class VizioTlsTrustManagerProvider implements TlsTrustManagerProvider { - private final String hostname; - - private final Logger logger = LoggerFactory.getLogger(VizioTlsTrustManagerProvider.class); - - public VizioTlsTrustManagerProvider(String hostname) { - this.hostname = hostname; - } - - @Override - public String getHostName() { - return hostname; - } - - @Override - public X509ExtendedTrustManager getTrustManager() { - try { - logger.trace("Use self-signed certificate downloaded from Vizio TV."); - return PEMTrustManager.getInstanceFromServer("https://" + getHostName()); - } catch (CertificateException | MalformedURLException e) { - logger.debug("An unexpected exception occurred - returning a TrustAllTrustManager: {}", e.getMessage(), e); - } - return TrustAllTrustManager.getInstance(); - } -} diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/console/VizioCommandExtension.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/console/VizioCommandExtension.java index 69cfb41bd35..5a55582b3ad 100644 --- a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/console/VizioCommandExtension.java +++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/console/VizioCommandExtension.java @@ -107,7 +107,7 @@ public class VizioCommandExtension extends AbstractConsoleCommandExtension { Random rng = new Random(); int pairingDeviceId = rng.nextInt(100000); - int pairingToken = communicator.starPairing(args[2], pairingDeviceId).getItem() + int pairingToken = communicator.startPairing(args[2], pairingDeviceId).getItem() .getPairingReqToken(); if (pairingToken != -1) { handler.setPairingDeviceId(pairingDeviceId); diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/handler/VizioHandler.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/handler/VizioHandler.java index 1bb3d7ae1db..1bb0975b41c 100644 --- a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/handler/VizioHandler.java +++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/handler/VizioHandler.java @@ -25,11 +25,11 @@ import java.util.concurrent.TimeUnit; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.util.ssl.SslContextFactory; import org.openhab.binding.vizio.internal.VizioConfiguration; import org.openhab.binding.vizio.internal.VizioException; import org.openhab.binding.vizio.internal.VizioStateDescriptionOptionProvider; import org.openhab.binding.vizio.internal.communication.VizioCommunicator; -import org.openhab.binding.vizio.internal.communication.VizioTlsTrustManagerProvider; import org.openhab.binding.vizio.internal.dto.app.CurrentApp; import org.openhab.binding.vizio.internal.dto.applist.VizioApp; import org.openhab.binding.vizio.internal.dto.applist.VizioApps; @@ -40,7 +40,7 @@ import org.openhab.binding.vizio.internal.dto.inputlist.InputList; import org.openhab.binding.vizio.internal.dto.power.PowerMode; import org.openhab.binding.vizio.internal.enums.KeyCommand; import org.openhab.core.config.core.Configuration; -import org.openhab.core.io.net.http.TlsTrustManagerProvider; +import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.library.types.NextPreviousType; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.PercentType; @@ -53,12 +53,11 @@ import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.ThingStatusDetail; import org.openhab.core.thing.binding.BaseThingHandler; +import org.openhab.core.thing.util.ThingWebClientUtil; import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; import org.openhab.core.types.StateOption; import org.openhab.core.types.UnDefType; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.ServiceRegistration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -74,11 +73,11 @@ import com.google.gson.JsonSyntaxException; @NonNullByDefault public class VizioHandler extends BaseThingHandler { private final Logger logger = LoggerFactory.getLogger(VizioHandler.class); - private final HttpClient httpClient; + private final HttpClientFactory httpClientFactory; + private @Nullable HttpClient httpClient; private final VizioStateDescriptionOptionProvider stateDescriptionProvider; private final String dbAppsJson; - private @Nullable ServiceRegistration serviceRegistration; private @Nullable ScheduledFuture refreshJob; private @Nullable ScheduledFuture metadataRefreshJob; @@ -97,13 +96,13 @@ public class VizioHandler extends BaseThingHandler { private boolean powerOn = false; private boolean debounce = true; - public VizioHandler(Thing thing, HttpClient httpClient, + public VizioHandler(Thing thing, HttpClientFactory httpClientFactory, VizioStateDescriptionOptionProvider stateDescriptionProvider, String vizioAppsJson) { super(thing); - this.httpClient = httpClient; + this.httpClientFactory = httpClientFactory; this.stateDescriptionProvider = stateDescriptionProvider; this.dbAppsJson = vizioAppsJson; - this.communicator = new VizioCommunicator(httpClient, EMPTY, -1, EMPTY); + this.communicator = new VizioCommunicator(httpClientFactory.getCommonHttpClient(), EMPTY, -1, EMPTY); } @Override @@ -127,13 +126,22 @@ public class VizioHandler extends BaseThingHandler { host = "[" + host + "]"; } - this.communicator = new VizioCommunicator(httpClient, host, config.port, authToken != null ? authToken : EMPTY); - - // register trustmanager service to allow httpClient to accept self signed cert from the Vizio TV - VizioTlsTrustManagerProvider tlsTrustManagerProvider = new VizioTlsTrustManagerProvider( - host + ":" + config.port); - serviceRegistration = FrameworkUtil.getBundle(getClass()).getBundleContext() - .registerService(TlsTrustManagerProvider.class.getName(), tlsTrustManagerProvider, null); + final String httpClientName = ThingWebClientUtil.buildWebClientConsumerName(thing.getUID(), null); + try { + httpClient = httpClientFactory.createHttpClient(httpClientName, new SslContextFactory.Client(true)); + final HttpClient localHttpClient = this.httpClient; + if (localHttpClient != null) { + localHttpClient.start(); + this.communicator = new VizioCommunicator(localHttpClient, host, config.port, + authToken != null ? authToken : EMPTY); + } + } catch (Exception e) { + logger.error( + "Long running HttpClient for Vizio handler {} cannot be started. Creating Handler failed. Exception: {}", + httpClientName, e.getMessage(), e); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR); + return; + } if (authToken == null) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING, @@ -363,11 +371,14 @@ public class VizioHandler extends BaseThingHandler { this.metadataRefreshJob = null; } - ServiceRegistration localServiceRegistration = serviceRegistration; - if (localServiceRegistration != null) { - // remove trustmanager service - localServiceRegistration.unregister(); - serviceRegistration = null; + try { + HttpClient localHttpClient = this.httpClient; + if (localHttpClient != null) { + localHttpClient.stop(); + } + this.httpClient = null; + } catch (Exception e) { + logger.debug("Unable to stop Vizio httpClient. Exception: {}", e.getMessage(), e); } }