From 8dab6e2c080f40075dedaa5485468a93113524b7 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Thu, 12 Aug 2021 23:04:46 +0100 Subject: [PATCH] Attachment: add support for authenticated downloads of attachments (#12) --- README.md | 8 ++++ pom.xml | 2 +- .../java/net/kemitix/trello/ApiKeyPair.java | 16 ++++++++ .../net/kemitix/trello/ApiKeyPairImpl.java | 16 ++++++++ .../java/net/kemitix/trello/Attachment.java | 9 +++++ .../net/kemitix/trello/LocalAttachment.java | 5 +++ .../net/kemitix/trello/TrelloAttachment.java | 40 ++++++++++++++++--- 7 files changed, 90 insertions(+), 6 deletions(-) create mode 100644 src/main/java/net/kemitix/trello/ApiKeyPair.java create mode 100644 src/main/java/net/kemitix/trello/ApiKeyPairImpl.java diff --git a/README.md b/README.md index ff945c0..592d619 100644 --- a/README.md +++ b/README.md @@ -19,3 +19,11 @@ This wraps the `com.taskadapter:trello-java-wrapper` library. It is intended for use on my `slushy` and `fuller` projects. It was originally developed as part of `slushy` and was extracted when preparing to start on `fuller` which would need some of the same functionality. Requires JDK 11+ + +## Downloading Attachments + +From 1.1.0 of `kemitix-trello` support is included for downloading attachments +using the now required authorised methods. In order to do so the API key and +token are needed to download the attachment. These are provided either when +creating the new `TrelloAttachment` constructor, or by calling the method +`withApiKeyPair(...)`, which will return a new instance. diff --git a/pom.xml b/pom.xml index e358980..03c762b 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ kemitix-trello - 1.0.3 + 1.1.0 2.18 diff --git a/src/main/java/net/kemitix/trello/ApiKeyPair.java b/src/main/java/net/kemitix/trello/ApiKeyPair.java new file mode 100644 index 0000000..27597fe --- /dev/null +++ b/src/main/java/net/kemitix/trello/ApiKeyPair.java @@ -0,0 +1,16 @@ +package net.kemitix.trello; + +public interface ApiKeyPair { + + String getKey(); + String getToken(); + + static ApiKeyPair create(String key, String token) { + return new ApiKeyPairImpl(key, token); + } + + static ApiKeyPair none() { + return ApiKeyPairImpl.NONE; + } + +} diff --git a/src/main/java/net/kemitix/trello/ApiKeyPairImpl.java b/src/main/java/net/kemitix/trello/ApiKeyPairImpl.java new file mode 100644 index 0000000..af1e2ed --- /dev/null +++ b/src/main/java/net/kemitix/trello/ApiKeyPairImpl.java @@ -0,0 +1,16 @@ +package net.kemitix.trello; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor(access = AccessLevel.PROTECTED) +public class ApiKeyPairImpl implements ApiKeyPair { + + public static final ApiKeyPair NONE = new ApiKeyPairImpl("", ""); + + private final String key; + private final String token; + +} diff --git a/src/main/java/net/kemitix/trello/Attachment.java b/src/main/java/net/kemitix/trello/Attachment.java index 469c3f1..787055d 100644 --- a/src/main/java/net/kemitix/trello/Attachment.java +++ b/src/main/java/net/kemitix/trello/Attachment.java @@ -25,4 +25,13 @@ public interface Attachment { */ File getOriginalFilename(); + /** + * Adds the API Key Pair to a {@link TrelloAttachment}, creating a new + * instance, but ignored otherwise. + * + * @param apiKeyPair the key pair to add + * @return a new TrelloAttachment instance or itself + */ + Attachment withApiKeyPair(final ApiKeyPair apiKeyPair); + } diff --git a/src/main/java/net/kemitix/trello/LocalAttachment.java b/src/main/java/net/kemitix/trello/LocalAttachment.java index 73b9259..5a3677c 100644 --- a/src/main/java/net/kemitix/trello/LocalAttachment.java +++ b/src/main/java/net/kemitix/trello/LocalAttachment.java @@ -35,4 +35,9 @@ public class LocalAttachment public LocalAttachment download() { return this; } + + @Override + public Attachment withApiKeyPair(ApiKeyPair apiKeyPair) { + return this; + } } diff --git a/src/main/java/net/kemitix/trello/TrelloAttachment.java b/src/main/java/net/kemitix/trello/TrelloAttachment.java index 96dab20..ee95d16 100644 --- a/src/main/java/net/kemitix/trello/TrelloAttachment.java +++ b/src/main/java/net/kemitix/trello/TrelloAttachment.java @@ -5,12 +5,13 @@ import com.julienvey.trello.domain.Card; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.net.Authenticator; +import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; -import java.net.URLEncoder; +import java.net.URLConnection; import java.nio.channels.Channels; -import java.nio.charset.StandardCharsets; import java.text.Normalizer; import java.util.Optional; import java.util.logging.Logger; @@ -25,16 +26,19 @@ public class TrelloAttachment implements Attachment { private final Card card; private final AttachmentDirectory attachmentDirectory; private final String id; + private final ApiKeyPair apiKeyPair; private TrelloAttachment( com.julienvey.trello.domain.Attachment attachment, Card card, - AttachmentDirectory attachmentDirectory + AttachmentDirectory attachmentDirectory, + ApiKeyPair apiKeyPair ) { this.attachment = attachment; this.card = card; this.attachmentDirectory = attachmentDirectory; this.id = card.getIdShort(); + this.apiKeyPair = apiKeyPair; } public static Attachment create( @@ -42,7 +46,16 @@ public class TrelloAttachment implements Attachment { Card card, AttachmentDirectory dir ) { - return new TrelloAttachment(attachment, card, dir); + return new TrelloAttachment(attachment, card, dir, ApiKeyPair.none()); + } + + public static Attachment create( + com.julienvey.trello.domain.Attachment attachment, + Card card, + AttachmentDirectory dir, + ApiKeyPair apiKeyPair + ) { + return new TrelloAttachment(attachment, card, dir, apiKeyPair); } @Override @@ -67,7 +80,11 @@ public class TrelloAttachment implements Attachment { @Override public LocalAttachment download() { - try (var source = Channels.newChannel(getUrl().openStream());){ + if (apiKeyPair == ApiKeyPair.none()) { + throw new UnsupportedOperationException( + "Download not permitted without a valid API KeyPair"); + } + try (var source = Channels.newChannel(getConnection().getInputStream());) { File filename = new File(attachment.getName()); LOG.info("Downloading from " + filename); var file = attachmentDirectory.createFile(getFilename()); @@ -82,11 +99,24 @@ public class TrelloAttachment implements Attachment { } } + private URLConnection getConnection() throws IOException { + var connection = (HttpURLConnection) getUrl().openConnection(); + connection.setRequestProperty("Authorization", String.format( + "OAuth oauth_consumer_key=\"%s\", oauth_token=\"%s\"", + apiKeyPair.getKey(), apiKeyPair.getToken())); + return connection; + } + @Override public File getOriginalFilename() { return getFilename(); } + @Override + public Attachment withApiKeyPair(ApiKeyPair apiKeyPair) { + return create(attachment, card, attachmentDirectory, apiKeyPair); + } + private URL getUrl() throws MalformedURLException { return URI.create(attachment.getUrl()).toURL(); }