From 72702e4cbd6ee81b53b2c9173944f3fcf46503c2 Mon Sep 17 00:00:00 2001 From: "Jan N. Klug" Date: Mon, 14 Aug 2023 12:19:55 +0200 Subject: [PATCH] [http] Fix authentication and request issues (#517) Authentications need to be removed when the thing is reconfigured, state content requests need to use the proper content type Signed-off-by: Jan N. Klug --- .../http/internal/HttpThingHandler.java | 40 ++++++++++++------- .../internal/http/RateLimitedHttpClient.java | 2 +- .../internal/http/RefreshingUrlCache.java | 7 +++- .../binding/http/RefreshingUrlCacheTest.java | 2 +- 4 files changed, 32 insertions(+), 19 deletions(-) diff --git a/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/HttpThingHandler.java b/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/HttpThingHandler.java index 085795ae6e..d1869be3df 100644 --- a/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/HttpThingHandler.java +++ b/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/HttpThingHandler.java @@ -23,6 +23,7 @@ import java.util.Base64; import java.util.Date; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -180,10 +181,23 @@ public void initialize() { config.headers.removeIf(String::isBlank); // configure authentication - if (!config.username.isEmpty() || !config.password.isEmpty()) { - try { - AuthenticationStore authStore = rateLimitedHttpClient.getAuthenticationStore(); - URI uri = new URI(config.baseURL); + try { + AuthenticationStore authStore = rateLimitedHttpClient.getAuthenticationStore(); + URI uri = new URI(config.baseURL); + + // clear old auths if available + Authentication.Result authResult = authStore.findAuthenticationResult(uri); + if (authResult != null) { + authStore.removeAuthenticationResult(authResult); + } + for (String authType : List.of("Basic", "Digest")) { + Authentication authentication = authStore.findAuthentication(authType, uri, Authentication.ANY_REALM); + if (authentication != null) { + authStore.removeAuthentication(authentication); + } + } + + if (!config.username.isEmpty() || !config.password.isEmpty()) { switch (config.authMode) { case BASIC_PREEMPTIVE: config.headers.add("Authorization=Basic " + Base64.getEncoder() @@ -213,14 +227,12 @@ public void initialize() { logger.warn("Unknown authentication method '{}' for thing '{}'", config.authMode, thing.getUID()); } - } catch (URISyntaxException e) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "failed to create authentication: baseUrl is invalid"); + } else { + logger.debug("No authentication configured for thing '{}'", thing.getUID()); } - } else { - logger.debug("No authentication configured for thing '{}'", thing.getUID()); + } catch (URISyntaxException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Cannot create URI from baseUrl."); } - // create channels thing.getChannels().forEach(this::createChannel); @@ -317,11 +329,9 @@ private void createChannel(Channel channel) { // we need a key consisting of stateContent and URL, only if both are equal, we can use the same cache String key = channelConfig.stateContent + "$" + stateUrl; channelUrls.put(channelUID, key); - Objects.requireNonNull( - urlHandlers - .computeIfAbsent(key, - k -> new RefreshingUrlCache(scheduler, rateLimitedHttpClient, stateUrl, config, - channelConfig.stateContent, this))) + Objects.requireNonNull(urlHandlers.computeIfAbsent(key, + k -> new RefreshingUrlCache(scheduler, rateLimitedHttpClient, stateUrl, config, + channelConfig.stateContent, config.contentType, this))) .addConsumer(itemValueConverter::process); } diff --git a/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/http/RateLimitedHttpClient.java b/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/http/RateLimitedHttpClient.java index d6da8a7655..cde895d2b0 100644 --- a/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/http/RateLimitedHttpClient.java +++ b/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/http/RateLimitedHttpClient.java @@ -206,7 +206,7 @@ public RequestQueueEntry(URI finalUrl, HttpMethod method, String content, @Nulla */ public void completeFuture(HttpClient httpClient) { Request request = httpClient.newRequest(finalUrl).method(method); - if (method != HttpMethod.GET && !content.isEmpty()) { + if ((method == HttpMethod.POST || method == HttpMethod.PUT) && !content.isEmpty()) { if (contentType == null) { request.content(new StringContentProvider(content)); } else { diff --git a/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/http/RefreshingUrlCache.java b/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/http/RefreshingUrlCache.java index 5a8aecf7fb..90df5514a2 100644 --- a/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/http/RefreshingUrlCache.java +++ b/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/http/RefreshingUrlCache.java @@ -57,13 +57,15 @@ public class RefreshingUrlCache { private final Map headers; private final HttpMethod httpMethod; private final String httpContent; + private final @Nullable String httpContentType; private final HttpStatusListener httpStatusListener; private final ScheduledFuture future; private @Nullable ContentWrapper lastContent; public RefreshingUrlCache(ScheduledExecutorService executor, RateLimitedHttpClient httpClient, String url, - HttpThingConfig thingConfig, String httpContent, HttpStatusListener httpStatusListener) { + HttpThingConfig thingConfig, String httpContent, @Nullable String httpContentType, + HttpStatusListener httpStatusListener) { this.httpClient = httpClient; this.url = url; this.strictErrorHandling = thingConfig.strictErrorHandling; @@ -72,6 +74,7 @@ public RefreshingUrlCache(ScheduledExecutorService executor, RateLimitedHttpClie this.httpMethod = thingConfig.stateMethod; this.headers = thingConfig.getHeaders(); this.httpContent = httpContent; + this.httpContentType = httpContentType; this.httpStatusListener = httpStatusListener; fallbackEncoding = thingConfig.encoding; @@ -94,7 +97,7 @@ private void refresh(boolean isRetry) { URI uri = Util.uriFromString(String.format(this.url, new Date())); logger.trace("Requesting refresh (retry={}) from '{}' with timeout {}ms", isRetry, uri, timeout); - httpClient.newRequest(uri, httpMethod, httpContent, null).thenAccept(request -> { + httpClient.newRequest(uri, httpMethod, httpContent, httpContentType).thenAccept(request -> { request.timeout(timeout, TimeUnit.MILLISECONDS); headers.forEach(request::header); diff --git a/bundles/org.smarthomej.binding.http/src/test/java/org/smarthomej/binding/http/RefreshingUrlCacheTest.java b/bundles/org.smarthomej.binding.http/src/test/java/org/smarthomej/binding/http/RefreshingUrlCacheTest.java index a92e87736a..7e188b19ca 100644 --- a/bundles/org.smarthomej.binding.http/src/test/java/org/smarthomej/binding/http/RefreshingUrlCacheTest.java +++ b/bundles/org.smarthomej.binding.http/src/test/java/org/smarthomej/binding/http/RefreshingUrlCacheTest.java @@ -253,7 +253,7 @@ public void testDateIsFormattedInURL() { */ private RefreshingUrlCache getUrlCache(String content) { RefreshingUrlCache urlCache = new RefreshingUrlCache(scheduler, rateLimitedHttpClient, url, thingConfig, - content, statusListener); + content, null, statusListener); urlCache.addConsumer(contentWrappers::add); return urlCache;