Fix SAT (#15428)
Signed-off-by: lsiepel <leosiepel@gmail.com> Signed-off-by: Leo Siepel <leosiepel@gmail.com>
This commit is contained in:
parent
619dd617aa
commit
7f56f0579a
|
@ -15,6 +15,7 @@ package org.openhab.binding.folderwatcher.internal.api;
|
|||
import static org.eclipse.jetty.http.HttpHeader.*;
|
||||
import static org.eclipse.jetty.http.HttpMethod.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
@ -23,10 +24,13 @@ import java.util.ArrayList;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
|
@ -34,10 +38,14 @@ import org.eclipse.jetty.client.api.ContentResponse;
|
|||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.openhab.binding.folderwatcher.internal.api.auth.AWS4SignerBase;
|
||||
import org.openhab.binding.folderwatcher.internal.api.auth.AWS4SignerForAuthorizationHeader;
|
||||
import org.openhab.binding.folderwatcher.internal.api.exception.APIException;
|
||||
import org.openhab.binding.folderwatcher.internal.api.exception.AuthException;
|
||||
import org.openhab.binding.folderwatcher.internal.api.util.HttpUtilException;
|
||||
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/**
|
||||
* The {@link S3Actions} class contains AWS S3 API implementation.
|
||||
|
@ -54,39 +62,44 @@ public class S3Actions {
|
|||
private String awsAccessKey;
|
||||
private String awsSecretKey;
|
||||
|
||||
public S3Actions(HttpClientFactory httpClientFactory, String bucketName, String region) {
|
||||
public S3Actions(HttpClientFactory httpClientFactory, String bucketName, String region) throws APIException {
|
||||
this(httpClientFactory, bucketName, region, "", "");
|
||||
}
|
||||
|
||||
public S3Actions(HttpClientFactory httpClientFactory, String bucketName, String region, String awsAccessKey,
|
||||
String awsSecretKey) {
|
||||
String awsSecretKey) throws APIException {
|
||||
this.httpClient = httpClientFactory.getCommonHttpClient();
|
||||
try {
|
||||
this.bucketUri = new URL("http://" + bucketName + ".s3." + region + ".amazonaws.com");
|
||||
} catch (MalformedURLException e) {
|
||||
throw new RuntimeException("Unable to parse service endpoint: " + e.getMessage());
|
||||
throw new APIException("Unable to parse service endpoint: " + e.getMessage());
|
||||
}
|
||||
this.region = region;
|
||||
this.awsAccessKey = awsAccessKey;
|
||||
this.awsSecretKey = awsSecretKey;
|
||||
}
|
||||
|
||||
public List<String> listBucket(String prefix) throws Exception {
|
||||
public List<String> listBucket(String prefix) throws APIException, AuthException {
|
||||
Map<String, String> headers = new HashMap<String, String>();
|
||||
Map<String, String> params = new HashMap<String, String>();
|
||||
return listObjectsV2(prefix, headers, params);
|
||||
}
|
||||
|
||||
private List<String> listObjectsV2(String prefix, Map<String, String> headers, Map<String, String> params)
|
||||
throws Exception {
|
||||
throws APIException, AuthException {
|
||||
params.put("list-type", "2");
|
||||
params.put("prefix", prefix);
|
||||
if (!awsAccessKey.isEmpty() || !awsSecretKey.isEmpty()) {
|
||||
headers.put("x-amz-content-sha256", AWS4SignerBase.EMPTY_BODY_SHA256);
|
||||
AWS4SignerForAuthorizationHeader signer = new AWS4SignerForAuthorizationHeader(this.bucketUri, "GET", "s3",
|
||||
region);
|
||||
String authorization = signer.computeSignature(headers, params, AWS4SignerBase.EMPTY_BODY_SHA256,
|
||||
awsAccessKey, awsSecretKey);
|
||||
String authorization;
|
||||
try {
|
||||
authorization = signer.computeSignature(headers, params, AWS4SignerBase.EMPTY_BODY_SHA256, awsAccessKey,
|
||||
awsSecretKey);
|
||||
} catch (HttpUtilException e) {
|
||||
throw new AuthException(e);
|
||||
}
|
||||
headers.put("Authorization", authorization);
|
||||
}
|
||||
|
||||
|
@ -102,15 +115,31 @@ public class S3Actions {
|
|||
request.param(paramKey, params.get(paramKey));
|
||||
}
|
||||
|
||||
ContentResponse contentResponse = request.send();
|
||||
ContentResponse contentResponse;
|
||||
try {
|
||||
contentResponse = request.send();
|
||||
} catch (InterruptedException | TimeoutException | ExecutionException e) {
|
||||
throw new APIException(e);
|
||||
}
|
||||
|
||||
if (contentResponse.getStatus() != 200) {
|
||||
throw new Exception("HTTP Response is not 200");
|
||||
throw new APIException("HTTP Response is not 200");
|
||||
}
|
||||
|
||||
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
|
||||
DocumentBuilder docBuilder;
|
||||
try {
|
||||
docBuilder = docBuilderFactory.newDocumentBuilder();
|
||||
} catch (ParserConfigurationException e) {
|
||||
throw new APIException(e);
|
||||
}
|
||||
InputSource is = new InputSource(new StringReader(contentResponse.getContentAsString()));
|
||||
Document doc = docBuilder.parse(is);
|
||||
Document doc;
|
||||
try {
|
||||
doc = docBuilder.parse(is);
|
||||
} catch (SAXException | IOException e) {
|
||||
throw new APIException(e);
|
||||
}
|
||||
NodeList nameNodesList = doc.getElementsByTagName("Key");
|
||||
List<String> returnList = new ArrayList<>();
|
||||
|
||||
|
|
|
@ -28,7 +28,9 @@ import javax.crypto.Mac;
|
|||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.folderwatcher.internal.api.exception.AuthException;
|
||||
import org.openhab.binding.folderwatcher.internal.api.util.BinaryUtils;
|
||||
import org.openhab.binding.folderwatcher.internal.api.util.HttpUtilException;
|
||||
import org.openhab.binding.folderwatcher.internal.api.util.HttpUtils;
|
||||
|
||||
/**
|
||||
|
@ -46,8 +48,8 @@ public abstract class AWS4SignerBase {
|
|||
public static final String SCHEME = "AWS4";
|
||||
public static final String ALGORITHM = "HMAC-SHA256";
|
||||
public static final String TERMINATOR = "aws4_request";
|
||||
public static final String ISO8601BasicFormat = "yyyyMMdd'T'HHmmss'Z'";
|
||||
public static final String DateStringFormat = "yyyyMMdd";
|
||||
public static final String ISO8601_BASIC_FORMAT = "yyyyMMdd'T'HHmmss'Z'";
|
||||
public static final String DATESTRING_FORMAT = "yyyyMMdd";
|
||||
protected URL endpointUrl;
|
||||
protected String httpMethod;
|
||||
protected String serviceName;
|
||||
|
@ -61,9 +63,9 @@ public abstract class AWS4SignerBase {
|
|||
this.serviceName = serviceName;
|
||||
this.regionName = regionName;
|
||||
|
||||
dateTimeFormat = new SimpleDateFormat(ISO8601BasicFormat);
|
||||
dateTimeFormat = new SimpleDateFormat(ISO8601_BASIC_FORMAT);
|
||||
dateTimeFormat.setTimeZone(new SimpleTimeZone(0, "UTC"));
|
||||
dateStampFormat = new SimpleDateFormat(DateStringFormat);
|
||||
dateStampFormat = new SimpleDateFormat(DATESTRING_FORMAT);
|
||||
dateStampFormat.setTimeZone(new SimpleTimeZone(0, "UTC"));
|
||||
}
|
||||
|
||||
|
@ -100,12 +102,12 @@ public abstract class AWS4SignerBase {
|
|||
}
|
||||
|
||||
protected static String getCanonicalRequest(URL endpoint, String httpMethod, String queryParameters,
|
||||
String canonicalizedHeaderNames, String canonicalizedHeaders, String bodyHash) {
|
||||
String canonicalizedHeaderNames, String canonicalizedHeaders, String bodyHash) throws HttpUtilException {
|
||||
return httpMethod + "\n" + getCanonicalizedResourcePath(endpoint) + "\n" + queryParameters + "\n"
|
||||
+ canonicalizedHeaders + "\n" + canonicalizedHeaderNames + "\n" + bodyHash;
|
||||
}
|
||||
|
||||
protected static String getCanonicalizedResourcePath(URL endpoint) {
|
||||
protected static String getCanonicalizedResourcePath(URL endpoint) throws HttpUtilException {
|
||||
if (endpoint == null) {
|
||||
return "/";
|
||||
}
|
||||
|
@ -122,7 +124,7 @@ public abstract class AWS4SignerBase {
|
|||
}
|
||||
}
|
||||
|
||||
public static String getCanonicalizedQueryString(Map<String, String> parameters) {
|
||||
public static String getCanonicalizedQueryString(Map<String, String> parameters) throws HttpUtilException {
|
||||
if (parameters == null || parameters.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
@ -152,39 +154,39 @@ public abstract class AWS4SignerBase {
|
|||
}
|
||||
|
||||
protected static String getStringToSign(String scheme, String algorithm, String dateTime, String scope,
|
||||
String canonicalRequest) {
|
||||
String canonicalRequest) throws AuthException {
|
||||
return scheme + "-" + algorithm + "\n" + dateTime + "\n" + scope + "\n"
|
||||
+ BinaryUtils.toHex(hash(canonicalRequest));
|
||||
}
|
||||
|
||||
public static byte[] hash(String text) {
|
||||
public static byte[] hash(String text) throws AuthException {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
md.update(text.getBytes("UTF-8"));
|
||||
return md.digest();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Unable to compute hash while signing request: " + e.getMessage(), e);
|
||||
throw new AuthException("Unable to compute hash while signing request: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] hash(byte[] data) {
|
||||
public static byte[] hash(byte[] data) throws AuthException {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
md.update(data);
|
||||
return md.digest();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Unable to compute hash while signing request: " + e.getMessage(), e);
|
||||
throw new AuthException("Unable to compute hash while signing request: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
protected static byte[] sign(String stringData, byte[] key, String algorithm) {
|
||||
protected static byte[] sign(String stringData, byte[] key, String algorithm) throws AuthException {
|
||||
try {
|
||||
byte[] data = stringData.getBytes("UTF-8");
|
||||
Mac mac = Mac.getInstance(algorithm);
|
||||
mac.init(new SecretKeySpec(key, algorithm));
|
||||
return mac.doFinal(data);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Unable to calculate a request signature: " + e.getMessage(), e);
|
||||
throw new AuthException("Unable to calculate a request signature: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,9 @@ import java.util.Date;
|
|||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.folderwatcher.internal.api.exception.AuthException;
|
||||
import org.openhab.binding.folderwatcher.internal.api.util.BinaryUtils;
|
||||
import org.openhab.binding.folderwatcher.internal.api.util.HttpUtilException;
|
||||
|
||||
/**
|
||||
* The {@link AWS4SignerForAuthorizationHeader} class contains methods for AWS S3 API authentication using HTTP(S)
|
||||
|
@ -35,7 +37,7 @@ public class AWS4SignerForAuthorizationHeader extends AWS4SignerBase {
|
|||
}
|
||||
|
||||
public String computeSignature(Map<String, String> headers, Map<String, String> queryParameters, String bodyHash,
|
||||
String awsAccessKey, String awsSecretKey) {
|
||||
String awsAccessKey, String awsSecretKey) throws AuthException, HttpUtilException {
|
||||
Date now = new Date();
|
||||
String dateTimeStamp = dateTimeFormat.format(now);
|
||||
headers.put("x-amz-date", dateTimeStamp);
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* 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.folderwatcher.internal.api.exception;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link APIException} signal's there was a problem with interacting with the API
|
||||
*
|
||||
* @author Leo Siepel - initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class APIException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public APIException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public APIException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public APIException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* 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.folderwatcher.internal.api.exception;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link AuthException} signal's there was a problem with authentication
|
||||
*
|
||||
* @author Leo Siepel - initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AuthException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public AuthException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public AuthException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public AuthException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* 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.folderwatcher.internal.api.util;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link HttpUtilException} signal;s there was a problem with contacting the API
|
||||
*
|
||||
* @author Leo Siepel - initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class HttpUtilException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public HttpUtilException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public HttpUtilException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public HttpUtilException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
|
@ -26,12 +26,12 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
|||
*/
|
||||
@NonNullByDefault
|
||||
public class HttpUtils {
|
||||
public static String urlEncode(String url, boolean keepPathSlash) {
|
||||
public static String urlEncode(String url, boolean keepPathSlash) throws HttpUtilException {
|
||||
String encoded;
|
||||
try {
|
||||
encoded = URLEncoder.encode(url, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException("UTF-8 encoding is not supported.", e);
|
||||
throw new HttpUtilException("UTF-8 encoding is not supported.", e);
|
||||
}
|
||||
if (keepPathSlash) {
|
||||
encoded = encoded.replace("%2F", "/");
|
||||
|
|
|
@ -24,6 +24,7 @@ import java.util.concurrent.TimeUnit;
|
|||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.folderwatcher.internal.api.S3Actions;
|
||||
import org.openhab.binding.folderwatcher.internal.api.exception.APIException;
|
||||
import org.openhab.binding.folderwatcher.internal.common.WatcherCommon;
|
||||
import org.openhab.binding.folderwatcher.internal.config.S3BucketWatcherConfiguration;
|
||||
import org.openhab.core.OpenHAB;
|
||||
|
@ -71,12 +72,16 @@ public class S3BucketWatcherHandler extends BaseThingHandler {
|
|||
@Override
|
||||
public void initialize() {
|
||||
config = getConfigAs(S3BucketWatcherConfiguration.class);
|
||||
|
||||
if (config.s3Anonymous) {
|
||||
s3 = new S3Actions(httpClientFactory, config.s3BucketName, config.awsRegion);
|
||||
} else {
|
||||
s3 = new S3Actions(httpClientFactory, config.s3BucketName, config.awsRegion, config.awsKey,
|
||||
config.awsSecret);
|
||||
try {
|
||||
if (config.s3Anonymous) {
|
||||
s3 = new S3Actions(httpClientFactory, config.s3BucketName, config.awsRegion);
|
||||
} else {
|
||||
s3 = new S3Actions(httpClientFactory, config.s3BucketName, config.awsRegion, config.awsKey,
|
||||
config.awsSecret);
|
||||
}
|
||||
} catch (APIException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR, e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
Loading…
Reference in New Issue