[vizio] Improve handling of TV's self-signed certificate (#14429)

Signed-off-by: Michael Lobstein <michael.lobstein@gmail.com>
This commit is contained in:
mlobstein 2023-02-21 08:58:48 -06:00 committed by GitHub
parent 8c56a0f0b3
commit 7c20a4804a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 38 additions and 87 deletions

View File

@ -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` &amp; `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

View File

@ -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;
}

View File

@ -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)),

View File

@ -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();
}
}

View File

@ -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);

View File

@ -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);
}
}