Skip to content

Commit

Permalink
Added CR remarks; change Authentication class; throw ConfigurationExc…
Browse files Browse the repository at this point in the history
…eption

Signed-off-by: Christian Kittel <ckittel@gmx.de>
  • Loading branch information
EvilPingu committed Jan 21, 2024
1 parent 249b6b3 commit b3ef54d
Show file tree
Hide file tree
Showing 7 changed files with 53 additions and 56 deletions.
2 changes: 1 addition & 1 deletion bundles/org.openhab.binding.homematic/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ The new user should have the following configuration:

The user and password must then be entered in the 'Username' and 'Password' settings.

If this is not done the binding will not be able to connect to the CCU and the CCU Thing will stay uninitialized and sets a timeout exception:
If this is not done the binding will not be able to connect to the CCU and the CCU Thing will stay uninitialized and sets a timeout exception or a authentication error

```text
xxx-xx-xx xx:xx:xx.xxx [hingStatusInfoChangedEvent] - - 'homematic:bridge:xxx' changed from INITIALIZING to OFFLINE (COMMUNICATION_ERROR): java.net.SocketTimeoutException: Connect Timeout
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@
*/
package org.openhab.binding.homematic.internal.common;

import java.net.URI;
import java.util.Base64;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.Authentication;
import org.eclipse.jetty.client.api.AuthenticationStore;
import org.eclipse.jetty.client.util.BasicAuthentication;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpHeader;
import org.openhab.core.i18n.ConfigurationException;

/**
* Handles the authentication to Homematic server.
Expand All @@ -28,32 +28,26 @@
@NonNullByDefault
public class AuthenticationHandler {

private final HomematicConfig config;
private final HttpClient httpClient;
private Boolean useAuthentication;
private @Nullable String authValue;

public AuthenticationHandler(HomematicConfig config, HttpClient httpClient) {
this.config = config;
this.httpClient = httpClient;
public AuthenticationHandler(HomematicConfig config) throws ConfigurationException {
this.useAuthentication = config.getUseAuthentication();
if (!useAuthentication) {
return;
}

if (config.getPassword() == null || config.getUserName() == null) {
throw new ConfigurationException("Username or password missing");
}
this.authValue = "Basic "
+ Base64.getEncoder().encodeToString((config.getUserName() + ":" + config.getPassword()).getBytes());
}

/**
* Add or remove the basic auth credetials to the AuthenticationStore if needed.
* Add or remove the basic auth credentials th the request if needed.
*/
public synchronized void updateAuthenticationInformation(final URI uri) throws IllegalStateException {
final AuthenticationStore authStore = httpClient.getAuthenticationStore();

Authentication findAuthentication = authStore.findAuthentication("Basic", uri, Authentication.ANY_REALM);

if (config.getUseAuthentication()) {
if (findAuthentication == null) {
if (config.getPassword() == null || config.getUserName() == null) {
throw new IllegalStateException("Username or password missing");
}
authStore.addAuthentication(new BasicAuthentication(uri, Authentication.ANY_REALM, config.getUserName(),
config.getPassword()));
}
} else if (findAuthentication != null) {
authStore.removeAuthentication(findAuthentication);
}
public Request updateAuthenticationInformation(final Request request) {
return useAuthentication ? request.header(HttpHeader.AUTHORIZATION, authValue) : request;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,14 @@
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.util.StringContentProvider;
Expand All @@ -44,6 +43,7 @@
import org.openhab.binding.homematic.internal.model.TclScript;
import org.openhab.binding.homematic.internal.model.TclScriptDataList;
import org.openhab.binding.homematic.internal.model.TclScriptList;
import org.openhab.core.i18n.ConfigurationException;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.slf4j.Logger;
Expand All @@ -63,16 +63,13 @@ public class CcuGateway extends AbstractHomematicGateway {
private Map<String, String> tclregaScripts;
private XStream xStream = new XStream(new StaxDriver());

private @NonNull AuthenticationHandler authenticationHandler;

protected CcuGateway(String id, HomematicConfig config, HomematicGatewayAdapter gatewayAdapter,
HttpClient httpClient) throws IOException {
HttpClient httpClient) throws IOException, ConfigurationException {
super(id, config, gatewayAdapter, httpClient);

try {
new AuthenticationHandler(config, httpClient)
.updateAuthenticationInformation(new URI(config.getTclRegaUrl()));
} catch (URISyntaxException e) {
throw new IOException(e);
}
this.authenticationHandler = new AuthenticationHandler(config);

xStream.allowTypesByWildcard(new String[] { HmDevice.class.getPackageName() + ".**" });
xStream.setClassLoader(CcuGateway.class.getClassLoader());
Expand Down Expand Up @@ -221,9 +218,9 @@ private synchronized <T> T sendScript(String script, Class<T> clazz) throws IOEx
}

StringContentProvider content = new StringContentProvider(script, config.getEncoding());
ContentResponse response = httpClient.POST(config.getTclRegaUrl()).content(content)
.timeout(config.getTimeout(), TimeUnit.SECONDS)
.header(HttpHeader.CONTENT_TYPE, "text/plain;charset=" + config.getEncoding()).send();
ContentResponse response = authenticationHandler.updateAuthenticationInformation(httpClient
.POST(config.getTclRegaUrl()).content(content).timeout(config.getTimeout(), TimeUnit.SECONDS)
.header(HttpHeader.CONTENT_TYPE, "text/plain;charset=" + config.getEncoding())).send();

String result = new String(response.getContent(), config.getEncoding());
int lastPos = result.lastIndexOf("<xml><exec>");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.openhab.binding.homematic.internal.common.HomematicConfig;
import org.openhab.binding.homematic.internal.communicator.client.RpcClient;
import org.openhab.binding.homematic.internal.communicator.client.XmlRpcClient;
import org.openhab.core.i18n.ConfigurationException;

/**
* Factory which evaluates the type of the Homematic gateway and instantiates the appropriate class.
Expand All @@ -30,7 +31,7 @@ public class HomematicGatewayFactory {
* Creates the HomematicGateway.
*/
public static HomematicGateway createGateway(String id, HomematicConfig config,
HomematicGatewayAdapter gatewayAdapter, HttpClient httpClient) throws IOException {
HomematicGatewayAdapter gatewayAdapter, HttpClient httpClient) throws IOException, ConfigurationException {
loadGatewayInfo(config, id, httpClient);
if (config.getGatewayInfo().isCCU()) {
return new CcuGateway(id, config, gatewayAdapter, httpClient);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,20 @@

import javax.xml.parsers.ParserConfigurationException;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.util.BytesContentProvider;
import org.eclipse.jetty.client.util.FutureResponseListener;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus;
import org.openhab.binding.homematic.internal.common.AuthenticationHandler;
import org.openhab.binding.homematic.internal.common.HomematicConfig;
import org.openhab.binding.homematic.internal.communicator.message.RpcRequest;
import org.openhab.binding.homematic.internal.communicator.message.XmlRpcRequest;
import org.openhab.binding.homematic.internal.communicator.message.XmlRpcResponse;
import org.openhab.binding.homematic.internal.communicator.parser.RpcResponseParser;
import org.openhab.core.i18n.ConfigurationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;
Expand All @@ -47,12 +48,11 @@
public class XmlRpcClient extends RpcClient<String> {
private final Logger logger = LoggerFactory.getLogger(XmlRpcClient.class);
private HttpClient httpClient;
private @NonNull AuthenticationHandler authenticationHandler;
private AuthenticationHandler authenticationHandler;

public XmlRpcClient(HomematicConfig config, HttpClient httpClient) throws IOException {
public XmlRpcClient(HomematicConfig config, HttpClient httpClient) throws IOException, ConfigurationException {
super(config);
this.httpClient = httpClient;
this.authenticationHandler = new AuthenticationHandler(config, httpClient);
}

@Override
Expand Down Expand Up @@ -109,16 +109,22 @@ private byte[] send(int port, RpcRequest<String> request) throws IOException {
if (port == config.getGroupPort()) {
url += "/groups";
}
final URI uri = new URI(url);

authenticationHandler.updateAuthenticationInformation(uri);
if (authenticationHandler == null) {
authenticationHandler = new AuthenticationHandler(config);
}

Request req = httpClient.POST(uri).content(content).timeout(config.getTimeout(), TimeUnit.SECONDS)
.header(HttpHeader.CONTENT_TYPE, "text/xml;charset=" + config.getEncoding());
Request req = authenticationHandler.updateAuthenticationInformation(
httpClient.POST(new URI(url)).content(content).timeout(config.getTimeout(), TimeUnit.SECONDS)
.header(HttpHeader.CONTENT_TYPE, "text/xml;charset=" + config.getEncoding()));

FutureResponseListener listener = new FutureResponseListener(req, config.getBufferSize() * 1024);
req.send(listener);
ContentResponse response = listener.get(config.getTimeout(), TimeUnit.SECONDS);

if (response.getStatus() == HttpStatus.UNAUTHORIZED_401) {
throw new IOException("Access to Homematic gateway unauthorized");
}

ret = response.getContent();
if (ret == null || ret.length == 0) {
throw new IOException("Received no data from the Homematic gateway");
Expand All @@ -128,7 +134,7 @@ private byte[] send(int port, RpcRequest<String> request) throws IOException {
logger.trace("Client XmlRpcResponse (port {}):\n{}", port, result);
}
} catch (InterruptedException | ExecutionException | TimeoutException | IllegalArgumentException
| URISyntaxException e) {
| URISyntaxException | ConfigurationException e) {
throw new IOException(e);
}
return ret;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.openhab.binding.homematic.internal.model.HmGatewayInfo;
import org.openhab.binding.homematic.internal.type.HomematicTypeGenerator;
import org.openhab.binding.homematic.internal.type.UidUtils;
import org.openhab.core.i18n.ConfigurationException;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Channel;
Expand Down Expand Up @@ -132,6 +133,9 @@ private void initializeInternal() {
ex.getMessage(), ex);
disposeInternal();
scheduleReinitialize();
} catch (ConfigurationException ex) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, ex.getMessage());
disposeInternal();
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,3 @@ thing-type.config.homematic.bridge.xmlCallbackPort.description = Callback port o

channel-type.homematic.DUTY_CYCLE_RATIO.label = Duty Cycle
channel-type.homematic.DUTY_CYCLE_RATIO.description = Current duty cycle usage

# thing types config

thing-type.config.homematic.bridge.username.label = Username for accessing the gateway
thing-type.config.homematic.bridge.username.description = Username for accessing the gateway. Is required if the "useAuthentication" is true

0 comments on commit b3ef54d

Please sign in to comment.