mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[homematic] Add Authentication (#16196)
* Add Authentication --------- Signed-off-by: Christian Kittel <ckittel@gmx.de> Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
This commit is contained in:
parent
94be9fdca7
commit
f1bb5d9936
@ -14,15 +14,28 @@ When the option "Restricted access" is used, some ports have to be added to the
|
||||
2000;
|
||||
2001;
|
||||
2010;
|
||||
8181;
|
||||
8701;
|
||||
9292;
|
||||
```
|
||||
|
||||
Also the IP of the device running openHAB has to be set to the list of "IP addresses for restricted access".
|
||||
|
||||
Also under `Home page > Settings > Control panel` with the menu `Security` the option `Authentication` has to be disabled as the binding does not support the configuration of `username` and `password`for the XML-RPC API.
|
||||
Also under `Home page > Settings > Control panel` with the menu `Security` the option `Authentication` has to be disabled if the option 'useAuthentication' is not set.
|
||||
This option may be enabled if the option 'useAuthentication' is set and BIN-RPC is not used.
|
||||
In this case, a user and password must be created.
|
||||
This can be done under `Home page > Settings > Control panel` with the menu `User management`.
|
||||
This can be done under `Home page > Settings > Control Panel` in the `User Management` menu.
|
||||
The new user should have the following configuration:
|
||||
|
||||
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:
|
||||
- User name - button for login: No
|
||||
- Permission level: User
|
||||
- Expert mode not visible: Yes
|
||||
- Automatically confirm the device message: Yes
|
||||
|
||||
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 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
|
||||
@ -179,6 +192,15 @@ Due to the factory reset, the device will also be unpaired from the gateway, eve
|
||||
If a large number of devices are connected to the gateway, the default buffersize of 2048 kB may be too small for communication with the gateway.
|
||||
In this case, e.g. the discovery fails.
|
||||
With this setting the buffer size can be adjusted. The value is specified in kB.
|
||||
|
||||
- **useAuthentication**
|
||||
Username and password are send to the gateway to authenticate the access to the gateway.
|
||||
|
||||
- **username**
|
||||
Username for Authentication to the gateway.
|
||||
|
||||
- **password**
|
||||
Password for Authentication to the gateway.
|
||||
|
||||
The syntax for a bridge is:
|
||||
|
||||
|
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.homematic.internal.common;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
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.
|
||||
*
|
||||
* @author Christian Kittel
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AuthenticationHandler {
|
||||
|
||||
private Boolean useAuthentication;
|
||||
private @Nullable String authValue;
|
||||
|
||||
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 credentials th the request if needed.
|
||||
*/
|
||||
public Request updateAuthenticationInformation(final Request request) {
|
||||
return useAuthentication ? request.header(HttpHeader.AUTHORIZATION, authValue) : request;
|
||||
}
|
||||
}
|
@ -60,6 +60,10 @@ public class HomematicConfig {
|
||||
private HmGatewayInfo gatewayInfo;
|
||||
private int callbackRegTimeout;
|
||||
|
||||
private boolean useAuthentication;
|
||||
private String userName;
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* Returns the Homematic gateway address.
|
||||
*/
|
||||
@ -272,6 +276,48 @@ public class HomematicConfig {
|
||||
return getRpcPort(channel.getDevice().getHmInterface());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if authorization for the gateway has to be used; otherwise false
|
||||
*/
|
||||
public boolean getUseAuthentication() {
|
||||
return useAuthentication;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if authorization for the gateway has to be used
|
||||
*/
|
||||
public void setUseAuthentication(Boolean useAuthentication) {
|
||||
this.useAuthentication = useAuthentication;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user name for authorize against the gateway
|
||||
*/
|
||||
public String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user name for authorize against the gateway
|
||||
*/
|
||||
public void setUserName(String userName) {
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the password for authorize against the gateway
|
||||
*/
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the password for authorize against the gateway
|
||||
*/
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Homematic gateway port of the interfaces.
|
||||
*/
|
||||
|
@ -12,7 +12,7 @@
|
||||
*/
|
||||
package org.openhab.binding.homematic.internal.communicator;
|
||||
|
||||
import static org.openhab.binding.homematic.internal.HomematicBindingConstants.*;
|
||||
import static org.openhab.binding.homematic.internal.HomematicBindingConstants.GATEWAY_POOL_NAME;
|
||||
import static org.openhab.binding.homematic.internal.misc.HomematicConstants.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -22,10 +22,12 @@ 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;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.openhab.binding.homematic.internal.common.AuthenticationHandler;
|
||||
import org.openhab.binding.homematic.internal.common.HomematicConfig;
|
||||
import org.openhab.binding.homematic.internal.communicator.client.UnknownParameterSetException;
|
||||
import org.openhab.binding.homematic.internal.communicator.client.UnknownRpcFailureException;
|
||||
@ -41,6 +43,7 @@ import org.openhab.binding.homematic.internal.model.HmResult;
|
||||
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;
|
||||
@ -60,10 +63,14 @@ 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) {
|
||||
HttpClient httpClient) throws IOException, ConfigurationException {
|
||||
super(id, config, gatewayAdapter, httpClient);
|
||||
|
||||
this.authenticationHandler = new AuthenticationHandler(config);
|
||||
|
||||
xStream.allowTypesByWildcard(new String[] { HmDevice.class.getPackageName() + ".**" });
|
||||
xStream.setClassLoader(CcuGateway.class.getClassLoader());
|
||||
xStream.autodetectAnnotations(true);
|
||||
@ -211,9 +218,9 @@ public class CcuGateway extends AbstractHomematicGateway {
|
||||
}
|
||||
|
||||
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>");
|
||||
|
@ -18,6 +18,7 @@ import org.eclipse.jetty.client.HttpClient;
|
||||
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.
|
||||
@ -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);
|
||||
|
@ -14,6 +14,8 @@ package org.openhab.binding.homematic.internal.communicator.client;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
@ -26,11 +28,14 @@ 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;
|
||||
@ -43,8 +48,9 @@ import org.xml.sax.SAXException;
|
||||
public class XmlRpcClient extends RpcClient<String> {
|
||||
private final Logger logger = LoggerFactory.getLogger(XmlRpcClient.class);
|
||||
private HttpClient httpClient;
|
||||
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;
|
||||
}
|
||||
@ -103,11 +109,22 @@ public class XmlRpcClient extends RpcClient<String> {
|
||||
if (port == config.getGroupPort()) {
|
||||
url += "/groups";
|
||||
}
|
||||
Request req = httpClient.POST(url).content(content).timeout(config.getTimeout(), TimeUnit.SECONDS)
|
||||
.header(HttpHeader.CONTENT_TYPE, "text/xml;charset=" + config.getEncoding());
|
||||
if (authenticationHandler == null) {
|
||||
authenticationHandler = new AuthenticationHandler(config);
|
||||
}
|
||||
|
||||
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");
|
||||
@ -116,7 +133,8 @@ public class XmlRpcClient extends RpcClient<String> {
|
||||
String result = new String(ret, config.getEncoding());
|
||||
logger.trace("Client XmlRpcResponse (port {}):\n{}", port, result);
|
||||
}
|
||||
} catch (InterruptedException | ExecutionException | TimeoutException | IllegalArgumentException e) {
|
||||
} catch (InterruptedException | ExecutionException | TimeoutException | IllegalArgumentException
|
||||
| URISyntaxException | ConfigurationException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
return ret;
|
||||
|
@ -36,6 +36,7 @@ import org.openhab.binding.homematic.internal.model.HmDevice;
|
||||
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;
|
||||
@ -132,6 +133,9 @@ public class HomematicBridgeHandler extends BaseBridgeHandler implements Homemat
|
||||
ex.getMessage(), ex);
|
||||
disposeInternal();
|
||||
scheduleReinitialize();
|
||||
} catch (ConfigurationException ex) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, ex.getMessage());
|
||||
disposeInternal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,8 @@ thing-type.config.homematic.bridge.hmIpPort.label = HMIP Port
|
||||
thing-type.config.homematic.bridge.hmIpPort.description = The port number of the Homematic IP daemon
|
||||
thing-type.config.homematic.bridge.installModeDuration.label = Install Mode Duration
|
||||
thing-type.config.homematic.bridge.installModeDuration.description = Time in seconds that the controller will be in install mode when a device discovery is initiated
|
||||
thing-type.config.homematic.bridge.password.label = Password
|
||||
thing-type.config.homematic.bridge.password.description = Password for accessing the gateway if authenticaton is required.
|
||||
thing-type.config.homematic.bridge.rfPort.label = RF Port
|
||||
thing-type.config.homematic.bridge.rfPort.description = The port number of the RF daemon
|
||||
thing-type.config.homematic.bridge.socketMaxAlive.label = Socket MaxAlive
|
||||
@ -45,6 +47,10 @@ thing-type.config.homematic.bridge.timeout.label = Timeout
|
||||
thing-type.config.homematic.bridge.timeout.description = The timeout in seconds for connections to a Homematic gateway
|
||||
thing-type.config.homematic.bridge.unpairOnDeletion.label = Unpair Devices On Deletion
|
||||
thing-type.config.homematic.bridge.unpairOnDeletion.description = If set to true, devices are unpaired from the gateway when their corresponding things are removed. The option "factoryResetOnDeletion" also unpairs a device, so in order to avoid unpairing on deletion, both options need to be set to false!
|
||||
thing-type.config.homematic.bridge.useAuthentication.label = Use authentication
|
||||
thing-type.config.homematic.bridge.useAuthentication.description = Use authentication to access the gateway. If set to true, username and password is required.
|
||||
thing-type.config.homematic.bridge.userName.label = User name
|
||||
thing-type.config.homematic.bridge.userName.description = User name for accessing the gateway if authenticaton is required.
|
||||
thing-type.config.homematic.bridge.wiredPort.label = Wired Port
|
||||
thing-type.config.homematic.bridge.wiredPort.description = The port number of the HS485 daemon
|
||||
thing-type.config.homematic.bridge.xmlCallbackPort.label = XML-RPC Callback Port
|
||||
|
@ -127,6 +127,23 @@
|
||||
<default>2048</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="useAuthentication" type="boolean">
|
||||
<label>Use authentication</label>
|
||||
<description>Use authentication to access the gateway. If set to true, username and password is required.</description>
|
||||
<advanced>true</advanced>
|
||||
<default>false</default>
|
||||
</parameter>
|
||||
<parameter name="userName" type="text">
|
||||
<label>User name</label>
|
||||
<description>User name for accessing the gateway if authenticaton is required.</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="password" type="text">
|
||||
<label>Password</label>
|
||||
<description>Password for accessing the gateway if authenticaton is required.</description>
|
||||
<advanced>true</advanced>
|
||||
<context>password</context>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</bridge-type>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user