diff --git a/.github/ISSUE_TEMPLATE/release-checklist.md b/.github/ISSUE_TEMPLATE/release-checklist.md
new file mode 100644
index 000000000..912285221
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/release-checklist.md
@@ -0,0 +1,43 @@
+---
+name: Release checklist
+about: Použi tento checklist na testovanie artefaktov pred public releaseom
+title: "Release "
+labels: release
+assignees: "@slovensko-digital/autogram-release-team"
+---
+
+## Windows
+
+- [ ] funguje inštalácia na Windows cez stiahnutý .msi
+- [ ] funguje spustenie v GUI móde
+- [ ] funguje URL handler [autogram://go](autogram://go)
+- [ ] funguje GUI otvoriť jeden súbor, ten sa zobrazí, viem ho podpísať, vytvorí sa podpísaný súbor
+- [ ] funguje CLI `autogram --help`
+
+## Linux
+- [ ] funguje inštalácia na Linux (Debian-based) cez stiahnutý .deb
+- [ ] funguje inštalácia na Linux cez stiahnutý .rpm
+- [ ] funguje spustenie v GUI móde
+- [ ] funguje URL handler [autogram://go](autogram://go)
+- [ ] funguje GUI otvoriť jeden súbor, ten sa zobrazí, viem ho podpísať, vytvorí sa podpísaný súbor
+- [ ] funguje CLI `autogram --help`
+
+## MacOS
+- [ ] funguje inštalácia na MacOS cez stiahnutý .pkg
+- [ ] funguje spustenie v GUI móde
+- [ ] funguje URL handler [autogram://go](autogram://go)
+- [ ] funguje GUI otvoriť jeden súbor, ten sa zobrazí, viem ho podpísať, vytvorí sa podpísaný súbor
+- [ ] funguje CLI `/Applications/Autogram.app/Contents/MacOS/AutogramApp --help`
+
+
+## Na aspoň jednom systéme
+
+- [ ] fungujú všetky smoke testy `./mvnw test -Psmoke`
+- [ ] funguje spustenie v GUI serverovom móde `autogram --url=autogram://listen?protocol=http&port=37201` na inom porte
+- [ ] funguje CLI `autogram --cli --source source.pdf`
+- [ ] funguje CLI `autogram --cli --source source.pdf --target target.pdf`
+- [ ] funguje CLI `autogram --cli --source source-dir --target target-dir`
+- [ ] funguje API info request
+- [ ] funguje API docs request
+- [ ] funguje API sign request
+- [ ] funguje s [extension](https://github.com/slovensko-digital/autogram-extension)
\ No newline at end of file
diff --git a/.github/workflows/prerelease.yaml b/.github/workflows/prerelease.yaml
new file mode 100644
index 000000000..43abc50af
--- /dev/null
+++ b/.github/workflows/prerelease.yaml
@@ -0,0 +1,22 @@
+name: Pre-release
+on:
+ release:
+ types: [prereleased]
+jobs:
+ checklist:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: imjohnbo/extract-issue-template-fields@f5232d9bc6ad9aff26c462e60a2c8ef0b17aa3fa
+ id: issueTemplate
+ with:
+ path: .github/ISSUE_TEMPLATE/release-checklist.md
+
+ - name: Create release checklist issue
+ uses: imjohnbo/issue-bot@3d96848fb5e9a4a473bb81ae62b4b4866a56e93a
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ assignees: ${{ steps.issueTemplate.outputs.assignees }}
+ labels: ${{ steps.issueTemplate.outputs.labels }}
+ title: ${{ steps.issueTemplate.outputs.title }} ${{ github.event.release.tag_name }}
+ body: ${{ steps.issueTemplate.outputs.body }}
diff --git a/RELEASE.md b/RELEASE.md
new file mode 100644
index 000000000..f1c5790f4
--- /dev/null
+++ b/RELEASE.md
@@ -0,0 +1,33 @@
+Release robí slovensko.digital, ale checklist je verejný aby mohol každý contributor samostatne
+pretestovať zmeny pred tým než urobí PR.
+
+# Release Checklist
+
+## Kontrola nového kódu
+
+Je potrebné skontrolovať či nepribudol škodlivý kód, exfiltrácia secrets cez GH actions, či je nová core funkcionalita pokrytá testami.
+
+## Testing
+
+- [ ] zelené všetky automatizované testy
+- [ ] funguje inštalácia na Windows cez stiahnutý .msi
+- [ ] funguje inštalácia na MacOS cez stiahnutý .pkg
+- [ ] funguje inštalácia na Linux (Debian-based) cez stiahnutý .deb
+- [ ] funguje inštalácia na Linux cez stiahnutý .rpm
+- [ ] funguje spustenie v GUI móde
+- [ ] funguje spustenie v GUI serverovom móde `autogram --url=autogram://listen?protocol=http&port=37200`
+- [ ] funguje URL handler na Windows [autogram://go](autogram://go)
+- [ ] funguje URL handler na MacOS [autogram://go](autogram://go)
+- [ ] funguje URL handler na Linux [autogram://go](autogram://go)
+- [ ] funguje GUI otvoriť jeden súbor, ten sa zobrazí, viem ho podpísať, vytvorí sa podpísaný súbor
+- [ ] funguje CLI `autogram --cli --source source.pdf`
+- [ ] funguje CLI `autogram --cli --source source.pdf --target target.pdf`
+- [ ] funguje CLI `autogram --cli --source source-dir --target target-dir`
+- [ ] funguje API info request
+- [ ] funguje API docs request
+- [ ] funguje API sign request
+- [ ] funguje s [extension](https://github.com/slovensko-digital/autogram-extension)
+
+## Pripraviť popis releasu
+
+vid. existujúce [Releases](https://github.com/slovensko-digital/autogram/releases)
diff --git a/pom.xml b/pom.xml
index a72ff3c8c..e1856fcfe 100644
--- a/pom.xml
+++ b/pom.xml
@@ -26,7 +26,9 @@
5.4.0
1.5.0
2.9.1
+ 2.0
1.3.0
+ HttpSmokeTest
@@ -133,6 +135,26 @@
${xmlunit.version}
test
+
+ org.yaml
+ snakeyaml
+ ${snakeyml.version}
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ ${junit.version}
+ test
+
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ ${junit.version}
+ test
+
+
com.google.jimfs
jimfs
@@ -150,10 +172,9 @@
3.1.2
${jlink.jdk.path}${file.separator}bin${file.separator}java
-
-
- **/BatchSigningHttpSmokeTests.java
-
+
+ ${testExcludedGroups}
@@ -519,5 +540,25 @@
Bearer ${env.GITHUB_TOKEN}
+
+
+ smoke
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.1.2
+
+ ${jlink.jdk.path}${file.separator}bin${file.separator}java
+ HttpSmokeTest
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/digital/slovensko/autogram/server/dto/ServerSigningParameters.java b/src/main/java/digital/slovensko/autogram/server/dto/ServerSigningParameters.java
index f50099e02..5570024b1 100644
--- a/src/main/java/digital/slovensko/autogram/server/dto/ServerSigningParameters.java
+++ b/src/main/java/digital/slovensko/autogram/server/dto/ServerSigningParameters.java
@@ -47,12 +47,12 @@ public enum VisualizationWidthEnum {
private final VisualizationWidthEnum visualizationWidth;
public ServerSigningParameters(SignatureLevel level, ASiCContainerType container,
- String containerFilename, String containerXmlns, SignaturePackaging packaging,
- DigestAlgorithm digestAlgorithm,
- Boolean en319132, LocalCanonicalizationMethod infoCanonicalization,
- LocalCanonicalizationMethod propertiesCanonicalization, LocalCanonicalizationMethod keyInfoCanonicalization,
- String schema, String transformation,
- String Identifier, boolean checkPDFACompliance, VisualizationWidthEnum preferredPreviewWidth) {
+ String containerFilename, String containerXmlns, SignaturePackaging packaging,
+ DigestAlgorithm digestAlgorithm,
+ Boolean en319132, LocalCanonicalizationMethod infoCanonicalization,
+ LocalCanonicalizationMethod propertiesCanonicalization, LocalCanonicalizationMethod keyInfoCanonicalization,
+ String schema, String transformation,
+ String Identifier, boolean checkPDFACompliance, VisualizationWidthEnum preferredPreviewWidth) {
this.level = level;
this.container = container;
this.containerXmlns = containerXmlns;
diff --git a/src/test/java/digital/slovensko/autogram/BatchSigningHttpSmokeTests.java b/src/test/java/digital/slovensko/autogram/BatchSigningHttpSmokeTests.java
index 18440dec5..6d66b1317 100644
--- a/src/test/java/digital/slovensko/autogram/BatchSigningHttpSmokeTests.java
+++ b/src/test/java/digital/slovensko/autogram/BatchSigningHttpSmokeTests.java
@@ -11,6 +11,7 @@
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Tag;
import com.google.gson.Gson;
@@ -18,6 +19,7 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
+@Tag("HttpSmokeTest")
public class BatchSigningHttpSmokeTests {
@Test
void testBatchSigningHappyScenario() throws URISyntaxException, ClientProtocolException, IOException {
diff --git a/src/test/java/digital/slovensko/autogram/SignHttpSmokeTest.java b/src/test/java/digital/slovensko/autogram/SignHttpSmokeTest.java
new file mode 100644
index 000000000..97e1150ee
--- /dev/null
+++ b/src/test/java/digital/slovensko/autogram/SignHttpSmokeTest.java
@@ -0,0 +1,214 @@
+package digital.slovensko.autogram;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.Map;
+
+import org.apache.http.HttpStatus;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.util.EntityUtils;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.TestInstance.Lifecycle;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.junit.platform.commons.util.ReflectionUtils;
+import org.yaml.snakeyaml.Yaml;
+
+import digital.slovensko.autogram.server.dto.Document;
+import digital.slovensko.autogram.server.dto.ServerSigningParameters;
+import digital.slovensko.autogram.server.dto.ServerSigningParameters.LocalCanonicalizationMethod;
+import digital.slovensko.autogram.server.dto.ServerSigningParameters.VisualizationWidthEnum;
+import eu.europa.esig.dss.enumerations.ASiCContainerType;
+import eu.europa.esig.dss.enumerations.DigestAlgorithm;
+import eu.europa.esig.dss.enumerations.SignatureLevel;
+import eu.europa.esig.dss.enumerations.SignaturePackaging;
+import digital.slovensko.autogram.server.dto.SignRequestBody;
+import digital.slovensko.autogram.server.dto.SignResponse;
+
+// TODO mvn test -Psmoke
+@TestInstance(Lifecycle.PER_CLASS)
+@Tag("HttpSmokeTest")
+public class SignHttpSmokeTest {
+
+ Map data;
+
+ URI baseUri;
+ HttpClientBuilder clientBuilder;
+
+ public static Object getNested(Object obj, String... keys) {
+ for (String key : keys) {
+ if (obj instanceof Map) {
+ obj = ((Map, ?>) obj).get(key);
+ } else {
+ return null;
+ }
+ }
+ return obj;
+ }
+
+ public Object getNested(String... keys) {
+ return getNested(data, keys);
+ }
+
+ public String getDecoded(Object obj) {
+ return new String(Base64.getDecoder().decode(((String) obj)));
+ }
+
+ public String getDecoded(String... keys) {
+ return getDecoded(getNested(keys));
+ }
+
+ @BeforeAll
+ public void initAll() throws FileNotFoundException, URISyntaxException {
+ Yaml yaml = new Yaml();
+ var inputStream = new FileInputStream(
+ new File("src/main/resources/digital/slovensko/autogram/server/server.yml"));
+
+ data = (Map) yaml.load(inputStream);
+ baseUri = new URI("http://localhost:37200");
+ clientBuilder = HttpClientBuilder.create();
+ }
+
+ @Test
+ @Tag("HttpSmokeTest")
+ void testSigningHappyScenario() throws URISyntaxException, ClientProtocolException, IOException {
+
+ var signRequest = new HttpPost(baseUri.resolve("/sign"));
+ signRequest.setHeader("Content-Type", "application/json");
+ var signRequestBody = """
+ {"document": {
+ "content": "Testovací dokument",
+ "filename": "TextDocument.txt"
+ },
+ "parameters": {
+ "level": "XAdES_BASELINE_B",
+ "container": "ASiC_E"
+ },
+ "payloadMimeType": "text/plain"
+ }
+ """;
+ System.out.println("Sign request body: " + signRequestBody);
+
+ signRequest.setEntity(new StringEntity(signRequestBody, "UTF-8"));
+ var signResponse = clientBuilder.build().execute(signRequest);
+ assertEquals(HttpStatus.SC_OK, signResponse.getStatusLine().getStatusCode());
+ // System.out.println("Sign Response: " + signResponse.getStatusLine());
+ // System.out.println("Sign Response: " + new String(
+ // signResponse.getEntity().getContent().readAllBytes(), StandardCharsets.UTF_8));
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {
+ "XAdES-XML-Base64-HTML_md", "XAdES-XML-Base64-TXT", "XAdES-XML-TXT-HTML",
+ "XAdES-XML-TXT-TXT_md", "XAdES-ASiC_E-Base64-HTML", "XAdES-ASiC_E-Base64-TXT",
+ "XAdES-ASiC_E-TXT-HTML_md", "XAdES-ASiC_E-TXT-TXT", "XAdES-ASiC_E-SKXDC-Base64-HTML",
+ "XAdES-ASiC_E-SKXDC-TXT-HTML", "PAdES-PDF_lg", "XAdES-PDF",
+ "XAdES-ASiC_E-PDF", "XAdES-ASiC_E-TXT", "XAdES-ASiC_E-DOCX",
+ "CAdES-ASiC_E-DOCX", "CAdES-ASiC_E-PNG_md", "CAdES-PNG_lg",
+ })
+ public void testFromYaml(String exampleName) throws ClientProtocolException, IOException, IllegalAccessException,
+ NoSuchFieldException, SecurityException {
+ var example = getNested("components", "examples", exampleName, "value");
+ var document = (Map) getNested(example, "document");
+ var parameters = getNested(example, "parameters");
+ var payloadMimeType = getNested(example, "payloadMimeType");
+
+ var rDocument = new Document(document.get("filename"), document.get("content"));
+ var rParameters = fromMap((Map) parameters);
+ var rPayloadMimeType = (String) payloadMimeType;
+
+ var body = new SignRequestBody(
+ rDocument,
+ rParameters,
+ rPayloadMimeType);
+ var gson = new com.google.gson.Gson();
+
+ var signRequest = new HttpPost(baseUri.resolve("/sign"));
+ signRequest.setHeader("Content-Type", "application/json");
+ var entity = new StringEntity(gson.toJson(body), "UTF-8");
+ signRequest.setEntity(entity);
+
+ System.out.println("Sign request body: " + EntityUtils.toString(entity));
+ var signResponse = clientBuilder.build().execute(signRequest);
+ System.out.println("Sign Response: " + signResponse.getStatusLine());
+
+ var responseStr = new String(
+ signResponse.getEntity().getContent().readAllBytes(), StandardCharsets.UTF_8);
+ System.out.println("Sign Response: " + responseStr);
+ assertEquals(HttpStatus.SC_OK, signResponse.getStatusLine().getStatusCode());
+ var response = gson.fromJson(responseStr, SignResponse.class);
+
+ ReflectionUtils
+ .findFields(SignResponse.class, (e) -> true,
+ ReflectionUtils.HierarchyTraversalMode.TOP_DOWN)
+ .forEach(f -> {
+ f.setAccessible(true);
+ try {
+ var o = f.get(response);
+ assertNotNull(o);
+ System.out.println(f.getName() + ": " + o);
+ } catch (IllegalArgumentException | IllegalAccessException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+ });
+ }
+
+
+ private static ServerSigningParameters fromMap(Map map) {
+ var level = SignatureLevel.valueByName((String) map.get("level"));
+ var container = fromMapToEnum(ASiCContainerType.class, map.get("container"));
+ var containerFilename = (String) map.get("containerFilename");
+ var containerXmlns = (String) map.get("containerXmlns");
+ var packaging = fromMapToEnum(SignaturePackaging.class, map.get("packaging"));
+ var digestAlgorithm = fromMapToEnum(DigestAlgorithm.class, map.get("digestAlgorithm"));
+ var en319132 = (Boolean) map.get("en319132");
+ var infoCanonicalization = fromMapToEnum(LocalCanonicalizationMethod.class, map.get("infoCanonicalization"));
+ var propertiesCanonicalization = fromMapToEnum(LocalCanonicalizationMethod.class,
+ map.get("propertiesCanonicalization"));
+ var keyInfoCanonicalization = fromMapToEnum(LocalCanonicalizationMethod.class,
+ map.get("keyInfoCanonicalization"));
+ var schema = (String) map.get("schema");
+ var transformation = (String) map.get("transformation");
+ var identifier = (String) map.get("identifier");
+ var checkPDFACompliance = map.getOrDefault("checkPDFACompliance", "false") == "true";
+ var visualizationWidth = fromMapToEnum(VisualizationWidthEnum.class, map.get("visualizationWidth"));
+
+ return new ServerSigningParameters(
+ level,
+ container,
+ containerFilename,
+ containerXmlns,
+ packaging,
+ digestAlgorithm,
+ en319132,
+ infoCanonicalization,
+ propertiesCanonicalization,
+ keyInfoCanonicalization,
+ schema,
+ transformation,
+ identifier, checkPDFACompliance, visualizationWidth);
+ }
+
+ private static > T fromMapToEnum(Class clazz, Object obj) {
+ var visualizationWidthStr = (String) obj;
+ if (visualizationWidthStr == null)
+ return null;
+ return T.valueOf(clazz, visualizationWidthStr);
+ }
+}
\ No newline at end of file