diff --git a/NOTICE b/NOTICE
index 53e7cfef..4fa4feb8 100644
--- a/NOTICE
+++ b/NOTICE
@@ -12,13 +12,12 @@ This software includes third party software subject to the following licenses:
Apache Commons Lang under Apache License, Version 2.0
Apache HttpClient under Apache License, Version 2.0
Apache HttpCore under Apache License, Version 2.0
- Extended StAX API under Eclipse Distribution License - v 1.0
- fastinfoset under Apache License, Version 2.0 or Eclipse Distribution License - v 1.0
istack common utility code runtime under Eclipse Distribution License - v 1.0
+ Jakarta Activation under EDL 1.0
+ Jakarta Activation API jar under EDL 1.0
Jakarta Annotations API under EPL 2.0 or GPL2 w/ CPE
+ Jakarta XML Binding API under Eclipse Distribution License - v 1.0
jakarta.ws.rs-api under EPL 2.0 or GPL2 w/ CPE
- jakarta.xml.bind-api under Eclipse Distribution License - v 1.0
- JavaBeans Activation Framework API jar under EDL 1.0
javax.inject:1 as OSGi bundle under EPL 2.0 or GPL2 w/ CPE
JAXB Runtime under Eclipse Distribution License - v 1.0
JAXB2 Basics - Runtime under BSD-Style License
diff --git a/pom.xml b/pom.xml
index 5a16c8ca..a78e4f4a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -17,8 +17,8 @@
1.8
1.8
set_with_-Dproject.previousVersion=X.Y
- 2.7
- 1.7.30
+ 2.8
+ 1.7.31
@@ -26,21 +26,21 @@
org.springframework
spring-framework-bom
- 5.3.3
+ 5.3.8
pom
import
org.glassfish.jersey
jersey-bom
- 2.33
+ 2.34
pom
import
org.junit
junit-bom
- 5.7.0
+ 5.8.0-M1
pom
import
@@ -88,7 +88,7 @@
org.glassfish.jaxb
jaxb-runtime
- 2.3.2
+ 2.3.4
runtime
@@ -106,7 +106,7 @@
commons-io
commons-io
- 2.8.0
+ 2.10.0
test
@@ -125,12 +125,12 @@
org.springframework.ws
spring-xml
- 3.0.10.RELEASE
+ 3.1.1
org.apache.commons
commons-lang3
- 3.11
+ 3.12.0
@@ -170,7 +170,7 @@
nl.jqno.equalsverifier
equalsverifier
- 3.5.2
+ 3.6.1
test
@@ -194,7 +194,7 @@
no.digipost
digg
- 0.25
+ 0.30
test
@@ -227,12 +227,12 @@
org.glassfish.jaxb
jaxb-runtime
- 2.3.2
+ 2.3.4
jakarta.xml.bind
jakarta.xml.bind-api
- 2.3.2
+ 2.3.3
@@ -243,7 +243,7 @@
com.github.siom79.japicmp
japicmp-maven-plugin
- 0.15.2
+ 0.15.3
@@ -271,7 +271,7 @@
maven-surefire-plugin
- 3.0.0-M4
+ 3.0.0-M5
maven-deploy-plugin
@@ -283,7 +283,7 @@
maven-dependency-plugin
- 3.1.1
+ 3.1.2
maven-install-plugin
@@ -291,15 +291,15 @@
maven-resources-plugin
- 3.1.0
+ 3.2.0
maven-site-plugin
- 3.8.2
+ 3.9.1
maven-javadoc-plugin
- 3.1.1
+ 3.2.0
maven-jar-plugin
@@ -331,7 +331,7 @@
org.codehaus.mojo
versions-maven-plugin
- 2.7
+ 2.8.1
diff --git a/src/main/java/no/digipost/signature/client/asice/ASiCEAttachable.java b/src/main/java/no/digipost/signature/client/asice/ASiCEAttachable.java
index bbd80ac3..844ca482 100644
--- a/src/main/java/no/digipost/signature/client/asice/ASiCEAttachable.java
+++ b/src/main/java/no/digipost/signature/client/asice/ASiCEAttachable.java
@@ -5,15 +5,19 @@
import static org.apache.commons.codec.digest.DigestUtils.sha256;
public interface ASiCEAttachable extends SignableFileReference {
+
+ public static final String XML_MEDIATYPE = "application/xml";
+
@Override
String getFileName();
- byte[] getBytes();
+ byte[] getContent();
- String getMimeType();
+ @Override
+ String getMediaType();
@Override
default byte[] getSha256() {
- return sha256(getBytes());
+ return sha256(getContent());
}
}
diff --git a/src/main/java/no/digipost/signature/client/asice/CreateASiCE.java b/src/main/java/no/digipost/signature/client/asice/CreateASiCE.java
index 17d6daaa..c4c6683d 100644
--- a/src/main/java/no/digipost/signature/client/asice/CreateASiCE.java
+++ b/src/main/java/no/digipost/signature/client/asice/CreateASiCE.java
@@ -43,8 +43,7 @@ public DocumentBundle createASiCE(JOB job) {
Manifest manifest = manifestCreator.createManifest(job, sender);
- List files = new ArrayList<>();
- files.add(job.getDocument());
+ List files = new ArrayList<>(job.getDocuments());
files.add(manifest);
Signature signature = createSignature.createSignature(files, keyStoreConfig);
diff --git a/src/main/java/no/digipost/signature/client/asice/archive/CreateZip.java b/src/main/java/no/digipost/signature/client/asice/archive/CreateZip.java
index e7a10795..0ad6a2be 100644
--- a/src/main/java/no/digipost/signature/client/asice/archive/CreateZip.java
+++ b/src/main/java/no/digipost/signature/client/asice/archive/CreateZip.java
@@ -16,9 +16,9 @@ public byte[] zipIt(final List files) {
try (ZipOutputStream zipOutputStream = new ZipOutputStream(archive)) {
for (ASiCEAttachable file : files) {
ZipEntry zipEntry = new ZipEntry(file.getFileName());
- zipEntry.setSize(file.getBytes().length);
+ zipEntry.setSize(file.getContent().length);
zipOutputStream.putNextEntry(zipEntry);
- zipOutputStream.write(file.getBytes());
+ zipOutputStream.write(file.getContent());
zipOutputStream.closeEntry();
}
}
diff --git a/src/main/java/no/digipost/signature/client/asice/manifest/CreateDirectManifest.java b/src/main/java/no/digipost/signature/client/asice/manifest/CreateDirectManifest.java
index dc703d4c..75b022f2 100644
--- a/src/main/java/no/digipost/signature/client/asice/manifest/CreateDirectManifest.java
+++ b/src/main/java/no/digipost/signature/client/asice/manifest/CreateDirectManifest.java
@@ -3,25 +3,25 @@
import no.digipost.signature.api.xml.XMLDirectDocument;
import no.digipost.signature.api.xml.XMLDirectSignatureJobManifest;
import no.digipost.signature.api.xml.XMLDirectSigner;
+import no.digipost.signature.api.xml.XMLHref;
import no.digipost.signature.api.xml.XMLSender;
import no.digipost.signature.client.core.AuthenticationLevel;
import no.digipost.signature.client.core.IdentifierInSignedDocuments;
import no.digipost.signature.client.core.OnBehalfOf;
import no.digipost.signature.client.core.Sender;
import no.digipost.signature.client.core.SignatureType;
-import no.digipost.signature.client.direct.DirectDocument;
import no.digipost.signature.client.direct.DirectJob;
import no.digipost.signature.client.direct.DirectSigner;
import java.util.ArrayList;
import java.util.List;
+import static java.util.stream.Collectors.toList;
+
public class CreateDirectManifest extends ManifestCreator {
@Override
Object buildXmlManifest(DirectJob job, Sender sender) {
- DirectDocument document = job.getDocument();
-
List signers = new ArrayList<>();
for (DirectSigner signer : job.getSigners()) {
XMLDirectSigner xmlSigner = new XMLDirectSigner()
@@ -39,13 +39,14 @@ Object buildXmlManifest(DirectJob job, Sender sender) {
.withSigners(signers)
.withRequiredAuthentication(job.getRequiredAuthentication().map(AuthenticationLevel::getXmlEnumValue).orElse(null))
.withSender(new XMLSender().withOrganizationNumber(sender.getOrganizationNumber()))
- .withDocument(new XMLDirectDocument()
- .withTitle(document.getTitle())
- .withDescription(document.getMessage())
- .withHref(document.getFileName())
- .withMime(document.getMimeType())
- )
- .withIdentifierInSignedDocuments(job.getIdentifierInSignedDocuments().map(IdentifierInSignedDocuments::getXmlEnumValue).orElse(null))
- ;
+ .withTitle(job.getTitle())
+ .withDescription(job.getDescription().orElse(null))
+ .withDocuments(job.getDocuments().stream()
+ .map(document -> new XMLDirectDocument()
+ .withTitle(document.getTitle())
+ .withHref(XMLHref.of(document.getFileName()))
+ .withMediaType(document.getMediaType()))
+ .collect(toList()))
+ .withIdentifierInSignedDocuments(job.getIdentifierInSignedDocuments().map(IdentifierInSignedDocuments::getXmlEnumValue).orElse(null));
}
}
diff --git a/src/main/java/no/digipost/signature/client/asice/manifest/CreatePortalManifest.java b/src/main/java/no/digipost/signature/client/asice/manifest/CreatePortalManifest.java
index 5bcb9cca..574fde06 100644
--- a/src/main/java/no/digipost/signature/client/asice/manifest/CreatePortalManifest.java
+++ b/src/main/java/no/digipost/signature/client/asice/manifest/CreatePortalManifest.java
@@ -3,6 +3,7 @@
import no.digipost.signature.api.xml.XMLAvailability;
import no.digipost.signature.api.xml.XMLEmail;
import no.digipost.signature.api.xml.XMLEnabled;
+import no.digipost.signature.api.xml.XMLHref;
import no.digipost.signature.api.xml.XMLNotifications;
import no.digipost.signature.api.xml.XMLNotificationsUsingLookup;
import no.digipost.signature.api.xml.XMLPortalDocument;
@@ -17,15 +18,16 @@
import no.digipost.signature.client.core.SignatureType;
import no.digipost.signature.client.portal.Notifications;
import no.digipost.signature.client.portal.NotificationsUsingLookup;
-import no.digipost.signature.client.portal.PortalDocument;
import no.digipost.signature.client.portal.PortalJob;
import no.digipost.signature.client.portal.PortalSigner;
import java.time.Clock;
+import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
+import static java.util.stream.Collectors.toList;
import static no.digipost.signature.client.core.exceptions.SignerNotSpecifiedException.SIGNER_NOT_SPECIFIED;
public class CreatePortalManifest extends ManifestCreator {
@@ -41,7 +43,6 @@ Object buildXmlManifest(PortalJob job, Sender sender) {
List xmlSigners = new ArrayList<>();
for (PortalSigner signer : job.getSigners()) {
XMLPortalSigner xmlPortalSigner = generateSigner(signer);
-
if (signer.getNotifications() != null) {
xmlPortalSigner.setNotifications(generateNotifications(signer.getNotifications()));
} else if (signer.getNotificationsUsingLookup() != null) {
@@ -50,27 +51,25 @@ Object buildXmlManifest(PortalJob job, Sender sender) {
xmlSigners.add(xmlPortalSigner);
}
- PortalDocument document = job.getDocument();
-
ZonedDateTime activationTime = job.getActivationTime().map(activation -> activation.atZone(clock.getZone())).orElse(null);
return new XMLPortalSignatureJobManifest()
.withSigners(xmlSigners)
.withRequiredAuthentication(job.getRequiredAuthentication().map(AuthenticationLevel::getXmlEnumValue).orElse(null))
.withSender(new XMLSender().withOrganizationNumber(sender.getOrganizationNumber()))
- .withDocument(new XMLPortalDocument()
- .withTitle(document.getTitle())
- .withNonsensitiveTitle(document.getNonsensitiveTitle())
- .withDescription(document.getMessage())
- .withHref(document.getFileName())
- .withMime(document.getMimeType())
- )
+ .withTitle(job.getTitle())
+ .withNonsensitiveTitle(job.getNonsensitiveTitle().orElse(null))
+ .withDescription(job.getDescription().orElse(null))
+ .withDocuments(job.getDocuments().stream()
+ .map(document -> new XMLPortalDocument()
+ .withTitle(document.getTitle())
+ .withHref(XMLHref.of(document.getFileName()))
+ .withMediaType(document.getMediaType()))
+ .collect(toList()))
.withAvailability(new XMLAvailability()
.withActivationTime(activationTime)
- .withAvailableSeconds(job.getAvailableSeconds())
- )
- .withIdentifierInSignedDocuments(job.getIdentifierInSignedDocuments().map(IdentifierInSignedDocuments::getXmlEnumValue).orElse(null))
- ;
+ .withAvailableSeconds(job.getAvailable().map(Duration::getSeconds).orElse(null)))
+ .withIdentifierInSignedDocuments(job.getIdentifierInSignedDocuments().map(IdentifierInSignedDocuments::getXmlEnumValue).orElse(null));
}
private XMLPortalSigner generateSigner(PortalSigner signer) {
diff --git a/src/main/java/no/digipost/signature/client/asice/manifest/Manifest.java b/src/main/java/no/digipost/signature/client/asice/manifest/Manifest.java
index 0a57c9d3..e79823e7 100644
--- a/src/main/java/no/digipost/signature/client/asice/manifest/Manifest.java
+++ b/src/main/java/no/digipost/signature/client/asice/manifest/Manifest.java
@@ -16,12 +16,13 @@ public String getFileName() {
}
@Override
- public byte[] getBytes() {
+ public byte[] getContent() {
return xmlBytes;
}
@Override
- public String getMimeType() {
- return "application/xml";
+ public String getMediaType() {
+ return XML_MEDIATYPE;
}
+
}
diff --git a/src/main/java/no/digipost/signature/client/asice/signature/CreateSignature.java b/src/main/java/no/digipost/signature/client/asice/signature/CreateSignature.java
index e236277d..f20c4fc9 100644
--- a/src/main/java/no/digipost/signature/client/asice/signature/CreateSignature.java
+++ b/src/main/java/no/digipost/signature/client/asice/signature/CreateSignature.java
@@ -169,8 +169,9 @@ private List references(final XMLSignatureFactory xmlSignatureFactory
for (int i = 0; i < files.size(); i++) {
try {
String signatureElementId = "ID_" + i;
- String uri = URLEncoder.encode(files.get(i).getFileName(), "UTF-8");
- Reference reference = xmlSignatureFactory.newReference(uri, sha256DigestMethod, null, null, signatureElementId, files.get(i).getSha256());
+ SignableFileReference file = files.get(i);
+ String uri = URLEncoder.encode(file.getFileName(), "UTF-8");
+ Reference reference = xmlSignatureFactory.newReference(uri, sha256DigestMethod, null, null, signatureElementId, file.getSha256());
result.add(reference);
} catch(UnsupportedEncodingException e) {
throw new RuntimeException(e);
diff --git a/src/main/java/no/digipost/signature/client/asice/signature/CreateXAdESArtifacts.java b/src/main/java/no/digipost/signature/client/asice/signature/CreateXAdESArtifacts.java
index 228c9fc8..2ea05a09 100644
--- a/src/main/java/no/digipost/signature/client/asice/signature/CreateXAdESArtifacts.java
+++ b/src/main/java/no/digipost/signature/client/asice/signature/CreateXAdESArtifacts.java
@@ -60,7 +60,7 @@ private List dataObjectFormats(List extends SignableFileRefe
List result = new ArrayList<>();
for (int i = 0; i < files.size(); i++) {
String signatureElementIdReference = format("#ID_%s", i);
- result.add(new DataObjectFormat(null, null, files.get(i).getMimeType(), null, signatureElementIdReference));
+ result.add(new DataObjectFormat(null, null, files.get(i).getMediaType(), null, signatureElementIdReference));
}
return result;
}
diff --git a/src/main/java/no/digipost/signature/client/asice/signature/SignableFileReference.java b/src/main/java/no/digipost/signature/client/asice/signature/SignableFileReference.java
index 3c720f8a..508a1584 100644
--- a/src/main/java/no/digipost/signature/client/asice/signature/SignableFileReference.java
+++ b/src/main/java/no/digipost/signature/client/asice/signature/SignableFileReference.java
@@ -6,6 +6,6 @@ public interface SignableFileReference {
byte[] getSha256();
- String getMimeType();
+ String getMediaType();
}
diff --git a/src/main/java/no/digipost/signature/client/asice/signature/Signature.java b/src/main/java/no/digipost/signature/client/asice/signature/Signature.java
index 34933f08..5335e154 100644
--- a/src/main/java/no/digipost/signature/client/asice/signature/Signature.java
+++ b/src/main/java/no/digipost/signature/client/asice/signature/Signature.java
@@ -16,13 +16,13 @@ public String getFileName() {
}
@Override
- public byte[] getBytes() {
+ public byte[] getContent() {
return xmlBytes;
}
@Override
- public String getMimeType() {
- return "application/xml";
+ public String getMediaType() {
+ return XML_MEDIATYPE;
}
}
diff --git a/src/main/java/no/digipost/signature/client/core/Document.java b/src/main/java/no/digipost/signature/client/core/Document.java
index 3bdaf02d..bb6884fe 100644
--- a/src/main/java/no/digipost/signature/client/core/Document.java
+++ b/src/main/java/no/digipost/signature/client/core/Document.java
@@ -2,20 +2,22 @@
import no.digipost.signature.client.asice.ASiCEAttachable;
-public abstract class Document implements ASiCEAttachable {
+public class Document implements ASiCEAttachable {
- private String title;
- private String message;
- private String fileName;
- private byte[] document;
- private FileType fileType;
+ private final String title;
+ private final String mediaType;
+ private final String fileName;
+ private final byte[] content;
- protected Document(final String title, final String message, final String fileName, final FileType fileType, final byte[] document) {
+ public Document(String title, String mediaType, String fileName, byte[] content) {
this.title = title;
- this.message = message;
+ this.mediaType = mediaType;
this.fileName = fileName;
- this.fileType = fileType;
- this.document = document;
+ this.content = content;
+ }
+
+ public String getTitle() {
+ return title;
}
@Override
@@ -24,31 +26,13 @@ public String getFileName() {
}
@Override
- public byte[] getBytes() {
- return document;
+ public String getMediaType() {
+ return mediaType;
}
@Override
- public String getMimeType() {
- return fileType.mimeType;
+ public byte[] getContent() {
+ return content;
}
- public String getTitle() {
- return title;
- }
-
- public String getMessage() {
- return message;
- }
-
- public enum FileType {
- PDF("application/pdf"),
- TXT("text/plain");
-
- public final String mimeType;
-
- FileType(final String mimeType) {
- this.mimeType = mimeType;
- }
- }
}
diff --git a/src/main/java/no/digipost/signature/client/core/DocumentType.java b/src/main/java/no/digipost/signature/client/core/DocumentType.java
new file mode 100644
index 00000000..17dd509e
--- /dev/null
+++ b/src/main/java/no/digipost/signature/client/core/DocumentType.java
@@ -0,0 +1,24 @@
+package no.digipost.signature.client.core;
+
+public enum DocumentType {
+
+ PDF("application/pdf", "pdf"),
+ TXT("text/plain", "txt");
+
+ private final String mediaType;
+ private final String fileExtension;
+
+ DocumentType(String mediaType, String fileExtension) {
+ this.mediaType = mediaType;
+ this.fileExtension = fileExtension;
+ }
+
+ public String getMediaType() {
+ return mediaType;
+ }
+
+ public String getFileExtension() {
+ return fileExtension;
+ }
+
+}
diff --git a/src/main/java/no/digipost/signature/client/core/SignatureJob.java b/src/main/java/no/digipost/signature/client/core/SignatureJob.java
index bd66e6cc..a9211f84 100644
--- a/src/main/java/no/digipost/signature/client/core/SignatureJob.java
+++ b/src/main/java/no/digipost/signature/client/core/SignatureJob.java
@@ -1,10 +1,11 @@
package no.digipost.signature.client.core;
+import java.util.List;
import java.util.Optional;
public interface SignatureJob {
- Document getDocument();
+ List getDocuments();
Optional getSender();
diff --git a/src/main/java/no/digipost/signature/client/core/internal/FileName.java b/src/main/java/no/digipost/signature/client/core/internal/FileName.java
new file mode 100644
index 00000000..110353f6
--- /dev/null
+++ b/src/main/java/no/digipost/signature/client/core/internal/FileName.java
@@ -0,0 +1,89 @@
+package no.digipost.signature.client.core.internal;
+
+import java.text.Normalizer;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.regex.Pattern;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import static java.text.Normalizer.Form.NFD;
+import static java.util.Arrays.binarySearch;
+import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.joining;
+import static java.util.stream.IntStream.rangeClosed;
+
+public final class FileName {
+
+ private static final Map knownReplacements; static {
+ knownReplacements = new LinkedHashMap<>();
+ knownReplacements.put(Pattern.compile("[\\s:;&%\\$\\*\\?\\+=@,\\(\\)\\[\\]\\{\\}#!\"“”\\^`´<>]+"), "_"); //various punctuation
+ knownReplacements.put(Pattern.compile("[æÆ]"), "ae");
+ knownReplacements.put(Pattern.compile("[øØ]"), "oe");
+ knownReplacements.put(Pattern.compile("[åÅ]"), "aa");
+ }
+
+ private static final char[] allowedChars = Stream.of(
+ rangeClosed('0', '9'),
+ rangeClosed('a', 'z'),
+ IntStream.of('-', '_', '.'))
+ .flatMapToInt(identity())
+ .sorted()
+ .mapToObj(c -> String.valueOf((char) c))
+ .collect(joining())
+ .toCharArray();
+
+
+ public static String reduceToFileNameSafeChars(String text) {
+ if (text == null || text.isEmpty()) {
+ throw new IllegalArgumentException(text);
+ }
+ String knownReplacements = text;
+ for (Entry replacement : FileName.knownReplacements.entrySet()) {
+ knownReplacements = replacement.getKey().matcher(knownReplacements).replaceAll(replacement.getValue());
+ }
+ String accentsRemoved = removeAccents(knownReplacements);
+ return reduceToAlphabet(accentsRemoved.toLowerCase(), allowedChars);
+ }
+
+
+ /**
+ * Reduce a text to only contain characters of a given alphabet
+ * using the following algorithm:
+ *
+ * - characters already part of the alphabet are kept as-is
+ * - characters not part of the alphabet is replaced with a
+ * character from the alphabet in a non-defined, but repeatable manner.
+ *
+ *
+ * @param text the source text
+ * @param alphabet the allowed characters in the resulting String
+ * @return the resulting string
+ */
+ private static String reduceToAlphabet(String text, char[] alphabet) {
+ StringBuilder reducedToAllowedChars = new StringBuilder(text.length());
+ for (int c : text.toCharArray()) {
+ if (binarySearch(allowedChars, (char) c) >= 0) {
+ reducedToAllowedChars.append((char) c);
+ } else {
+ reducedToAllowedChars.append(allowedChars[c % allowedChars.length]);
+ }
+ }
+ return reducedToAllowedChars.toString();
+ }
+
+
+ private static final Pattern UNICODE_ACCENT = Pattern.compile("\\p{M}");
+
+ /**
+ * Replaces accented characters (è, ü, etc) with their base glyphs (e, u, etc).
+ * @see https://stackoverflow.com/questions/3322152/is-there-a-way-to-get-rid-of-accents-and-convert-a-whole-string-to-regular-lette
+ */
+ private static String removeAccents(String text) {
+ return UNICODE_ACCENT.matcher(Normalizer.normalize(text, NFD)).replaceAll("");
+ }
+
+ private FileName() {
+ }
+}
diff --git a/src/main/java/no/digipost/signature/client/direct/DirectDocument.java b/src/main/java/no/digipost/signature/client/direct/DirectDocument.java
index 2595e012..6f03f01d 100644
--- a/src/main/java/no/digipost/signature/client/direct/DirectDocument.java
+++ b/src/main/java/no/digipost/signature/client/direct/DirectDocument.java
@@ -1,44 +1,47 @@
package no.digipost.signature.client.direct;
-import no.digipost.signature.client.core.Document;
+import no.digipost.signature.client.core.DocumentType;
-import static no.digipost.signature.client.core.Document.FileType.PDF;
+import static java.util.Objects.requireNonNull;
+import static no.digipost.signature.client.core.DocumentType.PDF;
-public class DirectDocument extends Document {
- private DirectDocument(String title, String message, String fileName, FileType fileType, byte[] document) {
- super(title, message, fileName, fileType, document);
+
+public class DirectDocument {
+
+ public static Builder builder(String title, byte[] documentContent) {
+ return new Builder(title, documentContent);
}
- public static Builder builder(final String title, final String fileName, final byte[] document) {
- return new Builder(title, fileName, document);
+
+ public final String title;
+ public final DocumentType type;
+ public final byte[] content;
+
+ private DirectDocument(String title, DocumentType type, byte[] content) {
+ this.title = title;
+ this.type = type;
+ this.content = content;
}
+
public static class Builder {
private String title;
- private String fileName;
- private byte[] document;
- private String message;
- private FileType fileType = PDF;
-
- public Builder(final String title, final String fileName, final byte[] document) {
- this.title = title;
- this.fileName = fileName;
- this.document = document;
- }
+ private DocumentType type = PDF;
+ private byte[] content;
- public Builder message(String message) {
- this.message = message;
- return this;
+ private Builder(String title, byte[] content) {
+ this.title = requireNonNull(title, "title");
+ this.content = requireNonNull(content, "document content");
}
- public Builder fileType(final FileType fileType) {
- this.fileType = fileType;
+ public Builder type(DocumentType type) {
+ this.type = requireNonNull(type, "document type");
return this;
}
public DirectDocument build() {
- return new DirectDocument(title, message, fileName, fileType, document);
+ return new DirectDocument(title, type, content);
}
}
}
diff --git a/src/main/java/no/digipost/signature/client/direct/DirectJob.java b/src/main/java/no/digipost/signature/client/direct/DirectJob.java
index 1b33a2ab..1299ef5c 100644
--- a/src/main/java/no/digipost/signature/client/direct/DirectJob.java
+++ b/src/main/java/no/digipost/signature/client/direct/DirectJob.java
@@ -1,6 +1,7 @@
package no.digipost.signature.client.direct;
import no.digipost.signature.client.core.AuthenticationLevel;
+import no.digipost.signature.client.core.Document;
import no.digipost.signature.client.core.IdentifierInSignedDocuments;
import no.digipost.signature.client.core.Sender;
import no.digipost.signature.client.core.SignatureJob;
@@ -8,29 +9,39 @@
import java.net.URI;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
+import static java.lang.String.format;
+import static java.util.Collections.singletonList;
import static java.util.Collections.unmodifiableList;
+import static no.digipost.signature.client.core.internal.FileName.reduceToFileNameSafeChars;
+
+/**
+ * Signature job with document(s) to be signed by
+ * one or more signers in direct flow.
+ */
public class DirectJob implements SignatureJob, WithExitUrls {
+ private final List documents;
+ private final List signers;
+ private final String title;
+ private final URI completionUrl;
+ private final URI rejectionUrl;
+ private final URI errorUrl;
+ private Optional description = Optional.empty();
private String reference;
- private List signers;
- private DirectDocument document;
- private URI completionUrl;
- private URI rejectionUrl;
- private URI errorUrl;
private Optional sender = Optional.empty();
private Optional statusRetrievalMethod = Optional.empty();
private Optional requiredAuthentication = Optional.empty();
private Optional identifierInSignedDocuments = Optional.empty();
- private DirectJob(List signers, DirectDocument document, URI completionUrl, URI rejectionUrl, URI errorUrl) {
+ private DirectJob(String title, List documents, List signers, URI completionUrl, URI rejectionUrl, URI errorUrl) {
+ this.title = title;
+ this.documents = unmodifiableList(new ArrayList<>(documents));
this.signers = unmodifiableList(new ArrayList<>(signers));
- this.document = document;
this.completionUrl = completionUrl;
this.rejectionUrl = rejectionUrl;
this.errorUrl = errorUrl;
@@ -42,8 +53,15 @@ public String getReference() {
}
@Override
- public DirectDocument getDocument() {
- return document;
+ public List getDocuments() {
+ List documents = new ArrayList<>();
+ for (int i = 0; i < this.documents.size(); i++) {
+ DirectDocument document = this.documents.get(i);
+ documents.add(new Document(document.title, document.type.getMediaType(),
+ format("%04d", i) + "_" + reduceToFileNameSafeChars(document.title) + "." + document.type.getFileExtension(),
+ document.content));
+ }
+ return documents;
}
@Override
@@ -84,38 +102,43 @@ public Optional getStatusRetrievalMethod() {
return statusRetrievalMethod;
}
+ public String getTitle() {
+ return title;
+ }
+
+ public Optional getDescription() {
+ return description;
+ }
+
/**
- * Create a new DirectJob.
+ * Create a new signature job for direct flow.
*
- * @param document The {@link DirectDocument} that should be signed.
- * @param hasExitUrls specifies the urls the user will be redirected back to upon completing/rejecting/failing
- * the signing ceremony. See {@link ExitUrls#of(URI, URI, URI)}, and alternatively
+ * @param document The {@link DirectDocument document} that should be signed.
+ * @param hasExitUrls specifies the URLs the user will be redirected back to upon completing/rejecting/failing
+ * to sign the document. See {@link ExitUrls#of(URI, URI, URI)}, and alternatively
* {@link ExitUrls#singleExitUrl(URI)}.
- * @param signers The {@link DirectSigner DirectSigners} of the document.
+ * @param signer The {@link DirectSigner signer} of the document.
*
* @return a builder to further customize the job
- * @see DirectJob#builder(DirectDocument, WithExitUrls, List)
*/
- public static Builder builder(DirectDocument document, WithExitUrls hasExitUrls, DirectSigner... signers) {
- return builder(document, hasExitUrls, Arrays.asList(signers));
+ public static Builder builder(String title, DirectDocument document, DirectSigner signer, WithExitUrls hasExitUrls) {
+ return builder(title, singletonList(document), singletonList(signer), hasExitUrls);
}
-
/**
- * Create a new DirectJob.
+ * Create a new signature job for direct flow.
*
- * @param document The {@link DirectDocument} that should be signed.
- * @param hasExitUrls specifies the urls the user will be redirected back to upon completing/rejecting/failing
- * the signing ceremony. See {@link ExitUrls#of(URI, URI, URI)}, and alternatively
+ * @param documents The {@link DirectDocument document} that should be signed.
+ * @param hasExitUrls specifies the URLs the user will be redirected back to upon completing/rejecting/failing
+ * to sign the documents. See {@link ExitUrls#of(URI, URI, URI)}, and alternatively
* {@link ExitUrls#singleExitUrl(URI)}.
- * @param signers The {@link DirectSigner DirectSigners} of the document.
+ * @param signers The {@link DirectSigner signers} of the document.
*
* @return a builder to further customize the job
- * @see DirectJob#builder(DirectDocument, WithExitUrls, DirectSigner...)
*/
- public static Builder builder(DirectDocument document, WithExitUrls hasExitUrls, List signers) {
- return new Builder(signers, document, hasExitUrls.getCompletionUrl(), hasExitUrls.getRejectionUrl(), hasExitUrls.getErrorUrl());
+ public static Builder builder(String title, List documents, List signers, WithExitUrls hasExitUrls) {
+ return new Builder(title, documents, signers, hasExitUrls.getCompletionUrl(), hasExitUrls.getRejectionUrl(), hasExitUrls.getErrorUrl());
}
public static class Builder implements JobCustomizations {
@@ -123,8 +146,8 @@ public static class Builder implements JobCustomizations {
private final DirectJob target;
private boolean built = false;
- private Builder(List signers, DirectDocument document, URI completionUrl, URI rejectionUrl, URI errorUrl) {
- target = new DirectJob(signers, document, completionUrl, rejectionUrl, errorUrl);
+ private Builder(String title, List documents, List signers, URI completionUrl, URI rejectionUrl, URI errorUrl) {
+ target = new DirectJob(title, documents, signers, completionUrl, rejectionUrl, errorUrl);
}
@Override
@@ -132,6 +155,11 @@ public Builder withReference(UUID uuid) {
return withReference(uuid.toString());
}
+ public Builder withDescription(String description) {
+ target.description = Optional.of(description);
+ return this;
+ }
+
@Override
public Builder withReference(String reference) {
target.reference = reference;
diff --git a/src/main/java/no/digipost/signature/client/portal/PortalDocument.java b/src/main/java/no/digipost/signature/client/portal/PortalDocument.java
index e7dfae3e..c15b3c6a 100644
--- a/src/main/java/no/digipost/signature/client/portal/PortalDocument.java
+++ b/src/main/java/no/digipost/signature/client/portal/PortalDocument.java
@@ -1,57 +1,47 @@
package no.digipost.signature.client.portal;
-import no.digipost.signature.client.core.Document;
+import no.digipost.signature.client.core.DocumentType;
-import static no.digipost.signature.client.core.Document.FileType.PDF;
+import static java.util.Objects.requireNonNull;
+import static no.digipost.signature.client.core.DocumentType.PDF;
-public class PortalDocument extends Document {
- private final String nonsensitiveTitle;
- private PortalDocument(String title, String nonsensitiveTitle, String message, String fileName, FileType fileType, byte[] document) {
- super(title, message, fileName, fileType, document);
- this.nonsensitiveTitle = nonsensitiveTitle;
- }
+public class PortalDocument {
- public String getNonsensitiveTitle() {
- return nonsensitiveTitle;
+ public static Builder builder(String title, byte[] documentContent) {
+ return new Builder(title, documentContent);
}
- public static Builder builder(final String title, final String fileName, final byte[] document) {
- return new Builder(title, fileName, document);
+
+ public final String title;
+ public final DocumentType type;
+ public final byte[] content;
+
+ private PortalDocument(String title, DocumentType type, byte[] content) {
+ this.title = title;
+ this.type = type;
+ this.content = content;
}
+
public static class Builder {
private String title;
- private String nonsensitiveTitle;
- private String fileName;
- private byte[] document;
- private String message;
- private FileType fileType = PDF;
-
- public Builder(final String title, final String fileName, final byte[] document) {
- this.title = title;
- this.fileName = fileName;
- this.document = document;
- }
+ private DocumentType type = PDF;
+ private byte[] content;
- public Builder message(String message) {
- this.message = message;
- return this;
- }
-
- public Builder nonsensitiveTitle(String nonsensitiveTitle) {
- this.nonsensitiveTitle = nonsensitiveTitle;
- return this;
+ public Builder(String title, byte[] content) {
+ this.title = requireNonNull(title, "title");
+ this.content = requireNonNull(content, "document content");
}
- public Builder fileType(final FileType fileType) {
- this.fileType = fileType;
+ public Builder type(DocumentType type) {
+ this.type = requireNonNull(type, "document type");
return this;
}
public PortalDocument build() {
- return new PortalDocument(title, nonsensitiveTitle, message, fileName, fileType, document);
+ return new PortalDocument(title, type, content);
}
}
}
diff --git a/src/main/java/no/digipost/signature/client/portal/PortalJob.java b/src/main/java/no/digipost/signature/client/portal/PortalJob.java
index 5fb4ef2f..7b0a6a73 100644
--- a/src/main/java/no/digipost/signature/client/portal/PortalJob.java
+++ b/src/main/java/no/digipost/signature/client/portal/PortalJob.java
@@ -1,36 +1,47 @@
package no.digipost.signature.client.portal;
import no.digipost.signature.client.core.AuthenticationLevel;
+import no.digipost.signature.client.core.Document;
import no.digipost.signature.client.core.IdentifierInSignedDocuments;
import no.digipost.signature.client.core.Sender;
import no.digipost.signature.client.core.SignatureJob;
import no.digipost.signature.client.core.internal.JobCustomizations;
+import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
-import java.util.concurrent.TimeUnit;
+import static java.lang.String.format;
+import static java.util.Collections.singletonList;
import static java.util.Collections.unmodifiableList;
+import static no.digipost.signature.client.core.internal.FileName.reduceToFileNameSafeChars;
+/**
+ * Signature job with document(s) to be signed by
+ * one or more signers in portal flow.
+ */
public class PortalJob implements SignatureJob {
+ private final List documents;
private final List signers;
- private final PortalDocument document;
+ private final String title;
+ private Optional nonsensitiveTitle = Optional.empty();
+ private Optional description = Optional.empty();
private String reference;
private Optional activationTime = Optional.empty();
- private Long availableSeconds;
+ private Optional available = Optional.empty();
private Optional sender = Optional.empty();
private Optional requiredAuthentication = Optional.empty();
private Optional identifierInSignedDocuments = Optional.empty();
- private PortalJob(List signers, PortalDocument document) {
+ private PortalJob(String title, List documents, List signers) {
+ this.title = title;
+ this.documents = unmodifiableList(new ArrayList<>(documents));
this.signers = unmodifiableList(new ArrayList<>(signers));
- this.document = document;
}
@Override
@@ -39,8 +50,15 @@ public String getReference() {
}
@Override
- public PortalDocument getDocument() {
- return document;
+ public List getDocuments() {
+ List documents = new ArrayList<>();
+ for (int i = 0; i < this.documents.size(); i++) {
+ PortalDocument document = this.documents.get(i);
+ documents.add(new Document(document.title, document.type.getMediaType(),
+ format("%04d", i) + "_" + reduceToFileNameSafeChars(document.title) + "." + document.type.getFileExtension(),
+ document.content));
+ }
+ return documents;
}
@Override
@@ -66,17 +84,45 @@ public Optional getActivationTime() {
return activationTime;
}
- public Long getAvailableSeconds() {
- return availableSeconds;
+ public Optional getAvailable() {
+ return available;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public Optional getNonsensitiveTitle() {
+ return nonsensitiveTitle;
+ }
+
+ public Optional getDescription() {
+ return description;
}
- public static Builder builder(PortalDocument document, PortalSigner... signers) {
- return builder(document, Arrays.asList(signers));
+ /**
+ * Create a new signature job for portal flow.
+ *
+ * @param document The {@link PortalDocument document} that should be signed.
+ * @param signer The {@link PortalSigner signer} of the document.
+ *
+ * @return a builder to further customize the job
+ */
+ public static Builder builder(String title, PortalDocument document, PortalSigner signer) {
+ return builder(title, singletonList(document), singletonList(signer));
}
- public static Builder builder(PortalDocument document, List signers) {
- return new Builder(signers, document);
+ /**
+ * Create a new signature job for portal flow.
+ *
+ * @param documents The {@link PortalDocument documents} that should be signed.
+ * @param signers The {@link PortalSigner signers} of the document.
+ *
+ * @return a builder to further customize the job
+ */
+ public static Builder builder(String title, List documents, List signers) {
+ return new Builder(title, documents, signers);
}
public static class Builder implements JobCustomizations {
@@ -84,8 +130,8 @@ public static class Builder implements JobCustomizations {
private final PortalJob target;
private boolean built = false;
- private Builder(List signers, PortalDocument document) {
- target = new PortalJob(signers, document);
+ private Builder(String title, List documents, List signers) {
+ target = new PortalJob(title, documents, signers);
}
@Override
@@ -122,8 +168,18 @@ public Builder withActivationTime(Instant activationTime) {
return this;
}
- public Builder availableFor(long duration, TimeUnit unit) {
- target.availableSeconds = unit.toSeconds(duration);
+ public Builder availableFor(Duration duration) {
+ target.available = Optional.of(duration);
+ return this;
+ }
+
+ public Builder withNonsensitiveTitle(String nonsensitiveTitle) {
+ target.nonsensitiveTitle = Optional.of(nonsensitiveTitle);
+ return this;
+ }
+
+ public Builder withDescription(String description) {
+ target.description = Optional.of(description);
return this;
}
diff --git a/src/test/java/no/digipost/signature/client/asice/CreateASiCETest.java b/src/test/java/no/digipost/signature/client/asice/CreateASiCETest.java
index 92dafb3c..05f6073e 100644
--- a/src/test/java/no/digipost/signature/client/asice/CreateASiCETest.java
+++ b/src/test/java/no/digipost/signature/client/asice/CreateASiCETest.java
@@ -5,6 +5,7 @@
import no.digipost.signature.client.asice.manifest.CreatePortalManifest;
import no.digipost.signature.client.asice.manifest.ManifestCreator;
import no.digipost.signature.client.core.Document;
+import no.digipost.signature.client.core.DocumentType;
import no.digipost.signature.client.core.Sender;
import no.digipost.signature.client.core.SignatureJob;
import no.digipost.signature.client.direct.DirectDocument;
@@ -26,21 +27,24 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Clock;
+import java.time.Duration;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
+import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import static java.nio.file.Files.newDirectoryStream;
-import static java.util.concurrent.TimeUnit.DAYS;
+import static java.util.Arrays.asList;
+import static java.util.stream.Stream.concat;
import static no.digipost.signature.client.TestKonfigurasjon.CLIENT_KEYSTORE;
import static no.digipost.signature.client.asice.DumpDocumentBundleToDisk.TIMESTAMP_PATTERN;
import static no.digipost.signature.client.asice.DumpDocumentBundleToDisk.referenceFilenamePart;
import static no.digipost.signature.client.direct.ExitUrls.singleExitUrl;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.containsInAnyOrder;
public class CreateASiCETest {
@@ -56,20 +60,21 @@ public static void initTempFolder() throws URISyntaxException, IOException {
private static Path dumpFolder;
- private static final DirectDocument DIRECT_DOCUMENT = DirectDocument.builder("Title", "file.txt", "hello".getBytes())
- .message("Message")
- .fileType(Document.FileType.TXT)
+ private static final DirectDocument DIRECT_DOCUMENT = DirectDocument.builder("Document title", "hello".getBytes())
+ .type(DocumentType.TXT)
.build();
- private static final PortalDocument PORTAL_DOCUMENT = PortalDocument.builder("Title", "file.txt", "hello".getBytes())
- .message("Message")
- .fileType(Document.FileType.TXT)
+ private static final PortalDocument PORTAL_DOCUMENT = PortalDocument.builder("Document title", "hello".getBytes())
+ .type(DocumentType.TXT)
.build();
@Test
public void create_direct_asice_and_write_to_disk() throws IOException {
- DirectJob job = DirectJob.builder(DIRECT_DOCUMENT, singleExitUrl(URI.create("https://job.well.done.org")), DirectSigner.withPersonalIdentificationNumber("12345678910").build())
+ DirectJob job = DirectJob.builder("Job title",
+ asList(DIRECT_DOCUMENT, DIRECT_DOCUMENT),
+ asList(DirectSigner.withPersonalIdentificationNumber("12345678910").build()),
+ singleExitUrl(URI.create("https://job.well.done.org")))
.withReference("direct job")
.build();
@@ -78,16 +83,19 @@ public void create_direct_asice_and_write_to_disk() throws IOException {
@Test
public void create_portal_asice_and_write_to_disk() throws IOException {
- PortalJob job = PortalJob.builder(PORTAL_DOCUMENT, PortalSigner.identifiedByPersonalIdentificationNumber("12345678910", NotificationsUsingLookup.EMAIL_ONLY).build())
+ PortalJob job = PortalJob.builder("Job title",
+ asList(PORTAL_DOCUMENT, PORTAL_DOCUMENT, PORTAL_DOCUMENT),
+ asList(PortalSigner.identifiedByPersonalIdentificationNumber("12345678910", NotificationsUsingLookup.EMAIL_ONLY).build()))
.withReference("portal job")
+ .withDescription("Message")
.withActivationTime(clock.instant())
- .availableFor(30, DAYS)
+ .availableFor(Duration.ofDays(30))
.build();
create_document_bundle_and_dump_to_disk(new CreatePortalManifest(clock), job);
}
- private void create_document_bundle_and_dump_to_disk(ManifestCreator manifestCreator, JOB job) throws IOException {
+ private static void create_document_bundle_and_dump_to_disk(ManifestCreator manifestCreator, JOB job) throws IOException {
CreateASiCE aSiCECreator = new CreateASiCE<>(manifestCreator, ClientConfiguration.builder(CLIENT_KEYSTORE)
.globalSender(new Sender("123456789"))
.enableDocumentBundleDiskDump(dumpFolder)
@@ -105,9 +113,10 @@ private void create_document_bundle_and_dump_to_disk(
fileNames.add(entry.getName());
}
}
- assertThat(fileNames, hasItem(job.getDocument().getFileName()));
- assertThat(fileNames, hasItem("manifest.xml"));
- assertThat(fileNames, hasItem("META-INF/signatures.xml"));
+ assertThat(fileNames, containsInAnyOrder(concat(
+ job.getDocuments().stream().map(Document::getFileName),
+ Stream.of("manifest.xml", "META-INF/signatures.xml"))
+ .toArray(String[]::new)));
}
}
diff --git a/src/test/java/no/digipost/signature/client/asice/archive/CreateZipTest.java b/src/test/java/no/digipost/signature/client/asice/archive/CreateZipTest.java
index a3f79684..7cd80db2 100644
--- a/src/test/java/no/digipost/signature/client/asice/archive/CreateZipTest.java
+++ b/src/test/java/no/digipost/signature/client/asice/archive/CreateZipTest.java
@@ -1,6 +1,7 @@
package no.digipost.signature.client.asice.archive;
import no.digipost.signature.client.asice.ASiCEAttachable;
+import no.digipost.signature.client.core.DocumentType;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.Test;
@@ -41,7 +42,7 @@ private static void verifyZipFile(ZipInputStream zipInputStream, String fileName
assertArrayEquals(IOUtils.toByteArray(zipInputStream), contents.getBytes());
}
- private ASiCEAttachable file(final String fileName, final String contents) {
+ private ASiCEAttachable file(String fileName, String contents) {
return new ASiCEAttachable() {
@Override
public String getFileName() {
@@ -49,13 +50,13 @@ public String getFileName() {
}
@Override
- public byte[] getBytes() {
+ public byte[] getContent() {
return contents.getBytes();
}
@Override
- public String getMimeType() {
- return "text/plain";
+ public String getMediaType() {
+ return DocumentType.TXT.getMediaType();
}
};
}
diff --git a/src/test/java/no/digipost/signature/client/asice/manifest/CreateDirectManifestTest.java b/src/test/java/no/digipost/signature/client/asice/manifest/CreateDirectManifestTest.java
index c701c7c6..9412fbb0 100644
--- a/src/test/java/no/digipost/signature/client/asice/manifest/CreateDirectManifestTest.java
+++ b/src/test/java/no/digipost/signature/client/asice/manifest/CreateDirectManifestTest.java
@@ -1,6 +1,5 @@
package no.digipost.signature.client.asice.manifest;
-import no.digipost.signature.client.core.Document;
import no.digipost.signature.client.core.IdentifierInSignedDocuments;
import no.digipost.signature.client.core.Sender;
import no.digipost.signature.client.direct.DirectDocument;
@@ -11,9 +10,10 @@
import java.net.URI;
-import static uk.co.probablyfine.matchers.Java8Matchers.where;
+import static no.digipost.signature.client.core.DocumentType.TXT;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
+import static uk.co.probablyfine.matchers.Java8Matchers.where;
class CreateDirectManifestTest {
@@ -21,15 +21,13 @@ class CreateDirectManifestTest {
void accept_valid_manifest() {
CreateDirectManifest createManifest = new CreateDirectManifest();
- DirectDocument document = DirectDocument.builder("Title", "file.txt", "hello".getBytes())
- .message("Message")
- .fileType(Document.FileType.TXT)
- .build();
+ DirectDocument document = DirectDocument.builder("Title", "hello".getBytes()).type(TXT).build();
DirectJob job = DirectJob.builder(
+ "Job title",
document,
- ExitUrls.of(URI.create("http://localhost/signed"), URI.create("http://localhost/canceled"), URI.create("http://localhost/failed")),
- DirectSigner.withPersonalIdentificationNumber("12345678910").build())
+ DirectSigner.withPersonalIdentificationNumber("12345678910").build(),
+ ExitUrls.of(URI.create("http://localhost/signed"), URI.create("http://localhost/canceled"), URI.create("http://localhost/failed")))
.withIdentifierInSignedDocuments(IdentifierInSignedDocuments.NAME)
.build();
assertThat(createManifest, where(__ -> __.createManifest(job, new Sender("123456789")), instanceOf(Manifest.class)));
diff --git a/src/test/java/no/digipost/signature/client/asice/manifest/CreatePortalManifestTest.java b/src/test/java/no/digipost/signature/client/asice/manifest/CreatePortalManifestTest.java
index 0af280a3..4544663e 100644
--- a/src/test/java/no/digipost/signature/client/asice/manifest/CreatePortalManifestTest.java
+++ b/src/test/java/no/digipost/signature/client/asice/manifest/CreatePortalManifestTest.java
@@ -1,6 +1,5 @@
package no.digipost.signature.client.asice.manifest;
-import no.digipost.signature.client.core.Document;
import no.digipost.signature.client.core.IdentifierInSignedDocuments;
import no.digipost.signature.client.core.Sender;
import no.digipost.signature.client.portal.NotificationsUsingLookup;
@@ -10,12 +9,12 @@
import org.junit.jupiter.api.Test;
import java.time.Clock;
-import java.util.Collections;
+import java.time.Duration;
-import static uk.co.probablyfine.matchers.Java8Matchers.where;
-import static java.util.concurrent.TimeUnit.DAYS;
+import static no.digipost.signature.client.core.DocumentType.TXT;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
+import static uk.co.probablyfine.matchers.Java8Matchers.where;
class CreatePortalManifestTest {
@@ -25,14 +24,12 @@ class CreatePortalManifestTest {
void accept_valid_manifest() {
CreatePortalManifest createManifest = new CreatePortalManifest(clock);
- PortalDocument document = PortalDocument.builder("Title", "file.txt", "hello".getBytes())
- .message("Message")
- .fileType(Document.FileType.TXT)
- .build();
+ PortalDocument document = PortalDocument.builder("Title", "hello".getBytes()).type(TXT).build();
- PortalJob job = PortalJob.builder(document, Collections.singletonList(PortalSigner.identifiedByPersonalIdentificationNumber("12345678910", NotificationsUsingLookup.EMAIL_ONLY).build()))
+ PortalJob job = PortalJob.builder("Job title", document, PortalSigner.identifiedByPersonalIdentificationNumber("12345678910", NotificationsUsingLookup.EMAIL_ONLY).build())
.withActivationTime(clock.instant())
- .availableFor(30, DAYS)
+ .availableFor(Duration.ofDays(30))
+ .withDescription("Message")
.withIdentifierInSignedDocuments(IdentifierInSignedDocuments.PERSONAL_IDENTIFICATION_NUMBER_AND_NAME)
.build();
assertThat(createManifest, where(__ -> __.createManifest(job, new Sender("123456789")), instanceOf(Manifest.class)));
diff --git a/src/test/java/no/digipost/signature/client/asice/signature/CreateSignatureTest.java b/src/test/java/no/digipost/signature/client/asice/signature/CreateSignatureTest.java
index b00ed53b..918d6842 100644
--- a/src/test/java/no/digipost/signature/client/asice/signature/CreateSignatureTest.java
+++ b/src/test/java/no/digipost/signature/client/asice/signature/CreateSignatureTest.java
@@ -11,6 +11,7 @@
import no.digipost.signature.api.xml.thirdparty.xmldsig.X509IssuerSerialType;
import no.digipost.signature.client.TestKonfigurasjon;
import no.digipost.signature.client.asice.ASiCEAttachable;
+import no.digipost.signature.client.core.DocumentType;
import no.digipost.signature.client.security.KeyStoreConfig;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -26,12 +27,12 @@
import java.util.Base64;
import java.util.List;
-import static uk.co.probablyfine.matchers.Java8Matchers.where;
import static java.util.Arrays.asList;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
+import static uk.co.probablyfine.matchers.Java8Matchers.where;
public class CreateSignatureTest {
@@ -54,8 +55,8 @@ public class CreateSignatureTest {
public void setUp() {
noekkelpar = TestKonfigurasjon.CLIENT_KEYSTORE;
files = asList(
- file("dokument.pdf", "hoveddokument-innhold".getBytes(), "application/pdf"),
- file("manifest.xml", "manifest-innhold".getBytes(), "application/xml")
+ file("dokument.pdf", "hoveddokument-innhold".getBytes(), DocumentType.PDF.getMediaType()),
+ file("manifest.xml", "manifest-innhold".getBytes(), ASiCEAttachable.XML_MEDIATYPE)
);
ZonedDateTime signingTime = ZonedDateTime.of(2018, 11, 29, 9, 15, 0, 0, ZoneId.of("Europe/Oslo"));
@@ -75,7 +76,7 @@ public void test_generated_signatures() {
"PAdNndCACJiWrkHNQ5gTJ+UWx8y2kuzZHEGGTJ+ip9KpCRohDfLapQAMTh0zMLrUNbYpq6kiYWrlxTNfdcVm4skBY0j9Q==";
Signature signature = createSignature.createSignature(files, noekkelpar);
- XAdESSignatures xAdESSignatures = (XAdESSignatures) marshaller.unmarshal(new StreamSource(new ByteArrayInputStream(signature.getBytes())));
+ XAdESSignatures xAdESSignatures = (XAdESSignatures) marshaller.unmarshal(new StreamSource(new ByteArrayInputStream(signature.getContent())));
assertThat(xAdESSignatures, where(XAdESSignatures::getSignatures, hasSize(1)));
no.digipost.signature.api.xml.thirdparty.xmldsig.Signature dSignature = xAdESSignatures.getSignatures().get(0);
@@ -88,7 +89,7 @@ public void test_generated_signatures() {
public void test_xades_signed_properties() {
Signature signature = createSignature.createSignature(files, noekkelpar);
- XAdESSignatures xAdESSignatures = (XAdESSignatures) marshaller.unmarshal(new StreamSource(new ByteArrayInputStream(signature.getBytes())));
+ XAdESSignatures xAdESSignatures = (XAdESSignatures) marshaller.unmarshal(new StreamSource(new ByteArrayInputStream(signature.getContent())));
no.digipost.signature.api.xml.thirdparty.xmldsig.Object object = xAdESSignatures.getSignatures().get(0).getObjects().get(0);
QualifyingProperties xadesProperties = (QualifyingProperties) object.getContent().get(0);
@@ -102,12 +103,12 @@ public void test_xades_signed_properties() {
@Test
public void should_support_filenames_with_spaces_and_other_characters() {
List otherFiles = asList(
- file("dokument (2).pdf", "hoveddokument-innhold".getBytes(), "application/pdf"),
- file("manifest.xml", "manifest-innhold".getBytes(), "application/xml")
+ file("dokument (2).pdf", "hoveddokument-innhold".getBytes(), DocumentType.PDF.getMediaType()),
+ file("manifest.xml", "manifest-innhold".getBytes(), ASiCEAttachable.XML_MEDIATYPE)
);
Signature signature = createSignature.createSignature(otherFiles, noekkelpar);
- XAdESSignatures xAdESSignatures = (XAdESSignatures) marshaller.unmarshal(new StreamSource(new ByteArrayInputStream(signature.getBytes())));
+ XAdESSignatures xAdESSignatures = (XAdESSignatures) marshaller.unmarshal(new StreamSource(new ByteArrayInputStream(signature.getContent())));
String uri = xAdESSignatures.getSignatures().get(0).getSignedInfo().getReferences().get(0).getURI();
assertThat(uri, is("dokument+%282%29.pdf"));
}
@@ -160,14 +161,14 @@ private void assert_dokument_reference(final Reference dokumentReference) {
assertThat(dokumentReference.getDigestMethod().getAlgorithm(), is("http://www.w3.org/2001/04/xmlenc#sha256"));
}
- private ASiCEAttachable file(final String fileName, final byte[] contents, final String mimeType) {
+ private ASiCEAttachable file(String fileName, byte[] contents, String mediaType) {
return new ASiCEAttachable() {
@Override
public String getFileName() { return fileName; }
@Override
- public byte[] getBytes() { return contents; }
+ public byte[] getContent() { return contents; }
@Override
- public String getMimeType() { return mimeType; }
+ public String getMediaType() { return mediaType; }
};
}
diff --git a/src/test/java/no/digipost/signature/client/core/internal/FileNameTest.java b/src/test/java/no/digipost/signature/client/core/internal/FileNameTest.java
new file mode 100644
index 00000000..641810b2
--- /dev/null
+++ b/src/test/java/no/digipost/signature/client/core/internal/FileNameTest.java
@@ -0,0 +1,71 @@
+package no.digipost.signature.client.core.internal;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+import static no.digipost.signature.client.core.internal.FileName.reduceToFileNameSafeChars;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.quicktheories.QuickTheory.qt;
+import static org.quicktheories.generators.SourceDSL.strings;
+
+class FileNameTest {
+
+ @Test
+ void convertsToLowerCase() {
+ assertThat(reduceToFileNameSafeChars("Title"), is("title"));
+ }
+
+ @Test
+ void replacesNorwegianCharacters() {
+ assertThat(reduceToFileNameSafeChars("ÆØÅ"), is("aeoeaa"));
+ assertThat(reduceToFileNameSafeChars("æøå"), is("aeoeaa"));
+ }
+
+ @Test
+ void successiveSpacesIsReplacedWithAnUnderscore() {
+ assertThat(reduceToFileNameSafeChars("Hei på \t Deg"), is("hei_paa_deg"));
+ }
+
+ @Test
+ void successivePunctuationIsReplacedWithAnUnderscore() {
+ assertThat(reduceToFileNameSafeChars("*@,:;&%$^?+=()[]{}#\"“”!`´<>"), is("_"));
+ }
+
+ @Test
+ void emptyNameIsAnError() {
+ assertThrows(IllegalArgumentException.class, () -> reduceToFileNameSafeChars(""));
+ assertThrows(IllegalArgumentException.class, () -> reduceToFileNameSafeChars(null));
+ }
+
+ @Test
+ void accentsAndOtherDiacriticalMarksAreRemoved() {
+ assertThat(reduceToFileNameSafeChars("àáëêÈÉüÜïñÑ"), is("aaeeeeuuinn"));
+ }
+
+ @Test
+ void periodAndDashAreKeptAsIs() {
+ assertThat(reduceToFileNameSafeChars("Morse ...---..."), is("morse_...---..."));
+ }
+
+ @Test
+ void urlEncodingWillAlwaysProduceSameString() {
+ qt()
+ .forAll(strings().allPossible().ofLengthBetween(1, 100))
+ .as(FileName::reduceToFileNameSafeChars)
+ .checkAssert(safeFileName -> assertThat(urlEncode(safeFileName), equalTo(safeFileName)));
+ }
+
+ private static String urlEncode(String s) {
+ try {
+ return URLEncoder.encode(s, "UTF-8");
+ } catch (UnsupportedEncodingException utf8NotSupported) {
+ throw new RuntimeException(utf8NotSupported.getMessage(), utf8NotSupported);
+ }
+ }
+
+}
diff --git a/src/test/java/no/digipost/signature/client/docs/DirectClientUseCases.java b/src/test/java/no/digipost/signature/client/docs/DirectClientUseCases.java
index 3a48f113..8ceb5142 100644
--- a/src/test/java/no/digipost/signature/client/docs/DirectClientUseCases.java
+++ b/src/test/java/no/digipost/signature/client/docs/DirectClientUseCases.java
@@ -32,7 +32,7 @@ static void create_and_send_signature_job() {
DirectClient client = new DirectClient(clientConfiguration);
byte[] documentBytes = null; // Loaded document bytes
- DirectDocument document = DirectDocument.builder("Subject", "document.pdf", documentBytes).build();
+ DirectDocument document = DirectDocument.builder("Document title", documentBytes).build();
ExitUrls exitUrls = ExitUrls.of(
URI.create("http://sender.org/onCompletion"),
@@ -41,7 +41,7 @@ static void create_and_send_signature_job() {
);
DirectSigner signer = DirectSigner.withPersonalIdentificationNumber("12345678910").build();
- DirectJob directJob = DirectJob.builder(document, exitUrls, signer).build();
+ DirectJob directJob = DirectJob.builder("Job title", document, signer, exitUrls).build();
DirectJobResponse directJobResponse = client.create(directJob);
}
@@ -102,7 +102,7 @@ static void get_signature_job_status() {
static void create_job_and_status_by_polling() {
DirectClient client = null; // As initialized earlier
- DirectJob directJob = DirectJob.builder(document, exitUrls, signer)
+ DirectJob directJob = DirectJob.builder("Job title", document, signer, exitUrls)
.retrieveStatusBy(StatusRetrievalMethod.POLLING)
.build();
@@ -148,7 +148,7 @@ static void specifying_queues() {
DirectClient client = null; // As initialized earlier
Sender sender = new Sender("000000000", PollingQueue.of("CustomPollingQueue"));
- DirectJob directJob = DirectJob.builder(document, exitUrls, signer)
+ DirectJob directJob = DirectJob.builder("Job title", document, signer, exitUrls)
.retrieveStatusBy(StatusRetrievalMethod.POLLING).withSender(sender)
.build();
diff --git a/src/test/java/no/digipost/signature/client/docs/PortalClientUseCases.java b/src/test/java/no/digipost/signature/client/docs/PortalClientUseCases.java
index 089f0500..e94cbb1e 100644
--- a/src/test/java/no/digipost/signature/client/docs/PortalClientUseCases.java
+++ b/src/test/java/no/digipost/signature/client/docs/PortalClientUseCases.java
@@ -19,6 +19,8 @@
import java.io.InputStream;
import java.time.Instant;
+import static java.util.Arrays.asList;
+
@SuppressWarnings({"unused", "ConstantConditions", "StatementWithEmptyBody", "null"})
class PortalClientUseCases {
@@ -27,15 +29,17 @@ static void create_and_send_signature_job() {
PortalClient client = new PortalClient(clientConfiguration);
byte[] documentBytes = null; // Loaded document bytes
- PortalDocument document = PortalDocument.builder("Subject", "document.pdf", documentBytes).build();
+ PortalDocument document = PortalDocument.builder("Document title", documentBytes).build();
PortalJob portalJob = PortalJob.builder(
- document,
- PortalSigner.identifiedByPersonalIdentificationNumber("12345678910",
- NotificationsUsingLookup.EMAIL_ONLY).build(),
- PortalSigner.identifiedByPersonalIdentificationNumber("12345678911",
- Notifications.builder().withEmailTo("email@example.com").build()).build(),
- PortalSigner.identifiedByEmail("email@example.com").build()
+ "Job title",
+ asList(document),
+ asList(
+ PortalSigner.identifiedByPersonalIdentificationNumber("12345678910",
+ NotificationsUsingLookup.EMAIL_ONLY).build(),
+ PortalSigner.identifiedByPersonalIdentificationNumber("12345678911",
+ Notifications.builder().withEmailTo("email@example.com").build()).build(),
+ PortalSigner.identifiedByEmail("email@example.com").build())
).build();
PortalJobResponse portalJobResponse = client.create(portalJob);
@@ -95,15 +99,17 @@ static void specifying_queues() {
Sender sender = new Sender("000000000", PollingQueue.of("CustomPollingQueue"));
byte[] documentBytes = null; // Loaded document bytes
- PortalDocument document = PortalDocument.builder("Subject", "document.pdf", documentBytes).build();
+ PortalDocument document = PortalDocument.builder("Document title", documentBytes).build();
PortalJob portalJob = PortalJob.builder(
- document,
- PortalSigner.identifiedByPersonalIdentificationNumber("12345678910",
- NotificationsUsingLookup.EMAIL_ONLY).build(),
- PortalSigner.identifiedByPersonalIdentificationNumber("12345678911",
- Notifications.builder().withEmailTo("email@example.com").build()).build(),
- PortalSigner.identifiedByEmail("email@example.com").build()
+ "Job title",
+ asList(document),
+ asList(
+ PortalSigner.identifiedByPersonalIdentificationNumber("12345678910",
+ NotificationsUsingLookup.EMAIL_ONLY).build(),
+ PortalSigner.identifiedByPersonalIdentificationNumber("12345678911",
+ Notifications.builder().withEmailTo("email@example.com").build()).build(),
+ PortalSigner.identifiedByEmail("email@example.com").build())
).withSender(sender).build();
PortalJobResponse portalJobResponse = client.create(portalJob);