diff --git a/bundles/org.openhab.binding.http/README.md b/bundles/org.openhab.binding.http/README.md index a8c540f699c..333c4041e46 100644 --- a/bundles/org.openhab.binding.http/README.md +++ b/bundles/org.openhab.binding.http/README.md @@ -18,14 +18,19 @@ It can be extended with different channels. | `delay` | no | 0 | Delay between two requests in ms (advanced parameter). | | `username` | yes | - | Username for authentication (advanced parameter). | | `password` | yes | - | Password for authentication (advanced parameter). | -| `authMode` | no | BASIC | Authentication mode, `BASIC` or `DIGEST` (advanced parameter). | +| `authMode` | no | BASIC | Authentication mode, `BASIC`, `BASIC_PREEMPTIVE` or `DIGEST` (advanced parameter). | | `commandMethod` | no | GET | Method used for sending commands `GET`, `PUT`, `POST`. | | `contentType` | yes | - | MIME content-type of the command requests. Only used for `PUT` and `POST`. | | `encoding` | yes | - | Encoding to be used if no encoding is found in responses (advanced parameter). | | `headers` | yes | - | Additional headers that are sent along with the request. Format is "header=value".| | `ignoreSSLErrors` | no | false | If set to true ignores invalid SSL certificate errors. This is potentially dangerous.| -*Note:* optional "no" means that you have to configure a value unless a default is provided and you are ok with that setting. +*Note:* Optional "no" means that you have to configure a value unless a default is provided and you are ok with that setting. + +*Note:* The `BASIC_PREEMPTIVE` mode adds basic authentication headers even if the server did not request authentication. +This is dangerous and might be misused. +The option exists to be able to authenticate when the server is not sending the proper 401/Unauthorized code. +Authentication might fail if redirections are involved as headers are stripper prior to redirection. *Note:* If you rate-limit requests by using the `delay` parameter you have to make sure that the time between two refreshes is larger than the time needed for one refresh cycle. diff --git a/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpThingHandler.java b/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpThingHandler.java index 343145a92e4..f7bce257259 100644 --- a/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpThingHandler.java +++ b/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpThingHandler.java @@ -14,10 +14,7 @@ package org.openhab.binding.http.internal; import java.net.URI; import java.net.URISyntaxException; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.function.Function; @@ -85,7 +82,6 @@ public class HttpThingHandler extends BaseThingHandler { private final Map urlHandlers = new HashMap<>(); private final Map channels = new HashMap<>(); private final Map channelUrls = new HashMap<>(); - private @Nullable Authentication authentication; public HttpThingHandler(Thing thing, HttpClientProvider httpClientProvider, ValueTransformationProvider valueTransformationProvider, @@ -139,6 +135,7 @@ public class HttpThingHandler extends BaseThingHandler { return; } + // check SSL handling and initialize client if (config.ignoreSSLErrors) { logger.info("Using the insecure client for thing '{}'.", thing.getUID()); httpClient = httpClientProvider.getInsecureClient(); @@ -158,29 +155,34 @@ public class HttpThingHandler extends BaseThingHandler { channelCount, thing.getUID(), config.delay, config.refresh); } - authentication = null; + // remove empty headers + config.headers.removeIf(String::isBlank); + + // configure authentication if (!config.username.isEmpty()) { try { + AuthenticationStore authStore = httpClient.getAuthenticationStore(); URI uri = new URI(config.baseURL); switch (config.authMode) { + case BASIC_PREEMPTIVE: + config.headers.add("Authorization=Basic " + Base64.getEncoder() + .encodeToString((config.username + ":" + config.password).getBytes())); + logger.debug("Preemptive Basic Authentication configured for thing '{}'", thing.getUID()); + break; case BASIC: - authentication = new BasicAuthentication(uri, Authentication.ANY_REALM, config.username, - config.password); + authStore.addAuthentication(new BasicAuthentication(uri, Authentication.ANY_REALM, + config.username, config.password)); logger.debug("Basic Authentication configured for thing '{}'", thing.getUID()); break; case DIGEST: - authentication = new DigestAuthentication(uri, Authentication.ANY_REALM, config.username, - config.password); + authStore.addAuthentication(new DigestAuthentication(uri, Authentication.ANY_REALM, + config.username, config.password)); logger.debug("Digest Authentication configured for thing '{}'", thing.getUID()); break; default: logger.warn("Unknown authentication method '{}' for thing '{}'", config.authMode, thing.getUID()); } - if (authentication != null) { - AuthenticationStore authStore = httpClient.getAuthenticationStore(); - authStore.addAuthentication(authentication); - } } catch (URISyntaxException e) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "failed to create authentication: baseUrl is invalid"); @@ -189,6 +191,7 @@ public class HttpThingHandler extends BaseThingHandler { logger.debug("No authentication configured for thing '{}'", thing.getUID()); } + // create channels thing.getChannels().forEach(this::createChannel); updateStatus(ThingStatus.ONLINE); diff --git a/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/config/HttpAuthMode.java b/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/config/HttpAuthMode.java index c78c21ff57d..29aa65f3490 100644 --- a/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/config/HttpAuthMode.java +++ b/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/config/HttpAuthMode.java @@ -21,6 +21,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; */ @NonNullByDefault public enum HttpAuthMode { + BASIC_PREEMPTIVE, BASIC, DIGEST } diff --git a/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/config/HttpThingConfig.java b/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/config/HttpThingConfig.java index 258f94466a9..d6df2431643 100644 --- a/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/config/HttpThingConfig.java +++ b/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/config/HttpThingConfig.java @@ -12,8 +12,7 @@ */ package org.openhab.binding.http.internal.config; -import java.util.Collections; -import java.util.List; +import java.util.ArrayList; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -43,5 +42,6 @@ public class HttpThingConfig { public boolean ignoreSSLErrors = false; - public List headers = Collections.emptyList(); + // ArrayList is required as implementation because list may be modified later + public ArrayList headers = new ArrayList<>(); } diff --git a/bundles/org.openhab.binding.http/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.http/src/main/resources/OH-INF/thing/thing-types.xml index f298a53c5df..61e14edb83b 100644 --- a/bundles/org.openhab.binding.http/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.http/src/main/resources/OH-INF/thing/thing-types.xml @@ -52,6 +52,7 @@ + BASIC