From 96f8b3dafd20af719a4ac0b0bd3d39a39cab5663 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asger=20Geel=20Weirs=C3=B8e?= Date: Mon, 22 Jan 2024 09:43:48 +0100 Subject: [PATCH 1/2] Version AP-v1.2 --- .gitignore | 3 +- README.md | 27 ++ docs/public.png | Bin 0 -> 85004 bytes mvnw | 0 mvnw.cmd | 374 +++++++++--------- pom.xml | 6 +- .../Schematron/PEPPOL/CEN-EN16931-UBL.xslt | 22 +- .../Schematron/PEPPOL/PEPPOL-EN16931-UBL.xslt | 243 ++++++------ .../Schematron/PEPPOL/PEPPOLBIS-T01.xslt | 29 +- .../Schematron/PEPPOL/PEPPOLBIS-T110.xslt | 28 +- .../Schematron/PEPPOL/PEPPOLBIS-T111.xslt | 14 +- .../Schematron/PEPPOL/PEPPOLBIS-T114.xslt | 28 +- .../Schematron/PEPPOL/PEPPOLBIS-T115.xslt | 10 +- .../Schematron/PEPPOL/PEPPOLBIS-T116.xslt | 14 +- .../Schematron/PEPPOL/PEPPOLBIS-T16.xslt | 26 +- .../Schematron/PEPPOL/PEPPOLBIS-T19.xslt | 34 +- .../Schematron/PEPPOL/PEPPOLBIS-T58.xslt | 4 +- .../Schematron/PEPPOL/PEPPOLBIS-T71.xslt | 4 +- .../Schematron/PEPPOL/PEPPOLBIS-T76.xslt | 12 +- .../Schematron/PEPPOL/PEPPOLBIS-T77.xslt | 20 +- .../NemhandelPersisterHandlerTest.java | 2 +- .../as4/reader/NemhandelReaderTest.java | 5 +- .../resources/as2-peppol-bis-invoice-sbdh.xml | 0 .../busdox-servicemetadata-9908-12345678.xml | 222 ++++++----- .../busdox-servicemetadata-9908-923829644.xml | 230 ++++++----- .../busdox-servicemetadata-9908-98765432.xml | 226 ++++++----- 26 files changed, 867 insertions(+), 716 deletions(-) create mode 100644 docs/public.png mode change 100644 => 100755 mvnw mode change 100644 => 100755 src/test/resources/as2-peppol-bis-invoice-sbdh.xml diff --git a/.gitignore b/.gitignore index cc7629b..0a7e543 100644 --- a/.gitignore +++ b/.gitignore @@ -206,4 +206,5 @@ gradlew.bat gradle/ ERSTpackage/ ERST_oxalis-dependency.txt -src/main/resources/db/DEMO-databasechangelog.csv \ No newline at end of file +src/main/resources/db/DEMO-databasechangelog.csv +src/main/resources/db/demo-oxalis-as4-db-changelog.xml diff --git a/README.md b/README.md index a2bcdec..ccd919b 100644 --- a/README.md +++ b/README.md @@ -9,3 +9,30 @@ For general instructions on how to install and use Oxalis, please refer to [oxal * [Installation guide](docs/installation/index.md) * [OpenPEPPOL Test Bed](docs/peppol-test-bed/index.md) * [CEF connectivity test](docs/cef-connectivity/index.md) + +## Versions +There will be two different versions of the Reference Implementation to note. This being AP-v1.1.1 and AP-v1.2. +Both of these will be released on the same date, but they are first allowed to be ran in production on different times. + +![Time graph over versions. AP-v1.1 in Nemhandel eDelivery mode is allowed until 20/03-24, in Peppol eDelivery mode until 26/02-24. AP-v1.1.1 is allowed in Nemhandel Peppol mode from the 26/02-2024 until 20/03-2024. AP-v1.2 is allowed in both Nemhandel eDelivery mode and Peppol eDelivery mode from 20/03-24.](docs/public.png "Time graph over versions") + + + +The releases in this repository is in the shape of tags. +To have the tags be in your git history, in the git terminal type: +```bash +git fetch --all --tags +``` + +Now it is possible to check out AP-v1.1.1 with the following command: + +```bash +git checkout tags/ap-v1.1.1 +``` +likewise, it is possible to check out AP-v1.2 with the following command: + +```bash +git checkout tags/ap-v1.2 +``` + +Please note the allowed intervals that the different versions can be utilized from the image. diff --git a/docs/public.png b/docs/public.png new file mode 100644 index 0000000000000000000000000000000000000000..6c78d600d81f99edd41110bf9451febac6e22bcf GIT binary patch literal 85004 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 literal 0 HcmV?d00001 diff --git a/mvnw b/mvnw old mode 100644 new mode 100755 diff --git a/mvnw.cmd b/mvnw.cmd index 474c9d6..cba1f04 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -1,187 +1,187 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM http://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Apache Maven Wrapper startup batch script, version 3.1.1 -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* -if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" - -FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %WRAPPER_URL% - ) - - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) -) -@REM End of extension - -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* - -%MAVEN_JAVA_EXE% ^ - %JVM_CONFIG_MAVEN_PROPS% ^ - %MAVEN_OPTS% ^ - %MAVEN_DEBUG_OPTS% ^ - -classpath %WRAPPER_JAR% ^ - "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ - %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" -if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%"=="on" pause - -if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% - -cmd /C exit /B %ERROR_CODE% +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.1.1 +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %WRAPPER_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml index ff82903..ce15262 100755 --- a/pom.xml +++ b/pom.xml @@ -27,12 +27,12 @@ dk.erst.oxalis oxalis - 1.0.0-9ee54a32bac5bdabe2a86339ebbfdadeb3d5e19f + 1.1.1-cdcd75c57f1c18846d6950469e8f4fb53b3b8584 dk.erst.oxalis oxalis-as4 - 1.1.0 + 1.1.1 jar Oxalis :: AS4 Extension adding Nemhandel e-Delivery AS4 support to Oxalis @@ -479,7 +479,7 @@ io.swagger.core.v3 swagger-jaxrs2 - 2.2.8 + 2.2.15 diff --git a/src/main/resources/META-INF/Schematron/PEPPOL/CEN-EN16931-UBL.xslt b/src/main/resources/META-INF/Schematron/PEPPOL/CEN-EN16931-UBL.xslt index cc9ee92..7fc5cd1 100644 --- a/src/main/resources/META-INF/Schematron/PEPPOL/CEN-EN16931-UBL.xslt +++ b/src/main/resources/META-INF/Schematron/PEPPOL/CEN-EN16931-UBL.xslt @@ -3759,11 +3759,11 @@ - + context="//cac:PostalAddress | //cac:Address"/> @@ -3825,16 +3825,16 @@ - + + test="((cbc:DocumentTypeCode='130') or ((local-name(/*) = 'CreditNote') and (cbc:DocumentTypeCode='50')) or (not(cbc:ID/@schemeID) and not(cbc:DocumentTypeCode)))"> UBL-SR-43 fatal - [UBL-SR-43]-Scheme identifier shall only be used for invoiced object (document type code with value 130) + [UBL-SR-43]-Scheme identifier shall only be used for invoiced object (document type code with value 130 or 50) @@ -15158,10 +15158,10 @@ context="cbc:Amount | cbc:BaseAmount | cbc:PriceAmount | cbc:TaxAmount | cbc:TaxableAmount | cbc:LineExtensionAmount | cbc:TaxExclusiveAmount | cbc:TaxInclusiveAmount | cbc:AllowanceTotalAmount | cbc:ChargeTotalAmount | cbc:PrepaidAmount | cbc:PayableRoundingAmount | cbc:PayableAmount"/> - + + test="((not(contains(normalize-space(@currencyID), ' ')) and contains(' AED AFN ALL AMD ANG AOA ARS AUD AWG AZN BAM BBD BDT BGN BHD BIF BMD BND BOB BOV BRL BSD BTN BWP BYN BZD CAD CDF CHE CHF CHW CLF CLP CNY COP COU CRC CUC CUP CVE CZK DJF DKK DOP DZD EGP ERN ETB EUR FJD FKP GBP GEL GHS GIP GMD GNF GTQ GYD HKD HNL HRK HTG HUF IDR ILS INR IQD IRR ISK JMD JOD JPY KES KGS KHR KMF KPW KRW KWD KYD KZT LAK LBP LKR LRD LSL LYD MAD MDL MGA MKD MMK MNT MOP MRU MUR MVR MWK MXN MXV MYR MZN NAD NGN NIO NOK NPR NZD OMR PAB PEN PGK PHP PKR PLN PYG QAR RON RSD RUB RWF SAR SBD SCR SDG SEK SGD SHP SLE SLL SOS SRD SSP STN SVC SYP SZL THB TJS TMT TND TOP TRY TTD TWD TZS UAH UGX USD USN UYI UYU UYW UZS VES VND VUV WST XAF XAG XAU XBA XBB XBC XBD XCD XDR XOF XPD XPF XPT XSU XTS XUA XXX YER ZAR ZMW ZWL ', concat(' ', normalize-space(@currencyID), ' '))))"> BR-CL-03 fatal @@ -15179,10 +15179,10 @@ context="cbc:DocumentCurrencyCode"/> - + + test="((not(contains(normalize-space(.), ' ')) and contains(' AED AFN ALL AMD ANG AOA ARS AUD AWG AZN BAM BBD BDT BGN BHD BIF BMD BND BOB BOV BRL BSD BTN BWP BYN BZD CAD CDF CHE CHF CHW CLF CLP CNY COP COU CRC CUC CUP CVE CZK DJF DKK DOP DZD EGP ERN ETB EUR FJD FKP GBP GEL GHS GIP GMD GNF GTQ GYD HKD HNL HRK HTG HUF IDR ILS INR IQD IRR ISK JMD JOD JPY KES KGS KHR KMF KPW KRW KWD KYD KZT LAK LBP LKR LRD LSL LYD MAD MDL MGA MKD MMK MNT MOP MRU MUR MVR MWK MXN MXV MYR MZN NAD NGN NIO NOK NPR NZD OMR PAB PEN PGK PHP PKR PLN PYG QAR RON RSD RUB RWF SAR SBD SCR SDG SEK SGD SHP SLE SLL SOS SRD SSP STN SVC SYP SZL THB TJS TMT TND TOP TRY TTD TWD TZS UAH UGX USD USN UYI UYU UYW UZS VES VND VUV WST XAF XAG XAU XBA XBB XBC XBD XCD XDR XOF XPD XPF XPT XSU XTS XUA XXX YER ZAR ZMW ZWL ', concat(' ', normalize-space(.), ' '))))"> BR-CL-04 fatal @@ -15199,10 +15199,10 @@ - + + test="((not(contains(normalize-space(.), ' ')) and contains(' AED AFN ALL AMD ANG AOA ARS AUD AWG AZN BAM BBD BDT BGN BHD BIF BMD BND BOB BOV BRL BSD BTN BWP BYN BZD CAD CDF CHE CHF CHW CLF CLP CNY COP COU CRC CUC CUP CVE CZK DJF DKK DOP DZD EGP ERN ETB EUR FJD FKP GBP GEL GHS GIP GMD GNF GTQ GYD HKD HNL HRK HTG HUF IDR ILS INR IQD IRR ISK JMD JOD JPY KES KGS KHR KMF KPW KRW KWD KYD KZT LAK LBP LKR LRD LSL LYD MAD MDL MGA MKD MMK MNT MOP MRU MUR MVR MWK MXN MXV MYR MZN NAD NGN NIO NOK NPR NZD OMR PAB PEN PGK PHP PKR PLN PYG QAR RON RSD RUB RWF SAR SBD SCR SDG SEK SGD SHP SLE SLL SOS SRD SSP STN SVC SYP SZL THB TJS TMT TND TOP TRY TTD TWD TZS UAH UGX USD USN UYI UYU UYW UZS VES VND VUV WST XAF XAG XAU XBA XBB XBC XBD XCD XDR XOF XPD XPF XPT XSU XTS XUA XXX YER ZAR ZMW ZWL ', concat(' ', normalize-space(.), ' '))))"> BR-CL-05 fatal diff --git a/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOL-EN16931-UBL.xslt b/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOL-EN16931-UBL.xslt index 7c3ffa0..c7aab4f 100644 --- a/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOL-EN16931-UBL.xslt +++ b/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOL-EN16931-UBL.xslt @@ -278,13 +278,6 @@ - - - - - - - @@ -327,6 +320,13 @@ + + + + + + + @@ -371,7 +371,7 @@ + mode="M23"> @@ -388,15 +388,15 @@ - + - - - + + + - + @@ -414,17 +414,17 @@ - + - - - + + + + mode="M25"> @@ -544,25 +544,10 @@ - - - - - - PEPPOL-EN16931-R006 - fatal - - - - Only one invoiced object is allowed on document level - - - - + - + @@ -579,12 +564,12 @@ - + + mode="M25"> @@ -601,12 +586,12 @@ - + + mode="M25"> @@ -623,12 +608,12 @@ - + + mode="M25"> @@ -645,12 +630,12 @@ - + + mode="M25"> @@ -667,12 +652,12 @@ - + + mode="M25"> @@ -705,12 +690,12 @@ - + + mode="M25"> @@ -728,12 +713,12 @@ - + + mode="M25"> @@ -751,12 +736,12 @@ - + + mode="M25"> @@ -774,12 +759,12 @@ - + + mode="M25"> @@ -797,12 +782,12 @@ - + + mode="M25"> - + - + @@ -913,12 +898,12 @@ - + + mode="M25"> - + + mode="M25"> @@ -963,12 +948,12 @@ - + + mode="M25"> @@ -986,12 +971,12 @@ - + + mode="M25"> @@ -1009,12 +994,12 @@ - + + mode="M25"> @@ -1032,12 +1017,12 @@ - + + mode="M25"> @@ -1055,12 +1040,12 @@ - + + mode="M25"> @@ -1078,12 +1063,12 @@ - + + mode="M25"> @@ -1101,12 +1086,12 @@ - + + mode="M25"> @@ -1124,12 +1109,12 @@ - + + mode="M25"> @@ -1147,12 +1132,12 @@ - + + mode="M25"> @@ -1170,17 +1155,17 @@ - + - - - + + + + mode="M26"> @@ -1216,11 +1201,11 @@ - + - - - + + + + mode="M27"> @@ -1278,12 +1263,12 @@ - + + mode="M27"> @@ -1301,12 +1286,12 @@ - + + mode="M27"> @@ -1414,12 +1399,12 @@ - + + mode="M27"> @@ -1437,12 +1422,12 @@ - + + mode="M27"> @@ -1460,17 +1445,17 @@ - + - - - + + + + mode="M28"> @@ -1488,12 +1473,12 @@ - + + mode="M28"> @@ -1541,17 +1526,17 @@ - + - - - + + + + mode="M29"> @@ -1584,12 +1569,12 @@ - + + mode="M29"> @@ -1622,12 +1607,12 @@ - + + mode="M29"> @@ -1645,12 +1630,12 @@ - + + mode="M29"> @@ -1668,12 +1653,12 @@ - + + mode="M29"> @@ -1706,12 +1691,12 @@ - + + mode="M29"> @@ -1744,12 +1729,12 @@ - + + mode="M29"> @@ -1766,12 +1751,12 @@ - + + mode="M29"> @@ -1788,11 +1773,11 @@ - + - - - + + + @@ -1806,7 +1791,7 @@ + select="tokenize('1.1 1.6 2.1 2.4 5.1 5.2 ','\s')"/> + select="tokenize('0002 0007 0009 0037 0060 0088 0096 0097 0106 0130 0135 0142 0151 0183 0184 0188 0190 0191 0192 0193 0195 0196 0198 0199 0200 0201 0202 0204 0208 0209 0210 0211 0212 0213 0215 0216 0218 0221 0230 9901 9910 9913 9914 9915 9918 9919 9920 9922 9923 9924 9925 9926 9927 9928 9929 9930 9931 9932 9933 9934 9935 9936 9937 9938 9939 9940 9941 9942 9943 9944 9945 9946 9947 9948 9949 9950 9951 9952 9953 9957 9959', '\s')"/> - - - - + + + - - - + - + + + + diff --git a/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOLBIS-T110.xslt b/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOLBIS-T110.xslt index 2c10f42..3dd113d 100644 --- a/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOLBIS-T110.xslt +++ b/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOLBIS-T110.xslt @@ -613,29 +613,29 @@ - - + + + + + + - - - - - + diff --git a/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOLBIS-T111.xslt b/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOLBIS-T111.xslt index d8b8c1a..fbb2333 100644 --- a/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOLBIS-T111.xslt +++ b/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOLBIS-T111.xslt @@ -606,18 +606,18 @@ - - + select="tokenize('0002 0007 0009 0037 0060 0088 0096 0097 0106 0130 0135 0142 0151 0183 0184 0188 0190 0191 0192 0193 0195 0196 0198 0199 0200 0201 0202 0204 0208 0209 0210 0211 0212 0213 0215 0216 0218 0221 0230 9901 9910 9913 9914 9915 9918 9919 9920 9922 9923 9924 9925 9926 9927 9928 9929 9930 9931 9932 9933 9934 9935 9936 9937 9938 9939 9940 9941 9942 9943 9944 9945 9946 9947 9948 9949 9950 9951 9952 9953 9957 9959', '\s')"/> - + + + - - - - + + + - - - + - + + + + diff --git a/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOLBIS-T115.xslt b/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOLBIS-T115.xslt index f27777e..1948c2c 100644 --- a/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOLBIS-T115.xslt +++ b/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOLBIS-T115.xslt @@ -608,13 +608,13 @@ - - + select="tokenize('0002 0007 0009 0037 0060 0088 0096 0097 0106 0130 0135 0142 0151 0183 0184 0188 0190 0191 0192 0193 0195 0196 0198 0199 0200 0201 0202 0204 0208 0209 0210 0211 0212 0213 0215 0216 0218 0221 0230 9901 9910 9913 9914 9915 9918 9919 9920 9922 9923 9924 9925 9926 9927 9928 9929 9930 9931 9932 9933 9934 9935 9936 9937 9938 9939 9940 9941 9942 9943 9944 9945 9946 9947 9948 9949 9950 9951 9952 9953 9957 9959', '\s')"/> + + + + + select="tokenize('0002 0007 0009 0037 0060 0088 0096 0097 0106 0130 0135 0142 0151 0183 0184 0188 0190 0191 0192 0193 0195 0196 0198 0199 0200 0201 0202 0204 0208 0209 0210 0211 0212 0213 0215 0216 0218 0221 0230 9901 9910 9913 9914 9915 9918 9919 9920 9922 9923 9924 9925 9926 9927 9928 9929 9930 9931 9932 9933 9934 9935 9936 9937 9938 9939 9940 9941 9942 9943 9944 9945 9946 9947 9948 9949 9950 9951 9952 9953 9957 9959', '\s')"/> - - - + - + diff --git a/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOLBIS-T16.xslt b/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOLBIS-T16.xslt index 7df365d..f17520e 100644 --- a/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOLBIS-T16.xslt +++ b/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOLBIS-T16.xslt @@ -598,24 +598,24 @@ + + + select="tokenize('0002 0007 0009 0037 0060 0088 0096 0097 0106 0130 0135 0142 0151 0183 0184 0188 0190 0191 0192 0193 0195 0196 0198 0199 0200 0201 0202 0204 0208 0209 0210 0211 0212 0213 0215 0216 0218 0221 0230 9901 9910 9913 9914 9915 9918 9919 9920 9922 9923 9924 9925 9926 9927 9928 9929 9930 9931 9932 9933 9934 9935 9936 9937 9938 9939 9940 9941 9942 9943 9944 9945 9946 9947 9948 9949 9950 9951 9952 9953 9957 9959', '\s')"/> + + + - - - - + - - - + diff --git a/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOLBIS-T19.xslt b/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOLBIS-T19.xslt index db9b6bd..cb208af 100644 --- a/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOLBIS-T19.xslt +++ b/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOLBIS-T19.xslt @@ -607,35 +607,35 @@ - - - - - - - + + + - - + + + - + + + + diff --git a/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOLBIS-T58.xslt b/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOLBIS-T58.xslt index 48b854b..f72c4c4 100644 --- a/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOLBIS-T58.xslt +++ b/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOLBIS-T58.xslt @@ -608,10 +608,10 @@ - + select="tokenize('0002 0007 0009 0037 0060 0088 0096 0097 0106 0130 0135 0142 0151 0183 0184 0188 0190 0191 0192 0193 0195 0196 0198 0199 0200 0201 0202 0204 0208 0209 0210 0211 0212 0213 0215 0216 0218 0221 0230 9901 9910 9913 9914 9915 9918 9919 9920 9922 9923 9924 9925 9926 9927 9928 9929 9930 9931 9932 9933 9934 9935 9936 9937 9938 9939 9940 9941 9942 9943 9944 9945 9946 9947 9948 9949 9950 9951 9952 9953 9957 9959', '\s')"/> + + select="tokenize('0002 0007 0009 0037 0060 0088 0096 0097 0106 0130 0135 0142 0151 0183 0184 0188 0190 0191 0192 0193 0195 0196 0198 0199 0200 0201 0202 0204 0208 0209 0210 0211 0212 0213 0215 0216 0218 0221 0230 9901 9910 9913 9914 9915 9918 9919 9920 9922 9923 9924 9925 9926 9927 9928 9929 9930 9931 9932 9933 9934 9935 9936 9937 9938 9939 9940 9941 9942 9943 9944 9945 9946 9947 9948 9949 9950 9951 9952 9953 9957 9959', '\s')"/> + - + + + select="tokenize('0002 0007 0009 0037 0060 0088 0096 0097 0106 0130 0135 0142 0151 0183 0184 0188 0190 0191 0192 0193 0195 0196 0198 0199 0200 0201 0202 0204 0208 0209 0210 0211 0212 0213 0215 0216 0218 0221 0230 9901 9910 9913 9914 9915 9918 9919 9920 9922 9923 9924 9925 9926 9927 9928 9929 9930 9931 9932 9933 9934 9935 9936 9937 9938 9939 9940 9941 9942 9943 9944 9945 9946 9947 9948 9949 9950 9951 9952 9953 9957 9959', '\s')"/> - - + - diff --git a/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOLBIS-T77.xslt b/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOLBIS-T77.xslt index a80463f..d9d697d 100644 --- a/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOLBIS-T77.xslt +++ b/src/main/resources/META-INF/Schematron/PEPPOL/PEPPOLBIS-T77.xslt @@ -598,25 +598,25 @@ + + + + select="tokenize('0002 0007 0009 0037 0060 0088 0096 0097 0106 0130 0135 0142 0151 0183 0184 0188 0190 0191 0192 0193 0195 0196 0198 0199 0200 0201 0202 0204 0208 0209 0210 0211 0212 0213 0215 0216 0218 0221 0230 9901 9910 9913 9914 9915 9918 9919 9920 9922 9923 9924 9925 9926 9927 9928 9929 9930 9931 9932 9933 9934 9935 9936 9937 9938 9939 9940 9941 9942 9943 9944 9945 9946 9947 9948 9949 9950 9951 9952 9953 9957 9959', '\s')"/> + - + - - - - diff --git a/src/test/java/dk/erst/oxalis/as4/handlers/NemhandelPersisterHandlerTest.java b/src/test/java/dk/erst/oxalis/as4/handlers/NemhandelPersisterHandlerTest.java index 9d82d44..3ecda0a 100644 --- a/src/test/java/dk/erst/oxalis/as4/handlers/NemhandelPersisterHandlerTest.java +++ b/src/test/java/dk/erst/oxalis/as4/handlers/NemhandelPersisterHandlerTest.java @@ -180,7 +180,7 @@ public class NemhandelPersisterHandlerTest { em.persist(accountReceiver); }); - InputStream inputStream = new ByteArrayInputStream(xml.getBytes()); + InputStream inputStream = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)); ParticipantIdentifier receiver = ParticipantIdentifier.of("C3"); ParticipantIdentifier sender = ParticipantIdentifier.of("C2"); Header header = Header.of(sender, receiver, null, null, null, null, null); diff --git a/src/test/java/dk/erst/oxalis/as4/reader/NemhandelReaderTest.java b/src/test/java/dk/erst/oxalis/as4/reader/NemhandelReaderTest.java index b27875c..6d76adc 100644 --- a/src/test/java/dk/erst/oxalis/as4/reader/NemhandelReaderTest.java +++ b/src/test/java/dk/erst/oxalis/as4/reader/NemhandelReaderTest.java @@ -254,8 +254,7 @@ public class NemhandelReaderTest { @Test public void NoExtensionInServiceMetadataCachesEmptyValues() throws Exception { - JAXBContext jaxbContext = JAXBContext.newInstance(ServiceGroupType.class, SignedServiceMetadataType.class, - ServiceMetadataType.class, EdelComponentVersionList.class); + JAXBContext jaxbContext = JAXBContext.newInstance(ServiceGroupType.class, ServiceMetadataType.class, EdelComponentVersionList.class); Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); FetcherResponse responseFromSMP = new FetcherResponse(getClass().getResourceAsStream("/smp-response-examples/busdox-servicemetadata-9908-12345678.xml"), null); @@ -264,7 +263,7 @@ public class NemhandelReaderTest { JAXBElement result = (JAXBElement) unmarshaller.unmarshal(new DOMSource(doc)); Object o = result.getValue(); - ServiceMetadataType serviceMetadataType = ((SignedServiceMetadataType) o).getServiceMetadata(); + ServiceMetadataType serviceMetadataType = (ServiceMetadataType) o; assertEquals(VersionCache.getComponentVersions().size(), 0); reader.getServiceMetadata(serviceMetadataType); assertEquals(VersionCache.getComponentVersions().size(), 2); diff --git a/src/test/resources/as2-peppol-bis-invoice-sbdh.xml b/src/test/resources/as2-peppol-bis-invoice-sbdh.xml old mode 100644 new mode 100755 diff --git a/src/test/resources/smp-response-examples/busdox-servicemetadata-9908-12345678.xml b/src/test/resources/smp-response-examples/busdox-servicemetadata-9908-12345678.xml index 46fd128..ff5b5ae 100644 --- a/src/test/resources/smp-response-examples/busdox-servicemetadata-9908-12345678.xml +++ b/src/test/resources/smp-response-examples/busdox-servicemetadata-9908-12345678.xml @@ -1,88 +1,134 @@ - - - - 9908:12345678 - - urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2::CreditNote##urn:www.cenbii.eu:transaction:biitrns014:ver2.0:extended:urn:www.peppol.eu:bis:peppol5a:ver2.0::2.1 - - - - urn:www.cenbii.eu:profile:bii05:ver2.0 - - - - https://test-api.basware.com/peppol/as2 - - - - false - 2024-01-01T23:59:59.000Z - - MIIFvzCCA6egAwIBAgIQBV7uSJlbeLx9GB1tBD9qhTANBgkqhkiG9w0BAQsFADBrMQswCQYDVQQG EwJCRTEZMBcGA1UEChMQT3BlblBFUFBPTCBBSVNCTDEWMBQGA1UECxMNRk9SIFRFU1QgT05MWTEp MCcGA1UEAxMgUEVQUE9MIEFDQ0VTUyBQT0lOVCBURVNUIENBIC0gRzIwHhcNMjAwOTE0MDAwMDAw WhcNMjIwOTA0MjM1OTU5WjBMMRIwEAYDVQQDDAlQT1AwMDAwMTAxFzAVBgNVBAsMDlBFUFBPTCBU RVNUIEFQMRAwDgYDVQQKDAdCYXN3YXJlMQswCQYDVQQGEwJGSTCCASIwDQYJKoZIhvcNAQEBBQAD ggEPADCCAQoCggEBALMwQO726jn0C2sQq8e16KVt/NRUaj5l/gOtfL0y0hEAQNRM8oM2E+LN0Gzn 4T7sLOEgcgOrnw0Sx9PBhosJjeHQmp4FxXBcTdfhpe6jeBG7oK4r4qO+d4qnssm3tW25k75o/Ocy baqWX6o9oEL9F4hMJ/cR53eINZuy/WlblEZSPrSCWhu2jH3fikbQ0yBbyneU1EQgtGYvnokg7pxy gLBnbWgpOkrPH43Ug0t+4hromsHYMo/FKs2ATxP1aKCCxzrlytSu60mhu5ZRYS+RwIP0vGQ6yb2G 7eb1r7xEWXfg0wcZMqTjs0t3jX206rSbMqcdIGpL7jibsZU0gXZkcxECAwEAAaOCAXwwggF4MAwG A1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgOoMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMCMB0GA1Ud DgQWBBSsoxuqualBzUgqSE3y9J1N0QDQ5zBdBgNVHR8EVjBUMFKgUKBOhkxodHRwOi8vcGtpLWNy bC5zeW1hdXRoLmNvbS9jYV82YTkzNzczNGEzOTNhMDgwNWJmMzNjZGE4YjMzMTA5My9MYXRlc3RD UkwuY3JsMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL3BraS1vY3NwLnN5bWF1 dGguY29tMB8GA1UdIwQYMBaAFGtvS7bxN7orPH8Yzborsrl8KjfrMC0GCmCGSAGG+EUBEAMEHzAd BhNghkgBhvhFARABAgMBAYGpkOEDFgY5NTc2MDgwOQYKYIZIAYb4RQEQBQQrMCkCAQAWJGFIUjBj SE02THk5d2Eya3RjbUV1YzNsdFlYVjBhQzVqYjIwPTANBgkqhkiG9w0BAQsFAAOCAgEAFJpFt3s5 T6Sjz3/yJs+6eAVuIr6lNGx7jLHeCIKHp/l4Y8KyiQPBcfP5PCo74+2HuCexEBzt7xAkwpRwo1aR VTFAr8QZlWHo4OOf7Ah5aQcbVFFSv0epTEdwNbgwWROCdD/BYEWWNPLboELeMY58LV8R2qlaglWx Fg2Ezn8/cqOZE2u/2Rp3djRW7eSvjy+XmOIBRQcgcKNRXebwPaUTq2W7TebWKwpj4E6EtFFTxGfg 41EQub5HH4HWpIvwOMzVNLj0iocAR5VWGedYjzQ4GryGM/1bml1Y1IN2EGgytZxUng8XEy8XIMey tOBbBEHLcwWPGOmES/wmod1qiEABx570S+a9ZOaNU01y1thCC6rkhLyeIOQKBm7z3d4+RqHRAIpr mldxzKS2cOQloJyczp6yBjhh2F5mzmWapDvGGsY7FnBPu+5ylk9GvuN3+pRHrzNxF+6GrH2qFqH3 CPafxQQAc/+sNFHD1bCGT4XeD5eeWhq+UEXBOfCzb7d4rXlftMAA4P8E1UnfwGzG0Qev5NGE4y0R 0BPMnCHLsFahCMO9ddnVBqjNqgvxquddtCC54d17zg6VNc5wz6wIzHdeslg20lmsv7MOrlvWAX2P 4HuZiKz3o/R7oJeqsg1jJBnyUK2ln37oqwkkYN8daHYH2LOb15YNBa6KUq6TOHrZSGE= - - Basware PEPPOL Test Access Point - arun.kumar@basware.com - - - - https://test-api.basware.com/peppol/as2 - - - - false - 2022-01-01T00:00:00.000Z - - MIIFvzCCA6egAwIBAgIQBV7uSJlbeLx9GB1tBD9qhTANBgkqhkiG9w0BAQsFADBrMQswCQYDVQQG EwJCRTEZMBcGA1UEChMQT3BlblBFUFBPTCBBSVNCTDEWMBQGA1UECxMNRk9SIFRFU1QgT05MWTEp MCcGA1UEAxMgUEVQUE9MIEFDQ0VTUyBQT0lOVCBURVNUIENBIC0gRzIwHhcNMjAwOTE0MDAwMDAw WhcNMjIwOTA0MjM1OTU5WjBMMRIwEAYDVQQDDAlQT1AwMDAwMTAxFzAVBgNVBAsMDlBFUFBPTCBU RVNUIEFQMRAwDgYDVQQKDAdCYXN3YXJlMQswCQYDVQQGEwJGSTCCASIwDQYJKoZIhvcNAQEBBQAD ggEPADCCAQoCggEBALMwQO726jn0C2sQq8e16KVt/NRUaj5l/gOtfL0y0hEAQNRM8oM2E+LN0Gzn 4T7sLOEgcgOrnw0Sx9PBhosJjeHQmp4FxXBcTdfhpe6jeBG7oK4r4qO+d4qnssm3tW25k75o/Ocy baqWX6o9oEL9F4hMJ/cR53eINZuy/WlblEZSPrSCWhu2jH3fikbQ0yBbyneU1EQgtGYvnokg7pxy gLBnbWgpOkrPH43Ug0t+4hromsHYMo/FKs2ATxP1aKCCxzrlytSu60mhu5ZRYS+RwIP0vGQ6yb2G 7eb1r7xEWXfg0wcZMqTjs0t3jX206rSbMqcdIGpL7jibsZU0gXZkcxECAwEAAaOCAXwwggF4MAwG A1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgOoMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMCMB0GA1Ud DgQWBBSsoxuqualBzUgqSE3y9J1N0QDQ5zBdBgNVHR8EVjBUMFKgUKBOhkxodHRwOi8vcGtpLWNy bC5zeW1hdXRoLmNvbS9jYV82YTkzNzczNGEzOTNhMDgwNWJmMzNjZGE4YjMzMTA5My9MYXRlc3RD UkwuY3JsMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL3BraS1vY3NwLnN5bWF1 dGguY29tMB8GA1UdIwQYMBaAFGtvS7bxN7orPH8Yzborsrl8KjfrMC0GCmCGSAGG+EUBEAMEHzAd BhNghkgBhvhFARABAgMBAYGpkOEDFgY5NTc2MDgwOQYKYIZIAYb4RQEQBQQrMCkCAQAWJGFIUjBj SE02THk5d2Eya3RjbUV1YzNsdFlYVjBhQzVqYjIwPTANBgkqhkiG9w0BAQsFAAOCAgEAFJpFt3s5 T6Sjz3/yJs+6eAVuIr6lNGx7jLHeCIKHp/l4Y8KyiQPBcfP5PCo74+2HuCexEBzt7xAkwpRwo1aR VTFAr8QZlWHo4OOf7Ah5aQcbVFFSv0epTEdwNbgwWROCdD/BYEWWNPLboELeMY58LV8R2qlaglWx Fg2Ezn8/cqOZE2u/2Rp3djRW7eSvjy+XmOIBRQcgcKNRXebwPaUTq2W7TebWKwpj4E6EtFFTxGfg 41EQub5HH4HWpIvwOMzVNLj0iocAR5VWGedYjzQ4GryGM/1bml1Y1IN2EGgytZxUng8XEy8XIMey tOBbBEHLcwWPGOmES/wmod1qiEABx570S+a9ZOaNU01y1thCC6rkhLyeIOQKBm7z3d4+RqHRAIpr mldxzKS2cOQloJyczp6yBjhh2F5mzmWapDvGGsY7FnBPu+5ylk9GvuN3+pRHrzNxF+6GrH2qFqH3 CPafxQQAc/+sNFHD1bCGT4XeD5eeWhq+UEXBOfCzb7d4rXlftMAA4P8E1UnfwGzG0Qev5NGE4y0R 0BPMnCHLsFahCMO9ddnVBqjNqgvxquddtCC54d17zg6VNc5wz6wIzHdeslg20lmsv7MOrlvWAX2P 4HuZiKz3o/R7oJeqsg1jJBnyUK2ln37oqwkkYN8daHYH2LOb15YNBa6KUq6TOHrZSGE= - - Basware PEPPOL Test Access Point - arun.kumar@basware.com - - - - https://test-api.basware.com/peppol/as4 - - - - false - - MIIFvzCCA6egAwIBAgIQBV7uSJlbeLx9GB1tBD9qhTANBgkqhkiG9w0BAQsFADBrMQswCQYDVQQG EwJCRTEZMBcGA1UEChMQT3BlblBFUFBPTCBBSVNCTDEWMBQGA1UECxMNRk9SIFRFU1QgT05MWTEp MCcGA1UEAxMgUEVQUE9MIEFDQ0VTUyBQT0lOVCBURVNUIENBIC0gRzIwHhcNMjAwOTE0MDAwMDAw WhcNMjIwOTA0MjM1OTU5WjBMMRIwEAYDVQQDDAlQT1AwMDAwMTAxFzAVBgNVBAsMDlBFUFBPTCBU RVNUIEFQMRAwDgYDVQQKDAdCYXN3YXJlMQswCQYDVQQGEwJGSTCCASIwDQYJKoZIhvcNAQEBBQAD ggEPADCCAQoCggEBALMwQO726jn0C2sQq8e16KVt/NRUaj5l/gOtfL0y0hEAQNRM8oM2E+LN0Gzn 4T7sLOEgcgOrnw0Sx9PBhosJjeHQmp4FxXBcTdfhpe6jeBG7oK4r4qO+d4qnssm3tW25k75o/Ocy baqWX6o9oEL9F4hMJ/cR53eINZuy/WlblEZSPrSCWhu2jH3fikbQ0yBbyneU1EQgtGYvnokg7pxy gLBnbWgpOkrPH43Ug0t+4hromsHYMo/FKs2ATxP1aKCCxzrlytSu60mhu5ZRYS+RwIP0vGQ6yb2G 7eb1r7xEWXfg0wcZMqTjs0t3jX206rSbMqcdIGpL7jibsZU0gXZkcxECAwEAAaOCAXwwggF4MAwG A1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgOoMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMCMB0GA1Ud DgQWBBSsoxuqualBzUgqSE3y9J1N0QDQ5zBdBgNVHR8EVjBUMFKgUKBOhkxodHRwOi8vcGtpLWNy bC5zeW1hdXRoLmNvbS9jYV82YTkzNzczNGEzOTNhMDgwNWJmMzNjZGE4YjMzMTA5My9MYXRlc3RD UkwuY3JsMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL3BraS1vY3NwLnN5bWF1 dGguY29tMB8GA1UdIwQYMBaAFGtvS7bxN7orPH8Yzborsrl8KjfrMC0GCmCGSAGG+EUBEAMEHzAd BhNghkgBhvhFARABAgMBAYGpkOEDFgY5NTc2MDgwOQYKYIZIAYb4RQEQBQQrMCkCAQAWJGFIUjBj SE02THk5d2Eya3RjbUV1YzNsdFlYVjBhQzVqYjIwPTANBgkqhkiG9w0BAQsFAAOCAgEAFJpFt3s5 T6Sjz3/yJs+6eAVuIr6lNGx7jLHeCIKHp/l4Y8KyiQPBcfP5PCo74+2HuCexEBzt7xAkwpRwo1aR VTFAr8QZlWHo4OOf7Ah5aQcbVFFSv0epTEdwNbgwWROCdD/BYEWWNPLboELeMY58LV8R2qlaglWx Fg2Ezn8/cqOZE2u/2Rp3djRW7eSvjy+XmOIBRQcgcKNRXebwPaUTq2W7TebWKwpj4E6EtFFTxGfg 41EQub5HH4HWpIvwOMzVNLj0iocAR5VWGedYjzQ4GryGM/1bml1Y1IN2EGgytZxUng8XEy8XIMey tOBbBEHLcwWPGOmES/wmod1qiEABx570S+a9ZOaNU01y1thCC6rkhLyeIOQKBm7z3d4+RqHRAIpr mldxzKS2cOQloJyczp6yBjhh2F5mzmWapDvGGsY7FnBPu+5ylk9GvuN3+pRHrzNxF+6GrH2qFqH3 CPafxQQAc/+sNFHD1bCGT4XeD5eeWhq+UEXBOfCzb7d4rXlftMAA4P8E1UnfwGzG0Qev5NGE4y0R 0BPMnCHLsFahCMO9ddnVBqjNqgvxquddtCC54d17zg6VNc5wz6wIzHdeslg20lmsv7MOrlvWAX2P 4HuZiKz3o/R7oJeqsg1jJBnyUK2ln37oqwkkYN8daHYH2LOb15YNBa6KUq6TOHrZSGE= - - Basware PEPPOL Test Access Point - arun.kumar@basware.com - - - - - - -HougVCnYTKRrdjC+OyIPwrh5nac=hMLZJ/pp6SqJQOiUn+14LW/f8s5QZbyNgzlVfKC2n2jAh5+eg8rz7Nsgb6M9GD2dClBLLnPM1RDe -ot2ij0db3/aEI2rbcRiElU7Ps4J+UrbngMOl/W3jpqqBZZZFuFhHCuPUT7dtLTaXoh8JNYWgqbAh -F9tdaWTCaz+kWc8HGgMs9bEDfzl7pQ2ZGszqtqaZH9jEmaEoqbdLztQXXeVQby5hgSoTwWnATSat -XuOpW1mo5DLJyh/CSV2vBz/vhOzdJGbg/vfIaWA8Aczr9VsEZ10iXRpmbkXEUksIyXvPjE9KO0ff -11YmdADRa350ACfT4a0avKlPXhc3AL0S9fH37A==CN=POP000010,OU=PEPPOL TEST SMP,O=Basware,C=FIMIIFzjCCA7agAwIBAgIQBkgalZBvVfIgaiKKlFOrVzANBgkqhkiG9w0BAQsFADB5MQswCQYDVQQG -EwJCRTEZMBcGA1UEChMQT3BlblBFUFBPTCBBSVNCTDEWMBQGA1UECxMNRk9SIFRFU1QgT05MWTE3 -MDUGA1UEAxMuUEVQUE9MIFNFUlZJQ0UgTUVUQURBVEEgUFVCTElTSEVSIFRFU1QgQ0EgLSBHMjAe -Fw0yMTA5MjkwMDAwMDBaFw0yMzA5MTkyMzU5NTlaME0xCzAJBgNVBAYTAkZJMRAwDgYDVQQKDAdC -YXN3YXJlMRgwFgYDVQQLDA9QRVBQT0wgVEVTVCBTTVAxEjAQBgNVBAMMCVBPUDAwMDAxMDCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANBe7sa9qbfgxdFlTlCf8pU/6C4nkpPHX7/SVUNV -LeosfCx0zC0nJ7HuyDbngCNmw8UM/OAKpre5YUrDTxJ9MZVVdANaxNrcJnLObXwKSB5inC7c4SOC -u1HrwHkWee/MTy0gQWpxpuBpoUXPp7nW6Qss0sCm0566uorTJ9Ud3A2KD88qZ7VOMuk8ABf5qWvU -v0ORLFFM8unaR44A5Zj1IzJkJFmaGpkArCfSdNqtMUAq6rMZn/NDf3HK8jKx/zIqwUaefRjQYMLi -OvzZad8Dy18HRtoxNZx0WMJi0gVhq7WCnF5k8LP7vz7xDqiKd1J/43Pvrxvb3iuXRHgPij6cLlkC -AwEAAaOCAXwwggF4MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgOoMBYGA1UdJQEB/wQMMAoG -CCsGAQUFBwMCMB0GA1UdDgQWBBQM3c5oM9tmeOCIlBi8ADAt0DWBtTBdBgNVHR8EVjBUMFKgUKBO -hkxodHRwOi8vcGtpLWNybC5zeW1hdXRoLmNvbS9jYV9iNmQwZGMxZGMzMTQ3NzIzZmUzNmI3NTc1 -OTk3YWZjNC9MYXRlc3RDUkwuY3JsMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDov -L3BraS1vY3NwLnN5bWF1dGguY29tMB8GA1UdIwQYMBaAFHwdskjxutkKBsoWY6nwek+9I517MC0G -CmCGSAGG+EUBEAMEHzAdBhNghkgBhvhFARABAgMBAYGo1YEKFgY5NTc2MDgwOQYKYIZIAYb4RQEQ -BQQrMCkCAQAWJGFIUjBjSE02THk5d2Eya3RjbUV1YzNsdFlYVjBhQzVqYjIwPTANBgkqhkiG9w0B -AQsFAAOCAgEAMwO2ma+VhM4U6cOlhWwU4K3yG9W5HuVpM8/G4oQhaV+rkgtQHd0Z2pKWpLSA0nkH -7PRqyF+nix/aSlSMtx58lUMr2IWfdCFNxWi3SCo5/U2g7f/T42yqQvTttYiOChFbsL1GLoMINa+G -CuRVXH0qTPTiLF9ZjAWp0RrcagiX4cLe8F0vhkF4Ledh+mbscshWyK5UPic4aEvsFUuAjvQJqubA -F5er+cJAbna43btd4edRASL4MrOEB7nQUgKUeL35gW/H0VGiyDOAeV8yhGCGUszKtpgncoYN0TWa -AUz+6zPYfryZ29+BkP0Z+nzDaoxDPnA580NDBhain+yY9zamACNjsgvtHFbdpfW9MhTLeWxY5c1t -gM5ad92bblpG/sjXm6RY2MLwon4Iq4KAuV29K1ZqCoqnL5qPx/TGRs4yWZ+d88g6JUE039kGt6PO -mQldoLE2YNTEVwoycQ1gNnd1qk5cWgQBZmr8C13OwCFTWTF53xV8Sfofqs9bIpPjmAhgB9MyWU5z -vL9QzF8sIHN9w7QBsD1YzicINCdJqLsm6Uhr50AW2dqrClXzwE3n0tSbWd0UzrqFXX53hw9pdy0v -JkRan9RF/VQ+o+CSbVdYP4jhNjJxQpDDFmbLP3Zk/FlzsGIGnGUIdDqOA3eS3o5UFdABabQaESr2 -XRkcaY8tJvY= \ No newline at end of file + + + + 9908:12345678 + + urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2::CreditNote##urn:www.cenbii.eu:transaction:biitrns014:ver2.0:extended:urn:www.peppol.eu:bis:peppol5a:ver2.0::2.1 + + + + urn:www.cenbii.eu:profile:bii05:ver2.0 + + + + https://test-api.basware.com/peppol/as2 + + + + false + 2026-01-01T23:59:59.000Z + + MIIFvzCCA6egAwIBAgIQBV7uSJlbeLx9GB1tBD9qhTANBgkqhkiG9w0BAQsFADBrMQswCQYDVQQG + EwJCRTEZMBcGA1UEChMQT3BlblBFUFBPTCBBSVNCTDEWMBQGA1UECxMNRk9SIFRFU1QgT05MWTEp + MCcGA1UEAxMgUEVQUE9MIEFDQ0VTUyBQT0lOVCBURVNUIENBIC0gRzIwHhcNMjAwOTE0MDAwMDAw + WhcNMjIwOTA0MjM1OTU5WjBMMRIwEAYDVQQDDAlQT1AwMDAwMTAxFzAVBgNVBAsMDlBFUFBPTCBU + RVNUIEFQMRAwDgYDVQQKDAdCYXN3YXJlMQswCQYDVQQGEwJGSTCCASIwDQYJKoZIhvcNAQEBBQAD + ggEPADCCAQoCggEBALMwQO726jn0C2sQq8e16KVt/NRUaj5l/gOtfL0y0hEAQNRM8oM2E+LN0Gzn + 4T7sLOEgcgOrnw0Sx9PBhosJjeHQmp4FxXBcTdfhpe6jeBG7oK4r4qO+d4qnssm3tW25k75o/Ocy + baqWX6o9oEL9F4hMJ/cR53eINZuy/WlblEZSPrSCWhu2jH3fikbQ0yBbyneU1EQgtGYvnokg7pxy + gLBnbWgpOkrPH43Ug0t+4hromsHYMo/FKs2ATxP1aKCCxzrlytSu60mhu5ZRYS+RwIP0vGQ6yb2G + 7eb1r7xEWXfg0wcZMqTjs0t3jX206rSbMqcdIGpL7jibsZU0gXZkcxECAwEAAaOCAXwwggF4MAwG + A1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgOoMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMCMB0GA1Ud + DgQWBBSsoxuqualBzUgqSE3y9J1N0QDQ5zBdBgNVHR8EVjBUMFKgUKBOhkxodHRwOi8vcGtpLWNy + bC5zeW1hdXRoLmNvbS9jYV82YTkzNzczNGEzOTNhMDgwNWJmMzNjZGE4YjMzMTA5My9MYXRlc3RD + UkwuY3JsMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL3BraS1vY3NwLnN5bWF1 + dGguY29tMB8GA1UdIwQYMBaAFGtvS7bxN7orPH8Yzborsrl8KjfrMC0GCmCGSAGG+EUBEAMEHzAd + BhNghkgBhvhFARABAgMBAYGpkOEDFgY5NTc2MDgwOQYKYIZIAYb4RQEQBQQrMCkCAQAWJGFIUjBj + SE02THk5d2Eya3RjbUV1YzNsdFlYVjBhQzVqYjIwPTANBgkqhkiG9w0BAQsFAAOCAgEAFJpFt3s5 + T6Sjz3/yJs+6eAVuIr6lNGx7jLHeCIKHp/l4Y8KyiQPBcfP5PCo74+2HuCexEBzt7xAkwpRwo1aR + VTFAr8QZlWHo4OOf7Ah5aQcbVFFSv0epTEdwNbgwWROCdD/BYEWWNPLboELeMY58LV8R2qlaglWx + Fg2Ezn8/cqOZE2u/2Rp3djRW7eSvjy+XmOIBRQcgcKNRXebwPaUTq2W7TebWKwpj4E6EtFFTxGfg + 41EQub5HH4HWpIvwOMzVNLj0iocAR5VWGedYjzQ4GryGM/1bml1Y1IN2EGgytZxUng8XEy8XIMey + tOBbBEHLcwWPGOmES/wmod1qiEABx570S+a9ZOaNU01y1thCC6rkhLyeIOQKBm7z3d4+RqHRAIpr + mldxzKS2cOQloJyczp6yBjhh2F5mzmWapDvGGsY7FnBPu+5ylk9GvuN3+pRHrzNxF+6GrH2qFqH3 + CPafxQQAc/+sNFHD1bCGT4XeD5eeWhq+UEXBOfCzb7d4rXlftMAA4P8E1UnfwGzG0Qev5NGE4y0R + 0BPMnCHLsFahCMO9ddnVBqjNqgvxquddtCC54d17zg6VNc5wz6wIzHdeslg20lmsv7MOrlvWAX2P + 4HuZiKz3o/R7oJeqsg1jJBnyUK2ln37oqwkkYN8daHYH2LOb15YNBa6KUq6TOHrZSGE= + + Basware PEPPOL Test Access Point + arun.kumar@basware.com + + + + https://test-api.basware.com/peppol/as2 + + + + false + 2022-01-01T00:00:00.000Z + + MIIFvzCCA6egAwIBAgIQBV7uSJlbeLx9GB1tBD9qhTANBgkqhkiG9w0BAQsFADBrMQswCQYDVQQG + EwJCRTEZMBcGA1UEChMQT3BlblBFUFBPTCBBSVNCTDEWMBQGA1UECxMNRk9SIFRFU1QgT05MWTEp + MCcGA1UEAxMgUEVQUE9MIEFDQ0VTUyBQT0lOVCBURVNUIENBIC0gRzIwHhcNMjAwOTE0MDAwMDAw + WhcNMjIwOTA0MjM1OTU5WjBMMRIwEAYDVQQDDAlQT1AwMDAwMTAxFzAVBgNVBAsMDlBFUFBPTCBU + RVNUIEFQMRAwDgYDVQQKDAdCYXN3YXJlMQswCQYDVQQGEwJGSTCCASIwDQYJKoZIhvcNAQEBBQAD + ggEPADCCAQoCggEBALMwQO726jn0C2sQq8e16KVt/NRUaj5l/gOtfL0y0hEAQNRM8oM2E+LN0Gzn + 4T7sLOEgcgOrnw0Sx9PBhosJjeHQmp4FxXBcTdfhpe6jeBG7oK4r4qO+d4qnssm3tW25k75o/Ocy + baqWX6o9oEL9F4hMJ/cR53eINZuy/WlblEZSPrSCWhu2jH3fikbQ0yBbyneU1EQgtGYvnokg7pxy + gLBnbWgpOkrPH43Ug0t+4hromsHYMo/FKs2ATxP1aKCCxzrlytSu60mhu5ZRYS+RwIP0vGQ6yb2G + 7eb1r7xEWXfg0wcZMqTjs0t3jX206rSbMqcdIGpL7jibsZU0gXZkcxECAwEAAaOCAXwwggF4MAwG + A1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgOoMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMCMB0GA1Ud + DgQWBBSsoxuqualBzUgqSE3y9J1N0QDQ5zBdBgNVHR8EVjBUMFKgUKBOhkxodHRwOi8vcGtpLWNy + bC5zeW1hdXRoLmNvbS9jYV82YTkzNzczNGEzOTNhMDgwNWJmMzNjZGE4YjMzMTA5My9MYXRlc3RD + UkwuY3JsMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL3BraS1vY3NwLnN5bWF1 + dGguY29tMB8GA1UdIwQYMBaAFGtvS7bxN7orPH8Yzborsrl8KjfrMC0GCmCGSAGG+EUBEAMEHzAd + BhNghkgBhvhFARABAgMBAYGpkOEDFgY5NTc2MDgwOQYKYIZIAYb4RQEQBQQrMCkCAQAWJGFIUjBj + SE02THk5d2Eya3RjbUV1YzNsdFlYVjBhQzVqYjIwPTANBgkqhkiG9w0BAQsFAAOCAgEAFJpFt3s5 + T6Sjz3/yJs+6eAVuIr6lNGx7jLHeCIKHp/l4Y8KyiQPBcfP5PCo74+2HuCexEBzt7xAkwpRwo1aR + VTFAr8QZlWHo4OOf7Ah5aQcbVFFSv0epTEdwNbgwWROCdD/BYEWWNPLboELeMY58LV8R2qlaglWx + Fg2Ezn8/cqOZE2u/2Rp3djRW7eSvjy+XmOIBRQcgcKNRXebwPaUTq2W7TebWKwpj4E6EtFFTxGfg + 41EQub5HH4HWpIvwOMzVNLj0iocAR5VWGedYjzQ4GryGM/1bml1Y1IN2EGgytZxUng8XEy8XIMey + tOBbBEHLcwWPGOmES/wmod1qiEABx570S+a9ZOaNU01y1thCC6rkhLyeIOQKBm7z3d4+RqHRAIpr + mldxzKS2cOQloJyczp6yBjhh2F5mzmWapDvGGsY7FnBPu+5ylk9GvuN3+pRHrzNxF+6GrH2qFqH3 + CPafxQQAc/+sNFHD1bCGT4XeD5eeWhq+UEXBOfCzb7d4rXlftMAA4P8E1UnfwGzG0Qev5NGE4y0R + 0BPMnCHLsFahCMO9ddnVBqjNqgvxquddtCC54d17zg6VNc5wz6wIzHdeslg20lmsv7MOrlvWAX2P + 4HuZiKz3o/R7oJeqsg1jJBnyUK2ln37oqwkkYN8daHYH2LOb15YNBa6KUq6TOHrZSGE= + + Basware PEPPOL Test Access Point + arun.kumar@basware.com + + + + https://test-api.basware.com/peppol/as4 + + + + false + + MIIFvzCCA6egAwIBAgIQBV7uSJlbeLx9GB1tBD9qhTANBgkqhkiG9w0BAQsFADBrMQswCQYDVQQG + EwJCRTEZMBcGA1UEChMQT3BlblBFUFBPTCBBSVNCTDEWMBQGA1UECxMNRk9SIFRFU1QgT05MWTEp + MCcGA1UEAxMgUEVQUE9MIEFDQ0VTUyBQT0lOVCBURVNUIENBIC0gRzIwHhcNMjAwOTE0MDAwMDAw + WhcNMjIwOTA0MjM1OTU5WjBMMRIwEAYDVQQDDAlQT1AwMDAwMTAxFzAVBgNVBAsMDlBFUFBPTCBU + RVNUIEFQMRAwDgYDVQQKDAdCYXN3YXJlMQswCQYDVQQGEwJGSTCCASIwDQYJKoZIhvcNAQEBBQAD + ggEPADCCAQoCggEBALMwQO726jn0C2sQq8e16KVt/NRUaj5l/gOtfL0y0hEAQNRM8oM2E+LN0Gzn + 4T7sLOEgcgOrnw0Sx9PBhosJjeHQmp4FxXBcTdfhpe6jeBG7oK4r4qO+d4qnssm3tW25k75o/Ocy + baqWX6o9oEL9F4hMJ/cR53eINZuy/WlblEZSPrSCWhu2jH3fikbQ0yBbyneU1EQgtGYvnokg7pxy + gLBnbWgpOkrPH43Ug0t+4hromsHYMo/FKs2ATxP1aKCCxzrlytSu60mhu5ZRYS+RwIP0vGQ6yb2G + 7eb1r7xEWXfg0wcZMqTjs0t3jX206rSbMqcdIGpL7jibsZU0gXZkcxECAwEAAaOCAXwwggF4MAwG + A1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgOoMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMCMB0GA1Ud + DgQWBBSsoxuqualBzUgqSE3y9J1N0QDQ5zBdBgNVHR8EVjBUMFKgUKBOhkxodHRwOi8vcGtpLWNy + bC5zeW1hdXRoLmNvbS9jYV82YTkzNzczNGEzOTNhMDgwNWJmMzNjZGE4YjMzMTA5My9MYXRlc3RD + UkwuY3JsMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL3BraS1vY3NwLnN5bWF1 + dGguY29tMB8GA1UdIwQYMBaAFGtvS7bxN7orPH8Yzborsrl8KjfrMC0GCmCGSAGG+EUBEAMEHzAd + BhNghkgBhvhFARABAgMBAYGpkOEDFgY5NTc2MDgwOQYKYIZIAYb4RQEQBQQrMCkCAQAWJGFIUjBj + SE02THk5d2Eya3RjbUV1YzNsdFlYVjBhQzVqYjIwPTANBgkqhkiG9w0BAQsFAAOCAgEAFJpFt3s5 + T6Sjz3/yJs+6eAVuIr6lNGx7jLHeCIKHp/l4Y8KyiQPBcfP5PCo74+2HuCexEBzt7xAkwpRwo1aR + VTFAr8QZlWHo4OOf7Ah5aQcbVFFSv0epTEdwNbgwWROCdD/BYEWWNPLboELeMY58LV8R2qlaglWx + Fg2Ezn8/cqOZE2u/2Rp3djRW7eSvjy+XmOIBRQcgcKNRXebwPaUTq2W7TebWKwpj4E6EtFFTxGfg + 41EQub5HH4HWpIvwOMzVNLj0iocAR5VWGedYjzQ4GryGM/1bml1Y1IN2EGgytZxUng8XEy8XIMey + tOBbBEHLcwWPGOmES/wmod1qiEABx570S+a9ZOaNU01y1thCC6rkhLyeIOQKBm7z3d4+RqHRAIpr + mldxzKS2cOQloJyczp6yBjhh2F5mzmWapDvGGsY7FnBPu+5ylk9GvuN3+pRHrzNxF+6GrH2qFqH3 + CPafxQQAc/+sNFHD1bCGT4XeD5eeWhq+UEXBOfCzb7d4rXlftMAA4P8E1UnfwGzG0Qev5NGE4y0R + 0BPMnCHLsFahCMO9ddnVBqjNqgvxquddtCC54d17zg6VNc5wz6wIzHdeslg20lmsv7MOrlvWAX2P + 4HuZiKz3o/R7oJeqsg1jJBnyUK2ln37oqwkkYN8daHYH2LOb15YNBa6KUq6TOHrZSGE= + + Basware PEPPOL Test Access Point + arun.kumar@basware.com + + + + + + \ No newline at end of file diff --git a/src/test/resources/smp-response-examples/busdox-servicemetadata-9908-923829644.xml b/src/test/resources/smp-response-examples/busdox-servicemetadata-9908-923829644.xml index d4a9d0d..9b8bece 100644 --- a/src/test/resources/smp-response-examples/busdox-servicemetadata-9908-923829644.xml +++ b/src/test/resources/smp-response-examples/busdox-servicemetadata-9908-923829644.xml @@ -1,92 +1,138 @@ - - - - 9908:923829644 - - urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2::CreditNote##urn:www.cenbii.eu:transaction:biitrns014:ver2.0:extended:urn:www.peppol.eu:bis:peppol5a:ver2.0::2.1 - - - - urn:www.cenbii.eu:profile:bii05:ver2.0 - - - - https://test-api.basware.com/peppol/as2 - - - - false - 2020-01-01T00:00:00.000Z - 2022-01-01T23:59:59.000Z - - MIIFvzCCA6egAwIBAgIQBV7uSJlbeLx9GB1tBD9qhTANBgkqhkiG9w0BAQsFADBrMQswCQYDVQQG EwJCRTEZMBcGA1UEChMQT3BlblBFUFBPTCBBSVNCTDEWMBQGA1UECxMNRk9SIFRFU1QgT05MWTEp MCcGA1UEAxMgUEVQUE9MIEFDQ0VTUyBQT0lOVCBURVNUIENBIC0gRzIwHhcNMjAwOTE0MDAwMDAw WhcNMjIwOTA0MjM1OTU5WjBMMRIwEAYDVQQDDAlQT1AwMDAwMTAxFzAVBgNVBAsMDlBFUFBPTCBU RVNUIEFQMRAwDgYDVQQKDAdCYXN3YXJlMQswCQYDVQQGEwJGSTCCASIwDQYJKoZIhvcNAQEBBQAD ggEPADCCAQoCggEBALMwQO726jn0C2sQq8e16KVt/NRUaj5l/gOtfL0y0hEAQNRM8oM2E+LN0Gzn 4T7sLOEgcgOrnw0Sx9PBhosJjeHQmp4FxXBcTdfhpe6jeBG7oK4r4qO+d4qnssm3tW25k75o/Ocy baqWX6o9oEL9F4hMJ/cR53eINZuy/WlblEZSPrSCWhu2jH3fikbQ0yBbyneU1EQgtGYvnokg7pxy gLBnbWgpOkrPH43Ug0t+4hromsHYMo/FKs2ATxP1aKCCxzrlytSu60mhu5ZRYS+RwIP0vGQ6yb2G 7eb1r7xEWXfg0wcZMqTjs0t3jX206rSbMqcdIGpL7jibsZU0gXZkcxECAwEAAaOCAXwwggF4MAwG A1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgOoMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMCMB0GA1Ud DgQWBBSsoxuqualBzUgqSE3y9J1N0QDQ5zBdBgNVHR8EVjBUMFKgUKBOhkxodHRwOi8vcGtpLWNy bC5zeW1hdXRoLmNvbS9jYV82YTkzNzczNGEzOTNhMDgwNWJmMzNjZGE4YjMzMTA5My9MYXRlc3RD UkwuY3JsMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL3BraS1vY3NwLnN5bWF1 dGguY29tMB8GA1UdIwQYMBaAFGtvS7bxN7orPH8Yzborsrl8KjfrMC0GCmCGSAGG+EUBEAMEHzAd BhNghkgBhvhFARABAgMBAYGpkOEDFgY5NTc2MDgwOQYKYIZIAYb4RQEQBQQrMCkCAQAWJGFIUjBj SE02THk5d2Eya3RjbUV1YzNsdFlYVjBhQzVqYjIwPTANBgkqhkiG9w0BAQsFAAOCAgEAFJpFt3s5 T6Sjz3/yJs+6eAVuIr6lNGx7jLHeCIKHp/l4Y8KyiQPBcfP5PCo74+2HuCexEBzt7xAkwpRwo1aR VTFAr8QZlWHo4OOf7Ah5aQcbVFFSv0epTEdwNbgwWROCdD/BYEWWNPLboELeMY58LV8R2qlaglWx Fg2Ezn8/cqOZE2u/2Rp3djRW7eSvjy+XmOIBRQcgcKNRXebwPaUTq2W7TebWKwpj4E6EtFFTxGfg 41EQub5HH4HWpIvwOMzVNLj0iocAR5VWGedYjzQ4GryGM/1bml1Y1IN2EGgytZxUng8XEy8XIMey tOBbBEHLcwWPGOmES/wmod1qiEABx570S+a9ZOaNU01y1thCC6rkhLyeIOQKBm7z3d4+RqHRAIpr mldxzKS2cOQloJyczp6yBjhh2F5mzmWapDvGGsY7FnBPu+5ylk9GvuN3+pRHrzNxF+6GrH2qFqH3 CPafxQQAc/+sNFHD1bCGT4XeD5eeWhq+UEXBOfCzb7d4rXlftMAA4P8E1UnfwGzG0Qev5NGE4y0R 0BPMnCHLsFahCMO9ddnVBqjNqgvxquddtCC54d17zg6VNc5wz6wIzHdeslg20lmsv7MOrlvWAX2P 4HuZiKz3o/R7oJeqsg1jJBnyUK2ln37oqwkkYN8daHYH2LOb15YNBa6KUq6TOHrZSGE= - - Basware PEPPOL Test Access Point - arun.kumar@basware.com - - - - https://test-api.basware.com/peppol/as4 - - - - false - 2022-01-01T00:00:00.000Z - 2024-01-01T23:59:59.000Z - - MIIFvzCCA6egAwIBAgIQBV7uSJlbeLx9GB1tBD9qhTANBgkqhkiG9w0BAQsFADBrMQswCQYDVQQG EwJCRTEZMBcGA1UEChMQT3BlblBFUFBPTCBBSVNCTDEWMBQGA1UECxMNRk9SIFRFU1QgT05MWTEp MCcGA1UEAxMgUEVQUE9MIEFDQ0VTUyBQT0lOVCBURVNUIENBIC0gRzIwHhcNMjAwOTE0MDAwMDAw WhcNMjIwOTA0MjM1OTU5WjBMMRIwEAYDVQQDDAlQT1AwMDAwMTAxFzAVBgNVBAsMDlBFUFBPTCBU RVNUIEFQMRAwDgYDVQQKDAdCYXN3YXJlMQswCQYDVQQGEwJGSTCCASIwDQYJKoZIhvcNAQEBBQAD ggEPADCCAQoCggEBALMwQO726jn0C2sQq8e16KVt/NRUaj5l/gOtfL0y0hEAQNRM8oM2E+LN0Gzn 4T7sLOEgcgOrnw0Sx9PBhosJjeHQmp4FxXBcTdfhpe6jeBG7oK4r4qO+d4qnssm3tW25k75o/Ocy baqWX6o9oEL9F4hMJ/cR53eINZuy/WlblEZSPrSCWhu2jH3fikbQ0yBbyneU1EQgtGYvnokg7pxy gLBnbWgpOkrPH43Ug0t+4hromsHYMo/FKs2ATxP1aKCCxzrlytSu60mhu5ZRYS+RwIP0vGQ6yb2G 7eb1r7xEWXfg0wcZMqTjs0t3jX206rSbMqcdIGpL7jibsZU0gXZkcxECAwEAAaOCAXwwggF4MAwG A1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgOoMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMCMB0GA1Ud DgQWBBSsoxuqualBzUgqSE3y9J1N0QDQ5zBdBgNVHR8EVjBUMFKgUKBOhkxodHRwOi8vcGtpLWNy bC5zeW1hdXRoLmNvbS9jYV82YTkzNzczNGEzOTNhMDgwNWJmMzNjZGE4YjMzMTA5My9MYXRlc3RD UkwuY3JsMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL3BraS1vY3NwLnN5bWF1 dGguY29tMB8GA1UdIwQYMBaAFGtvS7bxN7orPH8Yzborsrl8KjfrMC0GCmCGSAGG+EUBEAMEHzAd BhNghkgBhvhFARABAgMBAYGpkOEDFgY5NTc2MDgwOQYKYIZIAYb4RQEQBQQrMCkCAQAWJGFIUjBj SE02THk5d2Eya3RjbUV1YzNsdFlYVjBhQzVqYjIwPTANBgkqhkiG9w0BAQsFAAOCAgEAFJpFt3s5 T6Sjz3/yJs+6eAVuIr6lNGx7jLHeCIKHp/l4Y8KyiQPBcfP5PCo74+2HuCexEBzt7xAkwpRwo1aR VTFAr8QZlWHo4OOf7Ah5aQcbVFFSv0epTEdwNbgwWROCdD/BYEWWNPLboELeMY58LV8R2qlaglWx Fg2Ezn8/cqOZE2u/2Rp3djRW7eSvjy+XmOIBRQcgcKNRXebwPaUTq2W7TebWKwpj4E6EtFFTxGfg 41EQub5HH4HWpIvwOMzVNLj0iocAR5VWGedYjzQ4GryGM/1bml1Y1IN2EGgytZxUng8XEy8XIMey tOBbBEHLcwWPGOmES/wmod1qiEABx570S+a9ZOaNU01y1thCC6rkhLyeIOQKBm7z3d4+RqHRAIpr mldxzKS2cOQloJyczp6yBjhh2F5mzmWapDvGGsY7FnBPu+5ylk9GvuN3+pRHrzNxF+6GrH2qFqH3 CPafxQQAc/+sNFHD1bCGT4XeD5eeWhq+UEXBOfCzb7d4rXlftMAA4P8E1UnfwGzG0Qev5NGE4y0R 0BPMnCHLsFahCMO9ddnVBqjNqgvxquddtCC54d17zg6VNc5wz6wIzHdeslg20lmsv7MOrlvWAX2P 4HuZiKz3o/R7oJeqsg1jJBnyUK2ln37oqwkkYN8daHYH2LOb15YNBa6KUq6TOHrZSGE= - - Basware PEPPOL Test Access Point - arun.kumar@basware.com - - - - https://test-api.basware.com/peppol/as2 - - - - false - 2024-01-01T00:00:00.000Z - 2026-01-01T23:59:59.000Z - - MIIFvzCCA6egAwIBAgIQBV7uSJlbeLx9GB1tBD9qhTANBgkqhkiG9w0BAQsFADBrMQswCQYDVQQG EwJCRTEZMBcGA1UEChMQT3BlblBFUFBPTCBBSVNCTDEWMBQGA1UECxMNRk9SIFRFU1QgT05MWTEp MCcGA1UEAxMgUEVQUE9MIEFDQ0VTUyBQT0lOVCBURVNUIENBIC0gRzIwHhcNMjAwOTE0MDAwMDAw WhcNMjIwOTA0MjM1OTU5WjBMMRIwEAYDVQQDDAlQT1AwMDAwMTAxFzAVBgNVBAsMDlBFUFBPTCBU RVNUIEFQMRAwDgYDVQQKDAdCYXN3YXJlMQswCQYDVQQGEwJGSTCCASIwDQYJKoZIhvcNAQEBBQAD ggEPADCCAQoCggEBALMwQO726jn0C2sQq8e16KVt/NRUaj5l/gOtfL0y0hEAQNRM8oM2E+LN0Gzn 4T7sLOEgcgOrnw0Sx9PBhosJjeHQmp4FxXBcTdfhpe6jeBG7oK4r4qO+d4qnssm3tW25k75o/Ocy baqWX6o9oEL9F4hMJ/cR53eINZuy/WlblEZSPrSCWhu2jH3fikbQ0yBbyneU1EQgtGYvnokg7pxy gLBnbWgpOkrPH43Ug0t+4hromsHYMo/FKs2ATxP1aKCCxzrlytSu60mhu5ZRYS+RwIP0vGQ6yb2G 7eb1r7xEWXfg0wcZMqTjs0t3jX206rSbMqcdIGpL7jibsZU0gXZkcxECAwEAAaOCAXwwggF4MAwG A1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgOoMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMCMB0GA1Ud DgQWBBSsoxuqualBzUgqSE3y9J1N0QDQ5zBdBgNVHR8EVjBUMFKgUKBOhkxodHRwOi8vcGtpLWNy bC5zeW1hdXRoLmNvbS9jYV82YTkzNzczNGEzOTNhMDgwNWJmMzNjZGE4YjMzMTA5My9MYXRlc3RD UkwuY3JsMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL3BraS1vY3NwLnN5bWF1 dGguY29tMB8GA1UdIwQYMBaAFGtvS7bxN7orPH8Yzborsrl8KjfrMC0GCmCGSAGG+EUBEAMEHzAd BhNghkgBhvhFARABAgMBAYGpkOEDFgY5NTc2MDgwOQYKYIZIAYb4RQEQBQQrMCkCAQAWJGFIUjBj SE02THk5d2Eya3RjbUV1YzNsdFlYVjBhQzVqYjIwPTANBgkqhkiG9w0BAQsFAAOCAgEAFJpFt3s5 T6Sjz3/yJs+6eAVuIr6lNGx7jLHeCIKHp/l4Y8KyiQPBcfP5PCo74+2HuCexEBzt7xAkwpRwo1aR VTFAr8QZlWHo4OOf7Ah5aQcbVFFSv0epTEdwNbgwWROCdD/BYEWWNPLboELeMY58LV8R2qlaglWx Fg2Ezn8/cqOZE2u/2Rp3djRW7eSvjy+XmOIBRQcgcKNRXebwPaUTq2W7TebWKwpj4E6EtFFTxGfg 41EQub5HH4HWpIvwOMzVNLj0iocAR5VWGedYjzQ4GryGM/1bml1Y1IN2EGgytZxUng8XEy8XIMey tOBbBEHLcwWPGOmES/wmod1qiEABx570S+a9ZOaNU01y1thCC6rkhLyeIOQKBm7z3d4+RqHRAIpr mldxzKS2cOQloJyczp6yBjhh2F5mzmWapDvGGsY7FnBPu+5ylk9GvuN3+pRHrzNxF+6GrH2qFqH3 CPafxQQAc/+sNFHD1bCGT4XeD5eeWhq+UEXBOfCzb7d4rXlftMAA4P8E1UnfwGzG0Qev5NGE4y0R 0BPMnCHLsFahCMO9ddnVBqjNqgvxquddtCC54d17zg6VNc5wz6wIzHdeslg20lmsv7MOrlvWAX2P 4HuZiKz3o/R7oJeqsg1jJBnyUK2ln37oqwkkYN8daHYH2LOb15YNBa6KUq6TOHrZSGE= - - Basware PEPPOL Test Access Point - arun.kumar@basware.com - - - - - - -jG3sUDzwCJK+JatorJHqenZMEcw=uwT3clhW2DM/Q6F4j+OTi2hliFYDX7AQaza7noljuslspHB5D51S526Smtf1VuMHHa6xfcxxRGwn -4uMUPNc3seI3clI2QrgB4GKbvwTbfu637gJ1rBULzfndVjATpDZtzFFqvxbQiLNx7bOf4QRR04zk -sMbAeFmj0eE9H2BknVg/WQgElXqxveJKcCnFCOkvxSRVUzxYNOhzxRD+5Ld6F9OeIL+//vPbGaqj -QqacltOMH0y0tWqg9S7rAvrW5qDWQyNGFsVcc/mxZtRsdnPceTk7XyAzGdoJlzQsnPnXWkX273tG -UMvRrpGuLTndrcrGyLXUXDVmfSGcB5OF2n2NYA==CN=POP000010,OU=PEPPOL TEST SMP,O=Basware,C=FIMIIFzjCCA7agAwIBAgIQBkgalZBvVfIgaiKKlFOrVzANBgkqhkiG9w0BAQsFADB5MQswCQYDVQQG -EwJCRTEZMBcGA1UEChMQT3BlblBFUFBPTCBBSVNCTDEWMBQGA1UECxMNRk9SIFRFU1QgT05MWTE3 -MDUGA1UEAxMuUEVQUE9MIFNFUlZJQ0UgTUVUQURBVEEgUFVCTElTSEVSIFRFU1QgQ0EgLSBHMjAe -Fw0yMTA5MjkwMDAwMDBaFw0yMzA5MTkyMzU5NTlaME0xCzAJBgNVBAYTAkZJMRAwDgYDVQQKDAdC -YXN3YXJlMRgwFgYDVQQLDA9QRVBQT0wgVEVTVCBTTVAxEjAQBgNVBAMMCVBPUDAwMDAxMDCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANBe7sa9qbfgxdFlTlCf8pU/6C4nkpPHX7/SVUNV -LeosfCx0zC0nJ7HuyDbngCNmw8UM/OAKpre5YUrDTxJ9MZVVdANaxNrcJnLObXwKSB5inC7c4SOC -u1HrwHkWee/MTy0gQWpxpuBpoUXPp7nW6Qss0sCm0566uorTJ9Ud3A2KD88qZ7VOMuk8ABf5qWvU -v0ORLFFM8unaR44A5Zj1IzJkJFmaGpkArCfSdNqtMUAq6rMZn/NDf3HK8jKx/zIqwUaefRjQYMLi -OvzZad8Dy18HRtoxNZx0WMJi0gVhq7WCnF5k8LP7vz7xDqiKd1J/43Pvrxvb3iuXRHgPij6cLlkC -AwEAAaOCAXwwggF4MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgOoMBYGA1UdJQEB/wQMMAoG -CCsGAQUFBwMCMB0GA1UdDgQWBBQM3c5oM9tmeOCIlBi8ADAt0DWBtTBdBgNVHR8EVjBUMFKgUKBO -hkxodHRwOi8vcGtpLWNybC5zeW1hdXRoLmNvbS9jYV9iNmQwZGMxZGMzMTQ3NzIzZmUzNmI3NTc1 -OTk3YWZjNC9MYXRlc3RDUkwuY3JsMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDov -L3BraS1vY3NwLnN5bWF1dGguY29tMB8GA1UdIwQYMBaAFHwdskjxutkKBsoWY6nwek+9I517MC0G -CmCGSAGG+EUBEAMEHzAdBhNghkgBhvhFARABAgMBAYGo1YEKFgY5NTc2MDgwOQYKYIZIAYb4RQEQ -BQQrMCkCAQAWJGFIUjBjSE02THk5d2Eya3RjbUV1YzNsdFlYVjBhQzVqYjIwPTANBgkqhkiG9w0B -AQsFAAOCAgEAMwO2ma+VhM4U6cOlhWwU4K3yG9W5HuVpM8/G4oQhaV+rkgtQHd0Z2pKWpLSA0nkH -7PRqyF+nix/aSlSMtx58lUMr2IWfdCFNxWi3SCo5/U2g7f/T42yqQvTttYiOChFbsL1GLoMINa+G -CuRVXH0qTPTiLF9ZjAWp0RrcagiX4cLe8F0vhkF4Ledh+mbscshWyK5UPic4aEvsFUuAjvQJqubA -F5er+cJAbna43btd4edRASL4MrOEB7nQUgKUeL35gW/H0VGiyDOAeV8yhGCGUszKtpgncoYN0TWa -AUz+6zPYfryZ29+BkP0Z+nzDaoxDPnA580NDBhain+yY9zamACNjsgvtHFbdpfW9MhTLeWxY5c1t -gM5ad92bblpG/sjXm6RY2MLwon4Iq4KAuV29K1ZqCoqnL5qPx/TGRs4yWZ+d88g6JUE039kGt6PO -mQldoLE2YNTEVwoycQ1gNnd1qk5cWgQBZmr8C13OwCFTWTF53xV8Sfofqs9bIpPjmAhgB9MyWU5z -vL9QzF8sIHN9w7QBsD1YzicINCdJqLsm6Uhr50AW2dqrClXzwE3n0tSbWd0UzrqFXX53hw9pdy0v -JkRan9RF/VQ+o+CSbVdYP4jhNjJxQpDDFmbLP3Zk/FlzsGIGnGUIdDqOA3eS3o5UFdABabQaESr2 -XRkcaY8tJvY= \ No newline at end of file + + + + 9908:923829644 + + urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2::CreditNote##urn:www.cenbii.eu:transaction:biitrns014:ver2.0:extended:urn:www.peppol.eu:bis:peppol5a:ver2.0::2.1 + + + + urn:www.cenbii.eu:profile:bii05:ver2.0 + + + + https://test-api.basware.com/peppol/as2 + + + + false + 2020-01-01T00:00:00.000Z + 2022-01-01T23:59:59.000Z + + MIIFvzCCA6egAwIBAgIQBV7uSJlbeLx9GB1tBD9qhTANBgkqhkiG9w0BAQsFADBrMQswCQYDVQQG + EwJCRTEZMBcGA1UEChMQT3BlblBFUFBPTCBBSVNCTDEWMBQGA1UECxMNRk9SIFRFU1QgT05MWTEp + MCcGA1UEAxMgUEVQUE9MIEFDQ0VTUyBQT0lOVCBURVNUIENBIC0gRzIwHhcNMjAwOTE0MDAwMDAw + WhcNMjIwOTA0MjM1OTU5WjBMMRIwEAYDVQQDDAlQT1AwMDAwMTAxFzAVBgNVBAsMDlBFUFBPTCBU + RVNUIEFQMRAwDgYDVQQKDAdCYXN3YXJlMQswCQYDVQQGEwJGSTCCASIwDQYJKoZIhvcNAQEBBQAD + ggEPADCCAQoCggEBALMwQO726jn0C2sQq8e16KVt/NRUaj5l/gOtfL0y0hEAQNRM8oM2E+LN0Gzn + 4T7sLOEgcgOrnw0Sx9PBhosJjeHQmp4FxXBcTdfhpe6jeBG7oK4r4qO+d4qnssm3tW25k75o/Ocy + baqWX6o9oEL9F4hMJ/cR53eINZuy/WlblEZSPrSCWhu2jH3fikbQ0yBbyneU1EQgtGYvnokg7pxy + gLBnbWgpOkrPH43Ug0t+4hromsHYMo/FKs2ATxP1aKCCxzrlytSu60mhu5ZRYS+RwIP0vGQ6yb2G + 7eb1r7xEWXfg0wcZMqTjs0t3jX206rSbMqcdIGpL7jibsZU0gXZkcxECAwEAAaOCAXwwggF4MAwG + A1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgOoMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMCMB0GA1Ud + DgQWBBSsoxuqualBzUgqSE3y9J1N0QDQ5zBdBgNVHR8EVjBUMFKgUKBOhkxodHRwOi8vcGtpLWNy + bC5zeW1hdXRoLmNvbS9jYV82YTkzNzczNGEzOTNhMDgwNWJmMzNjZGE4YjMzMTA5My9MYXRlc3RD + UkwuY3JsMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL3BraS1vY3NwLnN5bWF1 + dGguY29tMB8GA1UdIwQYMBaAFGtvS7bxN7orPH8Yzborsrl8KjfrMC0GCmCGSAGG+EUBEAMEHzAd + BhNghkgBhvhFARABAgMBAYGpkOEDFgY5NTc2MDgwOQYKYIZIAYb4RQEQBQQrMCkCAQAWJGFIUjBj + SE02THk5d2Eya3RjbUV1YzNsdFlYVjBhQzVqYjIwPTANBgkqhkiG9w0BAQsFAAOCAgEAFJpFt3s5 + T6Sjz3/yJs+6eAVuIr6lNGx7jLHeCIKHp/l4Y8KyiQPBcfP5PCo74+2HuCexEBzt7xAkwpRwo1aR + VTFAr8QZlWHo4OOf7Ah5aQcbVFFSv0epTEdwNbgwWROCdD/BYEWWNPLboELeMY58LV8R2qlaglWx + Fg2Ezn8/cqOZE2u/2Rp3djRW7eSvjy+XmOIBRQcgcKNRXebwPaUTq2W7TebWKwpj4E6EtFFTxGfg + 41EQub5HH4HWpIvwOMzVNLj0iocAR5VWGedYjzQ4GryGM/1bml1Y1IN2EGgytZxUng8XEy8XIMey + tOBbBEHLcwWPGOmES/wmod1qiEABx570S+a9ZOaNU01y1thCC6rkhLyeIOQKBm7z3d4+RqHRAIpr + mldxzKS2cOQloJyczp6yBjhh2F5mzmWapDvGGsY7FnBPu+5ylk9GvuN3+pRHrzNxF+6GrH2qFqH3 + CPafxQQAc/+sNFHD1bCGT4XeD5eeWhq+UEXBOfCzb7d4rXlftMAA4P8E1UnfwGzG0Qev5NGE4y0R + 0BPMnCHLsFahCMO9ddnVBqjNqgvxquddtCC54d17zg6VNc5wz6wIzHdeslg20lmsv7MOrlvWAX2P + 4HuZiKz3o/R7oJeqsg1jJBnyUK2ln37oqwkkYN8daHYH2LOb15YNBa6KUq6TOHrZSGE= + + Basware PEPPOL Test Access Point + arun.kumar@basware.com + + + + https://test-api.basware.com/peppol/as4 + + + + false + 2024-01-01T00:00:00.000Z + 2026-01-01T23:59:59.000Z + + MIIFvzCCA6egAwIBAgIQBV7uSJlbeLx9GB1tBD9qhTANBgkqhkiG9w0BAQsFADBrMQswCQYDVQQG + EwJCRTEZMBcGA1UEChMQT3BlblBFUFBPTCBBSVNCTDEWMBQGA1UECxMNRk9SIFRFU1QgT05MWTEp + MCcGA1UEAxMgUEVQUE9MIEFDQ0VTUyBQT0lOVCBURVNUIENBIC0gRzIwHhcNMjAwOTE0MDAwMDAw + WhcNMjIwOTA0MjM1OTU5WjBMMRIwEAYDVQQDDAlQT1AwMDAwMTAxFzAVBgNVBAsMDlBFUFBPTCBU + RVNUIEFQMRAwDgYDVQQKDAdCYXN3YXJlMQswCQYDVQQGEwJGSTCCASIwDQYJKoZIhvcNAQEBBQAD + ggEPADCCAQoCggEBALMwQO726jn0C2sQq8e16KVt/NRUaj5l/gOtfL0y0hEAQNRM8oM2E+LN0Gzn + 4T7sLOEgcgOrnw0Sx9PBhosJjeHQmp4FxXBcTdfhpe6jeBG7oK4r4qO+d4qnssm3tW25k75o/Ocy + baqWX6o9oEL9F4hMJ/cR53eINZuy/WlblEZSPrSCWhu2jH3fikbQ0yBbyneU1EQgtGYvnokg7pxy + gLBnbWgpOkrPH43Ug0t+4hromsHYMo/FKs2ATxP1aKCCxzrlytSu60mhu5ZRYS+RwIP0vGQ6yb2G + 7eb1r7xEWXfg0wcZMqTjs0t3jX206rSbMqcdIGpL7jibsZU0gXZkcxECAwEAAaOCAXwwggF4MAwG + A1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgOoMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMCMB0GA1Ud + DgQWBBSsoxuqualBzUgqSE3y9J1N0QDQ5zBdBgNVHR8EVjBUMFKgUKBOhkxodHRwOi8vcGtpLWNy + bC5zeW1hdXRoLmNvbS9jYV82YTkzNzczNGEzOTNhMDgwNWJmMzNjZGE4YjMzMTA5My9MYXRlc3RD + UkwuY3JsMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL3BraS1vY3NwLnN5bWF1 + dGguY29tMB8GA1UdIwQYMBaAFGtvS7bxN7orPH8Yzborsrl8KjfrMC0GCmCGSAGG+EUBEAMEHzAd + BhNghkgBhvhFARABAgMBAYGpkOEDFgY5NTc2MDgwOQYKYIZIAYb4RQEQBQQrMCkCAQAWJGFIUjBj + SE02THk5d2Eya3RjbUV1YzNsdFlYVjBhQzVqYjIwPTANBgkqhkiG9w0BAQsFAAOCAgEAFJpFt3s5 + T6Sjz3/yJs+6eAVuIr6lNGx7jLHeCIKHp/l4Y8KyiQPBcfP5PCo74+2HuCexEBzt7xAkwpRwo1aR + VTFAr8QZlWHo4OOf7Ah5aQcbVFFSv0epTEdwNbgwWROCdD/BYEWWNPLboELeMY58LV8R2qlaglWx + Fg2Ezn8/cqOZE2u/2Rp3djRW7eSvjy+XmOIBRQcgcKNRXebwPaUTq2W7TebWKwpj4E6EtFFTxGfg + 41EQub5HH4HWpIvwOMzVNLj0iocAR5VWGedYjzQ4GryGM/1bml1Y1IN2EGgytZxUng8XEy8XIMey + tOBbBEHLcwWPGOmES/wmod1qiEABx570S+a9ZOaNU01y1thCC6rkhLyeIOQKBm7z3d4+RqHRAIpr + mldxzKS2cOQloJyczp6yBjhh2F5mzmWapDvGGsY7FnBPu+5ylk9GvuN3+pRHrzNxF+6GrH2qFqH3 + CPafxQQAc/+sNFHD1bCGT4XeD5eeWhq+UEXBOfCzb7d4rXlftMAA4P8E1UnfwGzG0Qev5NGE4y0R + 0BPMnCHLsFahCMO9ddnVBqjNqgvxquddtCC54d17zg6VNc5wz6wIzHdeslg20lmsv7MOrlvWAX2P + 4HuZiKz3o/R7oJeqsg1jJBnyUK2ln37oqwkkYN8daHYH2LOb15YNBa6KUq6TOHrZSGE= + + Basware PEPPOL Test Access Point + arun.kumar@basware.com + + + + https://test-api.basware.com/peppol/as2 + + + + false + 2026-01-01T00:00:00.000Z + 2028-01-01T23:59:59.000Z + + MIIFvzCCA6egAwIBAgIQBV7uSJlbeLx9GB1tBD9qhTANBgkqhkiG9w0BAQsFADBrMQswCQYDVQQG + EwJCRTEZMBcGA1UEChMQT3BlblBFUFBPTCBBSVNCTDEWMBQGA1UECxMNRk9SIFRFU1QgT05MWTEp + MCcGA1UEAxMgUEVQUE9MIEFDQ0VTUyBQT0lOVCBURVNUIENBIC0gRzIwHhcNMjAwOTE0MDAwMDAw + WhcNMjIwOTA0MjM1OTU5WjBMMRIwEAYDVQQDDAlQT1AwMDAwMTAxFzAVBgNVBAsMDlBFUFBPTCBU + RVNUIEFQMRAwDgYDVQQKDAdCYXN3YXJlMQswCQYDVQQGEwJGSTCCASIwDQYJKoZIhvcNAQEBBQAD + ggEPADCCAQoCggEBALMwQO726jn0C2sQq8e16KVt/NRUaj5l/gOtfL0y0hEAQNRM8oM2E+LN0Gzn + 4T7sLOEgcgOrnw0Sx9PBhosJjeHQmp4FxXBcTdfhpe6jeBG7oK4r4qO+d4qnssm3tW25k75o/Ocy + baqWX6o9oEL9F4hMJ/cR53eINZuy/WlblEZSPrSCWhu2jH3fikbQ0yBbyneU1EQgtGYvnokg7pxy + gLBnbWgpOkrPH43Ug0t+4hromsHYMo/FKs2ATxP1aKCCxzrlytSu60mhu5ZRYS+RwIP0vGQ6yb2G + 7eb1r7xEWXfg0wcZMqTjs0t3jX206rSbMqcdIGpL7jibsZU0gXZkcxECAwEAAaOCAXwwggF4MAwG + A1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgOoMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMCMB0GA1Ud + DgQWBBSsoxuqualBzUgqSE3y9J1N0QDQ5zBdBgNVHR8EVjBUMFKgUKBOhkxodHRwOi8vcGtpLWNy + bC5zeW1hdXRoLmNvbS9jYV82YTkzNzczNGEzOTNhMDgwNWJmMzNjZGE4YjMzMTA5My9MYXRlc3RD + UkwuY3JsMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL3BraS1vY3NwLnN5bWF1 + dGguY29tMB8GA1UdIwQYMBaAFGtvS7bxN7orPH8Yzborsrl8KjfrMC0GCmCGSAGG+EUBEAMEHzAd + BhNghkgBhvhFARABAgMBAYGpkOEDFgY5NTc2MDgwOQYKYIZIAYb4RQEQBQQrMCkCAQAWJGFIUjBj + SE02THk5d2Eya3RjbUV1YzNsdFlYVjBhQzVqYjIwPTANBgkqhkiG9w0BAQsFAAOCAgEAFJpFt3s5 + T6Sjz3/yJs+6eAVuIr6lNGx7jLHeCIKHp/l4Y8KyiQPBcfP5PCo74+2HuCexEBzt7xAkwpRwo1aR + VTFAr8QZlWHo4OOf7Ah5aQcbVFFSv0epTEdwNbgwWROCdD/BYEWWNPLboELeMY58LV8R2qlaglWx + Fg2Ezn8/cqOZE2u/2Rp3djRW7eSvjy+XmOIBRQcgcKNRXebwPaUTq2W7TebWKwpj4E6EtFFTxGfg + 41EQub5HH4HWpIvwOMzVNLj0iocAR5VWGedYjzQ4GryGM/1bml1Y1IN2EGgytZxUng8XEy8XIMey + tOBbBEHLcwWPGOmES/wmod1qiEABx570S+a9ZOaNU01y1thCC6rkhLyeIOQKBm7z3d4+RqHRAIpr + mldxzKS2cOQloJyczp6yBjhh2F5mzmWapDvGGsY7FnBPu+5ylk9GvuN3+pRHrzNxF+6GrH2qFqH3 + CPafxQQAc/+sNFHD1bCGT4XeD5eeWhq+UEXBOfCzb7d4rXlftMAA4P8E1UnfwGzG0Qev5NGE4y0R + 0BPMnCHLsFahCMO9ddnVBqjNqgvxquddtCC54d17zg6VNc5wz6wIzHdeslg20lmsv7MOrlvWAX2P + 4HuZiKz3o/R7oJeqsg1jJBnyUK2ln37oqwkkYN8daHYH2LOb15YNBa6KUq6TOHrZSGE= + + Basware PEPPOL Test Access Point + arun.kumar@basware.com + + + + + + \ No newline at end of file diff --git a/src/test/resources/smp-response-examples/busdox-servicemetadata-9908-98765432.xml b/src/test/resources/smp-response-examples/busdox-servicemetadata-9908-98765432.xml index 66183ca..1dca4a7 100644 --- a/src/test/resources/smp-response-examples/busdox-servicemetadata-9908-98765432.xml +++ b/src/test/resources/smp-response-examples/busdox-servicemetadata-9908-98765432.xml @@ -1,90 +1,136 @@ - - - - 9908:12345678 - - urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2::CreditNote##urn:www.cenbii.eu:transaction:biitrns014:ver2.0:extended:urn:www.peppol.eu:bis:peppol5a:ver2.0::2.1 - - - - urn:www.cenbii.eu:profile:bii05:ver2.0 - - - - https://test-api.basware.com/peppol/as2 - - - - false - 2022-01-01T23:59:59.000Z - - MIIFvzCCA6egAwIBAgIQBV7uSJlbeLx9GB1tBD9qhTANBgkqhkiG9w0BAQsFADBrMQswCQYDVQQG EwJCRTEZMBcGA1UEChMQT3BlblBFUFBPTCBBSVNCTDEWMBQGA1UECxMNRk9SIFRFU1QgT05MWTEp MCcGA1UEAxMgUEVQUE9MIEFDQ0VTUyBQT0lOVCBURVNUIENBIC0gRzIwHhcNMjAwOTE0MDAwMDAw WhcNMjIwOTA0MjM1OTU5WjBMMRIwEAYDVQQDDAlQT1AwMDAwMTAxFzAVBgNVBAsMDlBFUFBPTCBU RVNUIEFQMRAwDgYDVQQKDAdCYXN3YXJlMQswCQYDVQQGEwJGSTCCASIwDQYJKoZIhvcNAQEBBQAD ggEPADCCAQoCggEBALMwQO726jn0C2sQq8e16KVt/NRUaj5l/gOtfL0y0hEAQNRM8oM2E+LN0Gzn 4T7sLOEgcgOrnw0Sx9PBhosJjeHQmp4FxXBcTdfhpe6jeBG7oK4r4qO+d4qnssm3tW25k75o/Ocy baqWX6o9oEL9F4hMJ/cR53eINZuy/WlblEZSPrSCWhu2jH3fikbQ0yBbyneU1EQgtGYvnokg7pxy gLBnbWgpOkrPH43Ug0t+4hromsHYMo/FKs2ATxP1aKCCxzrlytSu60mhu5ZRYS+RwIP0vGQ6yb2G 7eb1r7xEWXfg0wcZMqTjs0t3jX206rSbMqcdIGpL7jibsZU0gXZkcxECAwEAAaOCAXwwggF4MAwG A1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgOoMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMCMB0GA1Ud DgQWBBSsoxuqualBzUgqSE3y9J1N0QDQ5zBdBgNVHR8EVjBUMFKgUKBOhkxodHRwOi8vcGtpLWNy bC5zeW1hdXRoLmNvbS9jYV82YTkzNzczNGEzOTNhMDgwNWJmMzNjZGE4YjMzMTA5My9MYXRlc3RD UkwuY3JsMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL3BraS1vY3NwLnN5bWF1 dGguY29tMB8GA1UdIwQYMBaAFGtvS7bxN7orPH8Yzborsrl8KjfrMC0GCmCGSAGG+EUBEAMEHzAd BhNghkgBhvhFARABAgMBAYGpkOEDFgY5NTc2MDgwOQYKYIZIAYb4RQEQBQQrMCkCAQAWJGFIUjBj SE02THk5d2Eya3RjbUV1YzNsdFlYVjBhQzVqYjIwPTANBgkqhkiG9w0BAQsFAAOCAgEAFJpFt3s5 T6Sjz3/yJs+6eAVuIr6lNGx7jLHeCIKHp/l4Y8KyiQPBcfP5PCo74+2HuCexEBzt7xAkwpRwo1aR VTFAr8QZlWHo4OOf7Ah5aQcbVFFSv0epTEdwNbgwWROCdD/BYEWWNPLboELeMY58LV8R2qlaglWx Fg2Ezn8/cqOZE2u/2Rp3djRW7eSvjy+XmOIBRQcgcKNRXebwPaUTq2W7TebWKwpj4E6EtFFTxGfg 41EQub5HH4HWpIvwOMzVNLj0iocAR5VWGedYjzQ4GryGM/1bml1Y1IN2EGgytZxUng8XEy8XIMey tOBbBEHLcwWPGOmES/wmod1qiEABx570S+a9ZOaNU01y1thCC6rkhLyeIOQKBm7z3d4+RqHRAIpr mldxzKS2cOQloJyczp6yBjhh2F5mzmWapDvGGsY7FnBPu+5ylk9GvuN3+pRHrzNxF+6GrH2qFqH3 CPafxQQAc/+sNFHD1bCGT4XeD5eeWhq+UEXBOfCzb7d4rXlftMAA4P8E1UnfwGzG0Qev5NGE4y0R 0BPMnCHLsFahCMO9ddnVBqjNqgvxquddtCC54d17zg6VNc5wz6wIzHdeslg20lmsv7MOrlvWAX2P 4HuZiKz3o/R7oJeqsg1jJBnyUK2ln37oqwkkYN8daHYH2LOb15YNBa6KUq6TOHrZSGE= - - Basware PEPPOL Test Access Point - arun.kumar@basware.com - - - - https://test-api.basware.com/peppol/as2 - - - - false - 2024-01-01T00:00:00.000Z - - MIIFvzCCA6egAwIBAgIQBV7uSJlbeLx9GB1tBD9qhTANBgkqhkiG9w0BAQsFADBrMQswCQYDVQQG EwJCRTEZMBcGA1UEChMQT3BlblBFUFBPTCBBSVNCTDEWMBQGA1UECxMNRk9SIFRFU1QgT05MWTEp MCcGA1UEAxMgUEVQUE9MIEFDQ0VTUyBQT0lOVCBURVNUIENBIC0gRzIwHhcNMjAwOTE0MDAwMDAw WhcNMjIwOTA0MjM1OTU5WjBMMRIwEAYDVQQDDAlQT1AwMDAwMTAxFzAVBgNVBAsMDlBFUFBPTCBU RVNUIEFQMRAwDgYDVQQKDAdCYXN3YXJlMQswCQYDVQQGEwJGSTCCASIwDQYJKoZIhvcNAQEBBQAD ggEPADCCAQoCggEBALMwQO726jn0C2sQq8e16KVt/NRUaj5l/gOtfL0y0hEAQNRM8oM2E+LN0Gzn 4T7sLOEgcgOrnw0Sx9PBhosJjeHQmp4FxXBcTdfhpe6jeBG7oK4r4qO+d4qnssm3tW25k75o/Ocy baqWX6o9oEL9F4hMJ/cR53eINZuy/WlblEZSPrSCWhu2jH3fikbQ0yBbyneU1EQgtGYvnokg7pxy gLBnbWgpOkrPH43Ug0t+4hromsHYMo/FKs2ATxP1aKCCxzrlytSu60mhu5ZRYS+RwIP0vGQ6yb2G 7eb1r7xEWXfg0wcZMqTjs0t3jX206rSbMqcdIGpL7jibsZU0gXZkcxECAwEAAaOCAXwwggF4MAwG A1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgOoMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMCMB0GA1Ud DgQWBBSsoxuqualBzUgqSE3y9J1N0QDQ5zBdBgNVHR8EVjBUMFKgUKBOhkxodHRwOi8vcGtpLWNy bC5zeW1hdXRoLmNvbS9jYV82YTkzNzczNGEzOTNhMDgwNWJmMzNjZGE4YjMzMTA5My9MYXRlc3RD UkwuY3JsMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL3BraS1vY3NwLnN5bWF1 dGguY29tMB8GA1UdIwQYMBaAFGtvS7bxN7orPH8Yzborsrl8KjfrMC0GCmCGSAGG+EUBEAMEHzAd BhNghkgBhvhFARABAgMBAYGpkOEDFgY5NTc2MDgwOQYKYIZIAYb4RQEQBQQrMCkCAQAWJGFIUjBj SE02THk5d2Eya3RjbUV1YzNsdFlYVjBhQzVqYjIwPTANBgkqhkiG9w0BAQsFAAOCAgEAFJpFt3s5 T6Sjz3/yJs+6eAVuIr6lNGx7jLHeCIKHp/l4Y8KyiQPBcfP5PCo74+2HuCexEBzt7xAkwpRwo1aR VTFAr8QZlWHo4OOf7Ah5aQcbVFFSv0epTEdwNbgwWROCdD/BYEWWNPLboELeMY58LV8R2qlaglWx Fg2Ezn8/cqOZE2u/2Rp3djRW7eSvjy+XmOIBRQcgcKNRXebwPaUTq2W7TebWKwpj4E6EtFFTxGfg 41EQub5HH4HWpIvwOMzVNLj0iocAR5VWGedYjzQ4GryGM/1bml1Y1IN2EGgytZxUng8XEy8XIMey tOBbBEHLcwWPGOmES/wmod1qiEABx570S+a9ZOaNU01y1thCC6rkhLyeIOQKBm7z3d4+RqHRAIpr mldxzKS2cOQloJyczp6yBjhh2F5mzmWapDvGGsY7FnBPu+5ylk9GvuN3+pRHrzNxF+6GrH2qFqH3 CPafxQQAc/+sNFHD1bCGT4XeD5eeWhq+UEXBOfCzb7d4rXlftMAA4P8E1UnfwGzG0Qev5NGE4y0R 0BPMnCHLsFahCMO9ddnVBqjNqgvxquddtCC54d17zg6VNc5wz6wIzHdeslg20lmsv7MOrlvWAX2P 4HuZiKz3o/R7oJeqsg1jJBnyUK2ln37oqwkkYN8daHYH2LOb15YNBa6KUq6TOHrZSGE= - - Basware PEPPOL Test Access Point - arun.kumar@basware.com - - - - https://test-api.basware.com/peppol/as4 - - - - false - 2024-01-01T00:00:00.000Z - 2022-01-01T23:59:59.000Z - - MIIFvzCCA6egAwIBAgIQBV7uSJlbeLx9GB1tBD9qhTANBgkqhkiG9w0BAQsFADBrMQswCQYDVQQG EwJCRTEZMBcGA1UEChMQT3BlblBFUFBPTCBBSVNCTDEWMBQGA1UECxMNRk9SIFRFU1QgT05MWTEp MCcGA1UEAxMgUEVQUE9MIEFDQ0VTUyBQT0lOVCBURVNUIENBIC0gRzIwHhcNMjAwOTE0MDAwMDAw WhcNMjIwOTA0MjM1OTU5WjBMMRIwEAYDVQQDDAlQT1AwMDAwMTAxFzAVBgNVBAsMDlBFUFBPTCBU RVNUIEFQMRAwDgYDVQQKDAdCYXN3YXJlMQswCQYDVQQGEwJGSTCCASIwDQYJKoZIhvcNAQEBBQAD ggEPADCCAQoCggEBALMwQO726jn0C2sQq8e16KVt/NRUaj5l/gOtfL0y0hEAQNRM8oM2E+LN0Gzn 4T7sLOEgcgOrnw0Sx9PBhosJjeHQmp4FxXBcTdfhpe6jeBG7oK4r4qO+d4qnssm3tW25k75o/Ocy baqWX6o9oEL9F4hMJ/cR53eINZuy/WlblEZSPrSCWhu2jH3fikbQ0yBbyneU1EQgtGYvnokg7pxy gLBnbWgpOkrPH43Ug0t+4hromsHYMo/FKs2ATxP1aKCCxzrlytSu60mhu5ZRYS+RwIP0vGQ6yb2G 7eb1r7xEWXfg0wcZMqTjs0t3jX206rSbMqcdIGpL7jibsZU0gXZkcxECAwEAAaOCAXwwggF4MAwG A1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgOoMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMCMB0GA1Ud DgQWBBSsoxuqualBzUgqSE3y9J1N0QDQ5zBdBgNVHR8EVjBUMFKgUKBOhkxodHRwOi8vcGtpLWNy bC5zeW1hdXRoLmNvbS9jYV82YTkzNzczNGEzOTNhMDgwNWJmMzNjZGE4YjMzMTA5My9MYXRlc3RD UkwuY3JsMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL3BraS1vY3NwLnN5bWF1 dGguY29tMB8GA1UdIwQYMBaAFGtvS7bxN7orPH8Yzborsrl8KjfrMC0GCmCGSAGG+EUBEAMEHzAd BhNghkgBhvhFARABAgMBAYGpkOEDFgY5NTc2MDgwOQYKYIZIAYb4RQEQBQQrMCkCAQAWJGFIUjBj SE02THk5d2Eya3RjbUV1YzNsdFlYVjBhQzVqYjIwPTANBgkqhkiG9w0BAQsFAAOCAgEAFJpFt3s5 T6Sjz3/yJs+6eAVuIr6lNGx7jLHeCIKHp/l4Y8KyiQPBcfP5PCo74+2HuCexEBzt7xAkwpRwo1aR VTFAr8QZlWHo4OOf7Ah5aQcbVFFSv0epTEdwNbgwWROCdD/BYEWWNPLboELeMY58LV8R2qlaglWx Fg2Ezn8/cqOZE2u/2Rp3djRW7eSvjy+XmOIBRQcgcKNRXebwPaUTq2W7TebWKwpj4E6EtFFTxGfg 41EQub5HH4HWpIvwOMzVNLj0iocAR5VWGedYjzQ4GryGM/1bml1Y1IN2EGgytZxUng8XEy8XIMey tOBbBEHLcwWPGOmES/wmod1qiEABx570S+a9ZOaNU01y1thCC6rkhLyeIOQKBm7z3d4+RqHRAIpr mldxzKS2cOQloJyczp6yBjhh2F5mzmWapDvGGsY7FnBPu+5ylk9GvuN3+pRHrzNxF+6GrH2qFqH3 CPafxQQAc/+sNFHD1bCGT4XeD5eeWhq+UEXBOfCzb7d4rXlftMAA4P8E1UnfwGzG0Qev5NGE4y0R 0BPMnCHLsFahCMO9ddnVBqjNqgvxquddtCC54d17zg6VNc5wz6wIzHdeslg20lmsv7MOrlvWAX2P 4HuZiKz3o/R7oJeqsg1jJBnyUK2ln37oqwkkYN8daHYH2LOb15YNBa6KUq6TOHrZSGE= - - Basware PEPPOL Test Access Point - arun.kumar@basware.com - - - - - - -9TrwQ0b6vyC1fS5yh+ndg3IzmWw=RlHHu1NslA3qLxX5j33Qkahzvrc+msLL43Ot7FXiLWIZHWhzQfcD9usZQYU/qkPuO+FxxYFZRcoW -XwQmdYOq6dXnzxSikl7lv8gxfqBo1fZJg55yzYUQgHSfXF5mLoTtBd7i+EWa3+zexJWoxfBrfdAN -EwxtPLYEYVvMGDR+gL3UK0njiqogGIbOj+QSTV7oqOXishVhXOL81GkvjMjGZ8DlLBTNI8O0e/di -hhxh7ynaF4ZKpsd4xejzyapz2ZoyFPtoJdiQv922Sy1EazGUMaCj0ZfdPE/ByrfhjLkn2VLq7FCp -TXf0S3aBiFoqkjol9BJKMkjWVCeoRoT6l7JBkg==CN=POP000010,OU=PEPPOL TEST SMP,O=Basware,C=FIMIIFzjCCA7agAwIBAgIQBkgalZBvVfIgaiKKlFOrVzANBgkqhkiG9w0BAQsFADB5MQswCQYDVQQG -EwJCRTEZMBcGA1UEChMQT3BlblBFUFBPTCBBSVNCTDEWMBQGA1UECxMNRk9SIFRFU1QgT05MWTE3 -MDUGA1UEAxMuUEVQUE9MIFNFUlZJQ0UgTUVUQURBVEEgUFVCTElTSEVSIFRFU1QgQ0EgLSBHMjAe -Fw0yMTA5MjkwMDAwMDBaFw0yMzA5MTkyMzU5NTlaME0xCzAJBgNVBAYTAkZJMRAwDgYDVQQKDAdC -YXN3YXJlMRgwFgYDVQQLDA9QRVBQT0wgVEVTVCBTTVAxEjAQBgNVBAMMCVBPUDAwMDAxMDCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANBe7sa9qbfgxdFlTlCf8pU/6C4nkpPHX7/SVUNV -LeosfCx0zC0nJ7HuyDbngCNmw8UM/OAKpre5YUrDTxJ9MZVVdANaxNrcJnLObXwKSB5inC7c4SOC -u1HrwHkWee/MTy0gQWpxpuBpoUXPp7nW6Qss0sCm0566uorTJ9Ud3A2KD88qZ7VOMuk8ABf5qWvU -v0ORLFFM8unaR44A5Zj1IzJkJFmaGpkArCfSdNqtMUAq6rMZn/NDf3HK8jKx/zIqwUaefRjQYMLi -OvzZad8Dy18HRtoxNZx0WMJi0gVhq7WCnF5k8LP7vz7xDqiKd1J/43Pvrxvb3iuXRHgPij6cLlkC -AwEAAaOCAXwwggF4MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgOoMBYGA1UdJQEB/wQMMAoG -CCsGAQUFBwMCMB0GA1UdDgQWBBQM3c5oM9tmeOCIlBi8ADAt0DWBtTBdBgNVHR8EVjBUMFKgUKBO -hkxodHRwOi8vcGtpLWNybC5zeW1hdXRoLmNvbS9jYV9iNmQwZGMxZGMzMTQ3NzIzZmUzNmI3NTc1 -OTk3YWZjNC9MYXRlc3RDUkwuY3JsMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDov -L3BraS1vY3NwLnN5bWF1dGguY29tMB8GA1UdIwQYMBaAFHwdskjxutkKBsoWY6nwek+9I517MC0G -CmCGSAGG+EUBEAMEHzAdBhNghkgBhvhFARABAgMBAYGo1YEKFgY5NTc2MDgwOQYKYIZIAYb4RQEQ -BQQrMCkCAQAWJGFIUjBjSE02THk5d2Eya3RjbUV1YzNsdFlYVjBhQzVqYjIwPTANBgkqhkiG9w0B -AQsFAAOCAgEAMwO2ma+VhM4U6cOlhWwU4K3yG9W5HuVpM8/G4oQhaV+rkgtQHd0Z2pKWpLSA0nkH -7PRqyF+nix/aSlSMtx58lUMr2IWfdCFNxWi3SCo5/U2g7f/T42yqQvTttYiOChFbsL1GLoMINa+G -CuRVXH0qTPTiLF9ZjAWp0RrcagiX4cLe8F0vhkF4Ledh+mbscshWyK5UPic4aEvsFUuAjvQJqubA -F5er+cJAbna43btd4edRASL4MrOEB7nQUgKUeL35gW/H0VGiyDOAeV8yhGCGUszKtpgncoYN0TWa -AUz+6zPYfryZ29+BkP0Z+nzDaoxDPnA580NDBhain+yY9zamACNjsgvtHFbdpfW9MhTLeWxY5c1t -gM5ad92bblpG/sjXm6RY2MLwon4Iq4KAuV29K1ZqCoqnL5qPx/TGRs4yWZ+d88g6JUE039kGt6PO -mQldoLE2YNTEVwoycQ1gNnd1qk5cWgQBZmr8C13OwCFTWTF53xV8Sfofqs9bIpPjmAhgB9MyWU5z -vL9QzF8sIHN9w7QBsD1YzicINCdJqLsm6Uhr50AW2dqrClXzwE3n0tSbWd0UzrqFXX53hw9pdy0v -JkRan9RF/VQ+o+CSbVdYP4jhNjJxQpDDFmbLP3Zk/FlzsGIGnGUIdDqOA3eS3o5UFdABabQaESr2 -XRkcaY8tJvY= \ No newline at end of file + + + + 9908:12345678 + + urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2::CreditNote##urn:www.cenbii.eu:transaction:biitrns014:ver2.0:extended:urn:www.peppol.eu:bis:peppol5a:ver2.0::2.1 + + + + urn:www.cenbii.eu:profile:bii05:ver2.0 + + + + https://test-api.basware.com/peppol/as2 + + + + false + 2022-01-01T23:59:59.000Z + + MIIFvzCCA6egAwIBAgIQBV7uSJlbeLx9GB1tBD9qhTANBgkqhkiG9w0BAQsFADBrMQswCQYDVQQG + EwJCRTEZMBcGA1UEChMQT3BlblBFUFBPTCBBSVNCTDEWMBQGA1UECxMNRk9SIFRFU1QgT05MWTEp + MCcGA1UEAxMgUEVQUE9MIEFDQ0VTUyBQT0lOVCBURVNUIENBIC0gRzIwHhcNMjAwOTE0MDAwMDAw + WhcNMjIwOTA0MjM1OTU5WjBMMRIwEAYDVQQDDAlQT1AwMDAwMTAxFzAVBgNVBAsMDlBFUFBPTCBU + RVNUIEFQMRAwDgYDVQQKDAdCYXN3YXJlMQswCQYDVQQGEwJGSTCCASIwDQYJKoZIhvcNAQEBBQAD + ggEPADCCAQoCggEBALMwQO726jn0C2sQq8e16KVt/NRUaj5l/gOtfL0y0hEAQNRM8oM2E+LN0Gzn + 4T7sLOEgcgOrnw0Sx9PBhosJjeHQmp4FxXBcTdfhpe6jeBG7oK4r4qO+d4qnssm3tW25k75o/Ocy + baqWX6o9oEL9F4hMJ/cR53eINZuy/WlblEZSPrSCWhu2jH3fikbQ0yBbyneU1EQgtGYvnokg7pxy + gLBnbWgpOkrPH43Ug0t+4hromsHYMo/FKs2ATxP1aKCCxzrlytSu60mhu5ZRYS+RwIP0vGQ6yb2G + 7eb1r7xEWXfg0wcZMqTjs0t3jX206rSbMqcdIGpL7jibsZU0gXZkcxECAwEAAaOCAXwwggF4MAwG + A1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgOoMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMCMB0GA1Ud + DgQWBBSsoxuqualBzUgqSE3y9J1N0QDQ5zBdBgNVHR8EVjBUMFKgUKBOhkxodHRwOi8vcGtpLWNy + bC5zeW1hdXRoLmNvbS9jYV82YTkzNzczNGEzOTNhMDgwNWJmMzNjZGE4YjMzMTA5My9MYXRlc3RD + UkwuY3JsMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL3BraS1vY3NwLnN5bWF1 + dGguY29tMB8GA1UdIwQYMBaAFGtvS7bxN7orPH8Yzborsrl8KjfrMC0GCmCGSAGG+EUBEAMEHzAd + BhNghkgBhvhFARABAgMBAYGpkOEDFgY5NTc2MDgwOQYKYIZIAYb4RQEQBQQrMCkCAQAWJGFIUjBj + SE02THk5d2Eya3RjbUV1YzNsdFlYVjBhQzVqYjIwPTANBgkqhkiG9w0BAQsFAAOCAgEAFJpFt3s5 + T6Sjz3/yJs+6eAVuIr6lNGx7jLHeCIKHp/l4Y8KyiQPBcfP5PCo74+2HuCexEBzt7xAkwpRwo1aR + VTFAr8QZlWHo4OOf7Ah5aQcbVFFSv0epTEdwNbgwWROCdD/BYEWWNPLboELeMY58LV8R2qlaglWx + Fg2Ezn8/cqOZE2u/2Rp3djRW7eSvjy+XmOIBRQcgcKNRXebwPaUTq2W7TebWKwpj4E6EtFFTxGfg + 41EQub5HH4HWpIvwOMzVNLj0iocAR5VWGedYjzQ4GryGM/1bml1Y1IN2EGgytZxUng8XEy8XIMey + tOBbBEHLcwWPGOmES/wmod1qiEABx570S+a9ZOaNU01y1thCC6rkhLyeIOQKBm7z3d4+RqHRAIpr + mldxzKS2cOQloJyczp6yBjhh2F5mzmWapDvGGsY7FnBPu+5ylk9GvuN3+pRHrzNxF+6GrH2qFqH3 + CPafxQQAc/+sNFHD1bCGT4XeD5eeWhq+UEXBOfCzb7d4rXlftMAA4P8E1UnfwGzG0Qev5NGE4y0R + 0BPMnCHLsFahCMO9ddnVBqjNqgvxquddtCC54d17zg6VNc5wz6wIzHdeslg20lmsv7MOrlvWAX2P + 4HuZiKz3o/R7oJeqsg1jJBnyUK2ln37oqwkkYN8daHYH2LOb15YNBa6KUq6TOHrZSGE= + + Basware PEPPOL Test Access Point + arun.kumar@basware.com + + + + https://test-api.basware.com/peppol/as2 + + + + false + 2026-01-01T00:00:00.000Z + + MIIFvzCCA6egAwIBAgIQBV7uSJlbeLx9GB1tBD9qhTANBgkqhkiG9w0BAQsFADBrMQswCQYDVQQG + EwJCRTEZMBcGA1UEChMQT3BlblBFUFBPTCBBSVNCTDEWMBQGA1UECxMNRk9SIFRFU1QgT05MWTEp + MCcGA1UEAxMgUEVQUE9MIEFDQ0VTUyBQT0lOVCBURVNUIENBIC0gRzIwHhcNMjAwOTE0MDAwMDAw + WhcNMjIwOTA0MjM1OTU5WjBMMRIwEAYDVQQDDAlQT1AwMDAwMTAxFzAVBgNVBAsMDlBFUFBPTCBU + RVNUIEFQMRAwDgYDVQQKDAdCYXN3YXJlMQswCQYDVQQGEwJGSTCCASIwDQYJKoZIhvcNAQEBBQAD + ggEPADCCAQoCggEBALMwQO726jn0C2sQq8e16KVt/NRUaj5l/gOtfL0y0hEAQNRM8oM2E+LN0Gzn + 4T7sLOEgcgOrnw0Sx9PBhosJjeHQmp4FxXBcTdfhpe6jeBG7oK4r4qO+d4qnssm3tW25k75o/Ocy + baqWX6o9oEL9F4hMJ/cR53eINZuy/WlblEZSPrSCWhu2jH3fikbQ0yBbyneU1EQgtGYvnokg7pxy + gLBnbWgpOkrPH43Ug0t+4hromsHYMo/FKs2ATxP1aKCCxzrlytSu60mhu5ZRYS+RwIP0vGQ6yb2G + 7eb1r7xEWXfg0wcZMqTjs0t3jX206rSbMqcdIGpL7jibsZU0gXZkcxECAwEAAaOCAXwwggF4MAwG + A1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgOoMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMCMB0GA1Ud + DgQWBBSsoxuqualBzUgqSE3y9J1N0QDQ5zBdBgNVHR8EVjBUMFKgUKBOhkxodHRwOi8vcGtpLWNy + bC5zeW1hdXRoLmNvbS9jYV82YTkzNzczNGEzOTNhMDgwNWJmMzNjZGE4YjMzMTA5My9MYXRlc3RD + UkwuY3JsMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL3BraS1vY3NwLnN5bWF1 + dGguY29tMB8GA1UdIwQYMBaAFGtvS7bxN7orPH8Yzborsrl8KjfrMC0GCmCGSAGG+EUBEAMEHzAd + BhNghkgBhvhFARABAgMBAYGpkOEDFgY5NTc2MDgwOQYKYIZIAYb4RQEQBQQrMCkCAQAWJGFIUjBj + SE02THk5d2Eya3RjbUV1YzNsdFlYVjBhQzVqYjIwPTANBgkqhkiG9w0BAQsFAAOCAgEAFJpFt3s5 + T6Sjz3/yJs+6eAVuIr6lNGx7jLHeCIKHp/l4Y8KyiQPBcfP5PCo74+2HuCexEBzt7xAkwpRwo1aR + VTFAr8QZlWHo4OOf7Ah5aQcbVFFSv0epTEdwNbgwWROCdD/BYEWWNPLboELeMY58LV8R2qlaglWx + Fg2Ezn8/cqOZE2u/2Rp3djRW7eSvjy+XmOIBRQcgcKNRXebwPaUTq2W7TebWKwpj4E6EtFFTxGfg + 41EQub5HH4HWpIvwOMzVNLj0iocAR5VWGedYjzQ4GryGM/1bml1Y1IN2EGgytZxUng8XEy8XIMey + tOBbBEHLcwWPGOmES/wmod1qiEABx570S+a9ZOaNU01y1thCC6rkhLyeIOQKBm7z3d4+RqHRAIpr + mldxzKS2cOQloJyczp6yBjhh2F5mzmWapDvGGsY7FnBPu+5ylk9GvuN3+pRHrzNxF+6GrH2qFqH3 + CPafxQQAc/+sNFHD1bCGT4XeD5eeWhq+UEXBOfCzb7d4rXlftMAA4P8E1UnfwGzG0Qev5NGE4y0R + 0BPMnCHLsFahCMO9ddnVBqjNqgvxquddtCC54d17zg6VNc5wz6wIzHdeslg20lmsv7MOrlvWAX2P + 4HuZiKz3o/R7oJeqsg1jJBnyUK2ln37oqwkkYN8daHYH2LOb15YNBa6KUq6TOHrZSGE= + + Basware PEPPOL Test Access Point + arun.kumar@basware.com + + + + https://test-api.basware.com/peppol/as4 + + + + false + 2026-01-01T00:00:00.000Z + 2024-01-01T23:59:59.000Z + + MIIFvzCCA6egAwIBAgIQBV7uSJlbeLx9GB1tBD9qhTANBgkqhkiG9w0BAQsFADBrMQswCQYDVQQG + EwJCRTEZMBcGA1UEChMQT3BlblBFUFBPTCBBSVNCTDEWMBQGA1UECxMNRk9SIFRFU1QgT05MWTEp + MCcGA1UEAxMgUEVQUE9MIEFDQ0VTUyBQT0lOVCBURVNUIENBIC0gRzIwHhcNMjAwOTE0MDAwMDAw + WhcNMjIwOTA0MjM1OTU5WjBMMRIwEAYDVQQDDAlQT1AwMDAwMTAxFzAVBgNVBAsMDlBFUFBPTCBU + RVNUIEFQMRAwDgYDVQQKDAdCYXN3YXJlMQswCQYDVQQGEwJGSTCCASIwDQYJKoZIhvcNAQEBBQAD + ggEPADCCAQoCggEBALMwQO726jn0C2sQq8e16KVt/NRUaj5l/gOtfL0y0hEAQNRM8oM2E+LN0Gzn + 4T7sLOEgcgOrnw0Sx9PBhosJjeHQmp4FxXBcTdfhpe6jeBG7oK4r4qO+d4qnssm3tW25k75o/Ocy + baqWX6o9oEL9F4hMJ/cR53eINZuy/WlblEZSPrSCWhu2jH3fikbQ0yBbyneU1EQgtGYvnokg7pxy + gLBnbWgpOkrPH43Ug0t+4hromsHYMo/FKs2ATxP1aKCCxzrlytSu60mhu5ZRYS+RwIP0vGQ6yb2G + 7eb1r7xEWXfg0wcZMqTjs0t3jX206rSbMqcdIGpL7jibsZU0gXZkcxECAwEAAaOCAXwwggF4MAwG + A1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgOoMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMCMB0GA1Ud + DgQWBBSsoxuqualBzUgqSE3y9J1N0QDQ5zBdBgNVHR8EVjBUMFKgUKBOhkxodHRwOi8vcGtpLWNy + bC5zeW1hdXRoLmNvbS9jYV82YTkzNzczNGEzOTNhMDgwNWJmMzNjZGE4YjMzMTA5My9MYXRlc3RD + UkwuY3JsMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL3BraS1vY3NwLnN5bWF1 + dGguY29tMB8GA1UdIwQYMBaAFGtvS7bxN7orPH8Yzborsrl8KjfrMC0GCmCGSAGG+EUBEAMEHzAd + BhNghkgBhvhFARABAgMBAYGpkOEDFgY5NTc2MDgwOQYKYIZIAYb4RQEQBQQrMCkCAQAWJGFIUjBj + SE02THk5d2Eya3RjbUV1YzNsdFlYVjBhQzVqYjIwPTANBgkqhkiG9w0BAQsFAAOCAgEAFJpFt3s5 + T6Sjz3/yJs+6eAVuIr6lNGx7jLHeCIKHp/l4Y8KyiQPBcfP5PCo74+2HuCexEBzt7xAkwpRwo1aR + VTFAr8QZlWHo4OOf7Ah5aQcbVFFSv0epTEdwNbgwWROCdD/BYEWWNPLboELeMY58LV8R2qlaglWx + Fg2Ezn8/cqOZE2u/2Rp3djRW7eSvjy+XmOIBRQcgcKNRXebwPaUTq2W7TebWKwpj4E6EtFFTxGfg + 41EQub5HH4HWpIvwOMzVNLj0iocAR5VWGedYjzQ4GryGM/1bml1Y1IN2EGgytZxUng8XEy8XIMey + tOBbBEHLcwWPGOmES/wmod1qiEABx570S+a9ZOaNU01y1thCC6rkhLyeIOQKBm7z3d4+RqHRAIpr + mldxzKS2cOQloJyczp6yBjhh2F5mzmWapDvGGsY7FnBPu+5ylk9GvuN3+pRHrzNxF+6GrH2qFqH3 + CPafxQQAc/+sNFHD1bCGT4XeD5eeWhq+UEXBOfCzb7d4rXlftMAA4P8E1UnfwGzG0Qev5NGE4y0R + 0BPMnCHLsFahCMO9ddnVBqjNqgvxquddtCC54d17zg6VNc5wz6wIzHdeslg20lmsv7MOrlvWAX2P + 4HuZiKz3o/R7oJeqsg1jJBnyUK2ln37oqwkkYN8daHYH2LOb15YNBa6KUq6TOHrZSGE= + + Basware PEPPOL Test Access Point + arun.kumar@basware.com + + + + + + \ No newline at end of file -- GitLab From 7a066cb703f7e624decd43ab2803028dcf0e627a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asger=20Geel=20Weirs=C3=B8e?= Date: Mon, 22 Jan 2024 10:22:25 +0100 Subject: [PATCH 2/2] Version AP-v1.2-RC --- docs/nemhandel-edelivery.md | 741 ---------------- .../nemhandel-signature-resign-example.png | Bin 207018 -> 0 bytes docs/nemhandel-edelivery/oxalis-ER.png | Bin 80058 -> 0 bytes .../oxalis-overview-packages.png | Bin 42333 -> 0 bytes pom.xml | 45 +- .../oxalis/as4/EDeliverySpecification.java | 39 + .../as4/async/AsyncProcessingRunnable.java | 30 +- .../as4/async/ExecutorServiceAsyncModule.java | 2 +- .../oxalis/as4/async/ExecutorServiceConf.java | 2 +- .../async/ExecutorServiceLifecycleFilter.java | 2 +- .../MessageLevelResponseFactoryImpl.java | 6 +- .../documenttype/DocumentTypeConfig.java | 25 +- .../DocumentTypeConfigResolverImpl.java | 2 +- .../dk/erst/oxalis/as4/error/ErrorCodes.java | 195 +--- .../dk/erst/oxalis/as4/error/Process.java | 2 +- .../as4/handlers/GetInboxMessagesHandler.java | 2 +- .../as4/handlers/GetMessageLogHandler.java | 2 +- .../handlers/GetOutboxMessagesHandler.java | 60 +- .../as4/handlers/NemhandelPersister.java | 37 +- .../oxalis/as4/handlers/SendSbdHandler.java | 52 +- .../oxalis/as4/handlers/dto/ErrorMessage.java | 4 + .../as4/handlers/dto/ErrorResponse.java | 5 +- .../as4/handlers/dto/ListMessageResponse.java | 4 +- .../as4/handlers/dto/MessageAndResponses.java | 65 ++ .../as4/handlers/dto/MessageLogResponse.java | 2 +- .../oxalis/as4/handlers/dto/MessageModel.java | 26 +- .../handlers/dto/MessageModelResponse.java | 2 +- .../as4/handlers/dto/MessageResponse.java | 2 +- .../as4/handlers/dto/UpdateStatusRequest.java | 4 +- .../as4/handlers/dto/ValidationResult.java | 23 +- .../dk/erst/oxalis/as4/jdbc/JdbcConf.java | 12 +- .../dk/erst/oxalis/as4/jdbc/JdbcFilter.java | 2 +- .../dk/erst/oxalis/as4/jdbc/JdbcModule.java | 10 +- .../persistence/model/AccountReceiver.java | 25 +- .../oxalis/as4/persistence/model/Message.java | 43 + .../as4/persistence/model/MessageLog.java | 4 +- .../as4/persistence/model/MessageStatus.java | 3 +- .../as4/persistence/model/NemhandelLog.java | 33 +- .../as4/providers/NemhandelProvider.java | 2 - .../as4/rest/ForwardedHeaderFilter.java | 2 +- .../rest/openapi/CustomOpenApiServlet.java | 3 +- .../as4/rest/resources/InboxResource.java | 9 +- .../as4/rest/resources/OutboxResource.java | 126 ++- .../as4/signature/SignatureException.java | 54 ++ .../as4/signature/SignatureFactory.java | 11 +- .../as4/signature/SignatureFactoryImpl.java | 204 ++--- .../TransferDelegationException.java | 52 -- .../signature/TransferDelegationFactory.java | 24 - .../TransferDelegationFactoryImpl.java | 75 -- .../as4/signature/XAdESServiceProvider.java | 28 + .../XAdESSignatureParametersProvider.java | 46 + .../erst/oxalis/as4/util/DocumentSender.java | 14 +- .../dk/erst/oxalis/as4/util/DocumentUtil.java | 4 +- .../oxalis/as4/util/OxalisDocumentSender.java | 136 +-- .../oxalis/as4/util/SBDMessageContext.java | 2 +- .../dk/erst/oxalis/as4/util/XPathUtil.java | 20 +- .../as4/validation/MessageValidatorImpl.java | 16 +- .../as4/validation/ValidationModule.java | 60 +- .../registration/RegistrationValidator.java | 27 + .../RegistrationValidatorImpl.java | 115 +++ .../as4/validation/schema/SchemaCache.java | 18 + .../validation/schema/SchemaCacheLoader.java | 112 +++ .../as4/validation/schema/SchemaResolver.java | 7 +- .../validation/schema/SchemaResolverImpl.java | 29 +- .../schema/SchemaValidatorImpl.java | 5 +- .../as4/validation/signature/DSSModule.java | 72 +- .../SignatureValidationPolicyProvider.java | 11 +- .../signature/SignatureValidator.java | 5 +- .../signature/SignatureValidatorImpl.java | 361 ++------ .../validation/version/VersionValidator.java | 2 +- .../version/VersionValidatorImpl.java | 8 +- .../version/model/JodaTimeAdapter.java | 4 +- .../network/oxalis/as4/config/As4Conf.java | 2 +- ...NKSPU_Alphanumeric40LetterNonemptyText.xsd | 10 + .../2007/10/01/NKSPU_BICIdentifier.xsd | 11 + .../10/01/NKSPU_BankAccountUnavailable.xsd | 14 + .../01/NKSPU_BankAccountUnavailableCode.xsd | 10 + .../01/NKSPU_BankAccountUnavailableText.xsd | 5 + .../2007/10/01/NKSPU_ForeignBankAccount.xsd | 13 + .../01/NKSPU_ForeignBankAccountIdentifier.xsd | 10 + .../10/01/NKSPU_ForeignBankBranchCode.xsd | 11 + .../10/01/NKSPU_ForeignBankInfoStructure.xsd | 27 + .../01/NKSPU_ForeignPersonInfoStructure.xsd | 17 + .../2007/10/01/NKSPU_IBANIdentifier.xsd | 11 + .../2007/10/01/NKSPU_InvalidMessageFormat.xsd | 18 + .../10/01/NKSPU_InvalidMessageFormatCode.xsd | 10 + .../10/01/NKSPU_InvalidMessageFormatText.xsd | 5 + ...nvalidMessageFormathighestseverityCode.xsd | 10 + .../2007/10/01/NKSPU_LookupDateTime.xsd | 5 + .../10/01/NKSPU_NemkontoAccountHolder.xsd | 22 + .../01/NKSPU_NemkontoAccountInformation.xsd | 17 + .../2007/10/01/NKSPU_NemkontoCurrencyCode.xsd | 179 ++++ ...kontoPrivatUdbetalerTransporterRequest.xsd | 14 + ...ontoPrivatUdbetalerTransporterResponse.xsd | 19 + .../2007/10/01/NKSPU_NemkontoRequest.xsd | 20 + .../2007/10/01/NKSPU_NemkontoRequestError.xsd | 13 + .../10/01/NKSPU_NemkontoRequestErrorCode.xsd | 10 + .../10/01/NKSPU_NemkontoRequestErrorText.xsd | 5 + .../2007/10/01/NKSPU_NemkontoResponse.xsd | 20 + ..._ProductionUnitIdentificationStructure.xsd | 16 + .../schemas/2007/10/01/NKSPU_Requester.xsd | 19 + .../10/01/NKSPU_RequesterPaymentReference.xsd | 6 + ...KSPU_RequesterPrimaryInternalReference.xsd | 6 + ...PU_RequesterSecondaryInternalReference.xsd | 6 + .../NKSPU_SEnumberIdentificationStructure.xsd | 15 + .../2007/10/01/NKSPU_SEnumberIdentifier.xsd | 10 + .../NKSPU_TransporterAgreementIdentifier.xsd | 11 + .../01/NKSPU_TransporterGroupIdentifier.xsd | 7 + .../2007/10/01/NKSPU_TransporterHeader.xsd | 19 + .../10/01/NKSPU_TransporterHeaderError.xsd | 18 + .../01/NKSPU_TransporterHeaderErrorCode.xsd | 10 + .../01/NKSPU_TransporterHeaderErrorText.xsd | 5 + .../01/NKSPU_TransporterSystemIdentifier.xsd | 11 + .../schemas/2007/10/01/NKSPU_VansHeader.xsd | 15 + .../01/NKSPU_VansNemkontoEnvironmentCode.xsd | 11 + .../2007/10/01/NKSPU_VansRecipientAddress.xsd | 6 + .../2007/10/01/NKSPU_VansSenderAddress.xsd | 15 + ...hanumeric40letternonemptytext.xsd.meta.xml | 9 + ...numeric40letternonemptytext.xsd.system.xml | 6 + .../nkspu_bankaccountunavailable.xsd.meta.xml | 16 + ...kspu_bankaccountunavailable.xsd.system.xml | 6 + ...pu_bankaccountunavailablecode.xsd.meta.xml | 15 + ..._bankaccountunavailablecode.xsd.system.xml | 6 + ...pu_bankaccountunavailabletext.xsd.meta.xml | 16 + ..._bankaccountunavailabletext.xsd.system.xml | 6 + .../10/01/nkspu_bicidentifier.xsd.meta.xml | 23 + .../10/01/nkspu_bicidentifier.xsd.system.xml | 6 + .../01/nkspu_foreignbankaccount.xsd.meta.xml | 16 + .../nkspu_foreignbankaccount.xsd.system.xml | 6 + ..._foreignbankaccountidentifier.xsd.meta.xml | 12 + ...oreignbankaccountidentifier.xsd.system.xml | 6 + .../nkspu_foreignbankbranchcode.xsd.meta.xml | 9 + ...nkspu_foreignbankbranchcode.xsd.system.xml | 6 + ...kspu_foreignbankinfostructure.xsd.meta.xml | 13 + ...pu_foreignbankinfostructure.xsd.system.xml | 6 + ...pu_foreignpersoninfostructure.xsd.meta.xml | 13 + ..._foreignpersoninfostructure.xsd.system.xml | 6 + .../10/01/nkspu_ibanidentifier.xsd.meta.xml | 27 + .../10/01/nkspu_ibanidentifier.xsd.system.xml | 6 + .../nkspu_invalidmessageformat.xsd.meta.xml | 13 + .../nkspu_invalidmessageformat.xsd.system.xml | 6 + ...kspu_invalidmessageformatcode.xsd.meta.xml | 13 + ...pu_invalidmessageformatcode.xsd.system.xml | 6 + ...sageformathighestseveritycode.xsd.meta.xml | 13 + ...geformathighestseveritycode.xsd.system.xml | 6 + ...kspu_invalidmessageformattext.xsd.meta.xml | 13 + ...pu_invalidmessageformattext.xsd.system.xml | 6 + .../10/01/nkspu_lookupdatetime.xsd.meta.xml | 13 + .../10/01/nkspu_lookupdatetime.xsd.system.xml | 6 + .../nkspu_nemkontoaccountholder.xsd.meta.xml | 13 + ...nkspu_nemkontoaccountholder.xsd.system.xml | 6 + ...pu_nemkontoaccountinformation.xsd.meta.xml | 13 + ..._nemkontoaccountinformation.xsd.system.xml | 6 + .../nkspu_nemkontocurrencycode.xsd.meta.xml | 9 + .../nkspu_nemkontocurrencycode.xsd.system.xml | 6 + ...atudbetalertransporterrequest.xsd.meta.xml | 13 + ...udbetalertransporterrequest.xsd.system.xml | 6 + ...tudbetalertransporterresponse.xsd.meta.xml | 13 + ...dbetalertransporterresponse.xsd.system.xml | 6 + .../10/01/nkspu_nemkontorequest.xsd.meta.xml | 13 + .../01/nkspu_nemkontorequest.xsd.system.xml | 6 + .../nkspu_nemkontorequesterror.xsd.meta.xml | 13 + .../nkspu_nemkontorequesterror.xsd.system.xml | 6 + ...kspu_nemkontorequesterrorcode.xsd.meta.xml | 13 + ...pu_nemkontorequesterrorcode.xsd.system.xml | 6 + ...kspu_nemkontorequesterrortext.xsd.meta.xml | 13 + ...pu_nemkontorequesterrortext.xsd.system.xml | 6 + .../10/01/nkspu_nemkontoresponse.xsd.meta.xml | 13 + .../01/nkspu_nemkontoresponse.xsd.system.xml | 6 + ...onunitidentificationstructure.xsd.meta.xml | 9 + ...unitidentificationstructure.xsd.system.xml | 6 + .../2007/10/01/nkspu_requester.xsd.meta.xml | 13 + .../2007/10/01/nkspu_requester.xsd.system.xml | 6 + ...spu_requesterpaymentreference.xsd.meta.xml | 12 + ...u_requesterpaymentreference.xsd.system.xml | 6 + ...esterprimaryinternalreference.xsd.meta.xml | 13 + ...terprimaryinternalreference.xsd.system.xml | 6 + ...tersecondaryinternalreference.xsd.meta.xml | 12 + ...rsecondaryinternalreference.xsd.system.xml | 6 + ...numberidentificationstructure.xsd.meta.xml | 9 + ...mberidentificationstructure.xsd.system.xml | 6 + .../01/nkspu_senumberidentifier.xsd.meta.xml | 13 + .../nkspu_senumberidentifier.xsd.system.xml | 6 + ...ransporteragreementidentifier.xsd.meta.xml | 13 + ...nsporteragreementidentifier.xsd.system.xml | 6 + ...pu_transportergroupidentifier.xsd.meta.xml | 13 + ..._transportergroupidentifier.xsd.system.xml | 6 + .../01/nkspu_transporterheader.xsd.meta.xml | 13 + .../01/nkspu_transporterheader.xsd.system.xml | 6 + .../nkspu_transporterheadererror.xsd.meta.xml | 13 + ...kspu_transporterheadererror.xsd.system.xml | 6 + ...pu_transporterheadererrorcode.xsd.meta.xml | 13 + ..._transporterheadererrorcode.xsd.system.xml | 6 + ...pu_transporterheadererrortext.xsd.meta.xml | 13 + ..._transporterheadererrortext.xsd.system.xml | 6 + ...u_transportersystemidentifier.xsd.meta.xml | 13 + ...transportersystemidentifier.xsd.system.xml | 6 + .../2007/10/01/nkspu_vansheader.xsd.meta.xml | 13 + .../10/01/nkspu_vansheader.xsd.system.xml | 6 + ...u_vansnemkontoenvironmentcode.xsd.meta.xml | 13 + ...vansnemkontoenvironmentcode.xsd.system.xml | 6 + .../nkspu_vansrecipientaddress.xsd.meta.xml | 13 + .../nkspu_vansrecipientaddress.xsd.system.xml | 6 + .../01/nkspu_vanssenderaddress.xsd.meta.xml | 13 + .../01/nkspu_vanssenderaddress.xsd.system.xml | 6 + .../xml/schemas/2006/05/01/EBMS_Common.xsd | 137 +++ .../2006/05/01/SWIFT_A_GeneralInformation.xsd | 12 + ...FT_B_OriginalGroupReferenceInformation.xsd | 39 + .../01/SWIFT_C_OriginalPaymentInformation.xsd | 22 + .../xml/schemas/2006/05/01/SWIFT_Common.xsd | 502 +++++++++++ ...ansactionReferenceInformationAndStatus.xsd | 55 ++ ...SWIFT_E_OriginalTransactionInformation.xsd | 18 + .../schemas/2006/05/01/SWIFT_NKSPayment.xsd | 252 ++++++ .../xml/schemas/2006/05/01/SWIFT_Package.xsd | 9 + .../xml/schemas/2006/05/01/NKS_NKSPayment.xsd | 21 + .../schemas/2006/05/01/NKS_NKSReceipt0.xsd | 15 + .../schemas/2006/05/01/NKS_NKSReceipt1.xsd | 15 + .../schemas/2006/05/01/NKS_NKSResponse2.xsd | 22 + .../schemas/2006/05/01/NKS_NKSResponse5.xsd | 22 + .../schemas/2006/05/01/NKS_NKSResponse7.xsd | 17 + .../schemas/2006/05/01/NKS_NKSResponse8.xsd | 17 + .../schemas/2006/05/01/NKS_NKSResponse9.xsd | 17 + .../other/CPR_CompletePostalLabelText.xsd | 9 + .../CPR_PersonCivilRegistrationIdentifier.xsd | 42 + .../other/CPR_SecondaryPostalLabel.xsd | 23 + .../other/CVR_CVRnumberIdentifier.xsd | 14 + .../other/CVR_ProductionUnitIdentifier.xsd | 13 + .../other/DKCC_CountryIdentificationCode.xsd | 59 ++ .../other/DKCC_PostalAddressFifthLineText.xsd | 6 + .../other/DKCC_PostalAddressFirstLineText.xsd | 6 + .../DKCC_PostalAddressFourthLineText.xsd | 6 + .../DKCC_PostalAddressSecondLineText.xsd | 6 + .../other/DKCC_PostalAddressSixthLineText.xsd | 6 + .../other/DKCC_PostalAddressThirdLineText.xsd | 6 + .../NemKonto/other/EAN_EAN13Identifier.xsd | 9 + .../other/ITST_BankAccountIdentifier.xsd | 9 + .../other/ITST_BankAccountStructure.xsd | 12 + .../other/ITST_BankBranchIdentifier.xsd | 9 + .../cpr_completepostallabeltext.xsd.meta.xml | 1 + ...cpr_completepostallabeltext.xsd.system.xml | 2 + ...oncivilregistrationidentifier.xsd.meta.xml | 1 + ...civilregistrationidentifier.xsd.system.xml | 2 + .../cpr_secondarypostallabel.xsd.meta.xml | 1 + .../cpr_secondarypostallabel.xsd.system.xml | 2 + .../cvr_cvrnumberidentifier.xsd.meta.xml | 1 + .../cvr_cvrnumberidentifier.xsd.system.xml | 2 + .../cvr_productionunitidentifier.xsd.meta.xml | 1 + ...vr_productionunitidentifier.xsd.system.xml | 2 + ...kcc_countryidentificationcode.xsd.meta.xml | 1 + ...c_countryidentificationcode.xsd.system.xml | 2 + ...cc_postaladdressfifthlinetext.xsd.meta.xml | 2 + ..._postaladdressfifthlinetext.xsd.system.xml | 2 + ...cc_postaladdressfirstlinetext.xsd.meta.xml | 2 + ..._postaladdressfirstlinetext.xsd.system.xml | 2 + ...c_postaladdressfourthlinetext.xsd.meta.xml | 4 + ...postaladdressfourthlinetext.xsd.system.xml | 2 + ...c_postaladdresssecondlinetext.xsd.meta.xml | 3 + ...postaladdresssecondlinetext.xsd.system.xml | 2 + ...cc_postaladdresssixthlinetext.xsd.meta.xml | 1 + ..._postaladdresssixthlinetext.xsd.system.xml | 2 + ...cc_postaladdressthirdlinetext.xsd.meta.xml | 2 + ..._postaladdressthirdlinetext.xsd.system.xml | 2 + .../other/ean_ean13identifier.xsd.meta.xml | 3 + .../other/ean_ean13identifier.xsd.system.xml | 2 + .../itst_bankaccountidentifier.xsd.meta.xml | 1 + .../itst_bankaccountidentifier.xsd.system.xml | 2 + .../itst_bankaccountstructure.xsd.meta.xml | 1 + .../itst_bankaccountstructure.xsd.system.xml | 2 + .../itst_bankbranchidentifier.xsd.meta.xml | 1 + .../itst_bankbranchidentifier.xsd.system.xml | 2 + .../NemKonto/other/nemhandel/CVR_full.xsd | 7 + .../NemKonto/other/nemhandel/DKCC_full.xsd | 12 + .../NemKonto/other/nemhandel/README.txt | 9 + .../resources/META-INF/Schemas/catalog.xml | 128 +++ .../transferDelegation.xsd | 42 - .../META-INF/Schematron/NemKonto/NemKonto.xsl | Bin 0 -> 2810 bytes .../resources/db/oxalis-as4-db-changelog.xml | 22 +- src/main/resources/logback.xml | 13 - src/main/resources/reference.conf | 289 +++++- .../dk/erst/oxalis/as4/AbstractXmlTest.java | 51 +- .../erst/oxalis/as4/NemhandelModuleTests.java | 100 +++ .../AsyncValidationRunnableImplTest.java | 216 +++-- .../MessageLevelResponseFactoryTest.java | 9 +- .../documenttype/DocumentTypeModuleTest.java | 275 +++++- .../erst/oxalis/as4/error/ErrorCodesTest.java | 8 +- .../handlers/GetOutboxMessageHandlerTest.java | 107 +++ .../NemhandelPersisterHandlerTest.java | 58 +- .../as4/handlers/SendSbdHandlerTest.java | 31 +- .../handlers/dto/ValidationResultTest.java | 56 ++ .../erst/oxalis/as4/jdbc/LiquibaseTest.java | 59 ++ .../as4/rest/resources/AbstractJaxRsTest.java | 5 - .../resources/ForwardedHeaderFilterTest.java | 2 - .../as4/rest/resources/InboxResourceTest.java | 35 +- .../rest/resources/OutboxResourceTest.java | 135 +++ .../as4/util/OxalisDocumentSenderTest.java | 326 +++---- .../as4/validation/MessageValidatorTest.java | 7 +- .../as4/validation/ValidationModuleTest.java | 83 +- .../RegistrationValidatorTest.java | 127 +++ .../schema/SchemaCacheLoaderTest.java | 69 ++ .../validation/schema/SchemaResolverTest.java | 70 +- .../schema/SchemaValidatorTest.java | 59 +- .../schematron/SchematronValidatorTest.java | 50 ++ .../validation/signature/DSSModuleTest.java | 14 +- .../signature/SignatureFactoryTest.java | 397 +++------ ...SignatureValidationPolicyProviderTest.java | 23 + .../signature/SignatureValidatorTest.java | 833 +++++------------- .../signature/TransferDelegationTest.java | 63 -- .../version/VersionValidatorTest.java | 73 +- .../model/EdelComponentVersionListTest.java | 30 +- .../resources/cef-sbd-with-identifiers.xml | 250 ++++++ .../resources/cef-sbd-without-identifiers.xml | 249 ++++++ src/test/resources/dummy-db-changelog.xml | 24 + src/test/resources/largefile.txt | 1 + .../nemkonto-examples/Kvittering 0.xml | 29 + .../nemkonto-examples/Kvittering 1.xml | 24 + .../Payment_GLN_5798009811578.xml | 101 +++ ...CPR-nr - DK konto og ingen kontonummer.XML | 99 +++ .../Request - CVR-nr SE-nr P-nr.xml | 74 ++ .../Retursvar 2 med ACPT.xml | 44 + .../Retursvar 5 med PART.xml | 51 ++ .../nemkonto-examples/Retursvar 7.xml | 94 ++ .../nemkonto-examples/Retursvar 8.xml | 86 ++ .../nemkonto-examples/Retursvar 9.xml | 71 ++ .../oxalis.conf | 144 +++ .../schematron-preload-conf/oxalis.conf | 10 +- src/test/resources/reference.conf | 170 +++- ...nt_Does_Not_Equal_PayableAmount_BZ1425.xml | 2 +- 327 files changed, 8249 insertions(+), 3391 deletions(-) delete mode 100644 docs/nemhandel-edelivery/nemhandel-signature-resign-example.png delete mode 100644 docs/nemhandel-edelivery/oxalis-ER.png delete mode 100644 docs/nemhandel-edelivery/oxalis-overview-packages.png create mode 100644 src/main/java/dk/erst/oxalis/as4/EDeliverySpecification.java create mode 100644 src/main/java/dk/erst/oxalis/as4/handlers/dto/MessageAndResponses.java create mode 100644 src/main/java/dk/erst/oxalis/as4/signature/SignatureException.java delete mode 100644 src/main/java/dk/erst/oxalis/as4/signature/TransferDelegationException.java delete mode 100644 src/main/java/dk/erst/oxalis/as4/signature/TransferDelegationFactory.java delete mode 100644 src/main/java/dk/erst/oxalis/as4/signature/TransferDelegationFactoryImpl.java create mode 100644 src/main/java/dk/erst/oxalis/as4/signature/XAdESServiceProvider.java create mode 100644 src/main/java/dk/erst/oxalis/as4/signature/XAdESSignatureParametersProvider.java create mode 100644 src/main/java/dk/erst/oxalis/as4/validation/registration/RegistrationValidator.java create mode 100644 src/main/java/dk/erst/oxalis/as4/validation/registration/RegistrationValidatorImpl.java create mode 100644 src/main/java/dk/erst/oxalis/as4/validation/schema/SchemaCache.java create mode 100644 src/main/java/dk/erst/oxalis/as4/validation/schema/SchemaCacheLoader.java create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_Alphanumeric40LetterNonemptyText.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_BICIdentifier.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_BankAccountUnavailable.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_BankAccountUnavailableCode.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_BankAccountUnavailableText.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_ForeignBankAccount.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_ForeignBankAccountIdentifier.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_ForeignBankBranchCode.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_ForeignBankInfoStructure.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_ForeignPersonInfoStructure.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_IBANIdentifier.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_InvalidMessageFormat.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_InvalidMessageFormatCode.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_InvalidMessageFormatText.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_InvalidMessageFormathighestseverityCode.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_LookupDateTime.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoAccountHolder.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoAccountInformation.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoCurrencyCode.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoPrivatUdbetalerTransporterRequest.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoPrivatUdbetalerTransporterResponse.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoRequest.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoRequestError.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoRequestErrorCode.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoRequestErrorText.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoResponse.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_ProductionUnitIdentificationStructure.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_Requester.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_RequesterPaymentReference.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_RequesterPrimaryInternalReference.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_RequesterSecondaryInternalReference.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_SEnumberIdentificationStructure.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_SEnumberIdentifier.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_TransporterAgreementIdentifier.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_TransporterGroupIdentifier.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_TransporterHeader.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_TransporterHeaderError.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_TransporterHeaderErrorCode.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_TransporterHeaderErrorText.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_TransporterSystemIdentifier.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_VansHeader.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_VansNemkontoEnvironmentCode.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_VansRecipientAddress.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_VansSenderAddress.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_alphanumeric40letternonemptytext.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_alphanumeric40letternonemptytext.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bankaccountunavailable.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bankaccountunavailable.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bankaccountunavailablecode.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bankaccountunavailablecode.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bankaccountunavailabletext.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bankaccountunavailabletext.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bicidentifier.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bicidentifier.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankaccount.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankaccount.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankaccountidentifier.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankaccountidentifier.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankbranchcode.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankbranchcode.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankinfostructure.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankinfostructure.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignpersoninfostructure.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignpersoninfostructure.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_ibanidentifier.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_ibanidentifier.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformat.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformat.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformatcode.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformatcode.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformathighestseveritycode.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformathighestseveritycode.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformattext.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformattext.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_lookupdatetime.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_lookupdatetime.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoaccountholder.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoaccountholder.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoaccountinformation.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoaccountinformation.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontocurrencycode.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontocurrencycode.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoprivatudbetalertransporterrequest.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoprivatudbetalertransporterrequest.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoprivatudbetalertransporterresponse.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoprivatudbetalertransporterresponse.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequest.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequest.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequesterror.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequesterror.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequesterrorcode.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequesterrorcode.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequesterrortext.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequesterrortext.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoresponse.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoresponse.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_productionunitidentificationstructure.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_productionunitidentificationstructure.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requester.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requester.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requesterpaymentreference.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requesterpaymentreference.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requesterprimaryinternalreference.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requesterprimaryinternalreference.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requestersecondaryinternalreference.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requestersecondaryinternalreference.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_senumberidentificationstructure.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_senumberidentificationstructure.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_senumberidentifier.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_senumberidentifier.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporteragreementidentifier.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporteragreementidentifier.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transportergroupidentifier.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transportergroupidentifier.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheader.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheader.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheadererror.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheadererror.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheadererrorcode.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheadererrorcode.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheadererrortext.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheadererrortext.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transportersystemidentifier.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transportersystemidentifier.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vansheader.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vansheader.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vansnemkontoenvironmentcode.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vansnemkontoenvironmentcode.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vansrecipientaddress.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vansrecipientaddress.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vanssenderaddress.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vanssenderaddress.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto.ebms/xml/schemas/2006/05/01/EBMS_Common.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_A_GeneralInformation.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_B_OriginalGroupReferenceInformation.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_C_OriginalPaymentInformation.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_Common.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_D_OriginalTransactionReferenceInformationAndStatus.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_E_OriginalTransactionInformation.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_NKSPayment.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_Package.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSPayment.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSReceipt0.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSReceipt1.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse2.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse5.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse7.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse8.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse9.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/CPR_CompletePostalLabelText.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/CPR_PersonCivilRegistrationIdentifier.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/CPR_SecondaryPostalLabel.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/CVR_CVRnumberIdentifier.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/CVR_ProductionUnitIdentifier.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/DKCC_CountryIdentificationCode.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/DKCC_PostalAddressFifthLineText.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/DKCC_PostalAddressFirstLineText.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/DKCC_PostalAddressFourthLineText.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/DKCC_PostalAddressSecondLineText.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/DKCC_PostalAddressSixthLineText.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/DKCC_PostalAddressThirdLineText.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/EAN_EAN13Identifier.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/ITST_BankAccountIdentifier.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/ITST_BankAccountStructure.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/ITST_BankBranchIdentifier.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/cpr_completepostallabeltext.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/cpr_completepostallabeltext.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/cpr_personcivilregistrationidentifier.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/cpr_personcivilregistrationidentifier.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/cpr_secondarypostallabel.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/cpr_secondarypostallabel.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/cvr_cvrnumberidentifier.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/cvr_cvrnumberidentifier.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/cvr_productionunitidentifier.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/cvr_productionunitidentifier.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_countryidentificationcode.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_countryidentificationcode.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressfifthlinetext.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressfifthlinetext.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressfirstlinetext.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressfirstlinetext.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressfourthlinetext.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressfourthlinetext.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdresssecondlinetext.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdresssecondlinetext.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdresssixthlinetext.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdresssixthlinetext.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressthirdlinetext.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressthirdlinetext.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/ean_ean13identifier.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/ean_ean13identifier.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/itst_bankaccountidentifier.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/itst_bankaccountidentifier.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/itst_bankaccountstructure.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/itst_bankaccountstructure.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/itst_bankbranchidentifier.xsd.meta.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/itst_bankbranchidentifier.xsd.system.xml create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/nemhandel/CVR_full.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/nemhandel/DKCC_full.xsd create mode 100644 src/main/resources/META-INF/Schemas/NemKonto/other/nemhandel/README.txt create mode 100644 src/main/resources/META-INF/Schemas/catalog.xml delete mode 100644 src/main/resources/META-INF/Schemas/transfer-delegation/transferDelegation.xsd create mode 100644 src/main/resources/META-INF/Schematron/NemKonto/NemKonto.xsl delete mode 100644 src/main/resources/logback.xml create mode 100644 src/test/java/dk/erst/oxalis/as4/NemhandelModuleTests.java create mode 100644 src/test/java/dk/erst/oxalis/as4/handlers/dto/ValidationResultTest.java create mode 100644 src/test/java/dk/erst/oxalis/as4/jdbc/LiquibaseTest.java create mode 100644 src/test/java/dk/erst/oxalis/as4/validation/registration/RegistrationValidatorTest.java create mode 100644 src/test/java/dk/erst/oxalis/as4/validation/schema/SchemaCacheLoaderTest.java create mode 100644 src/test/java/dk/erst/oxalis/as4/validation/signature/SignatureValidationPolicyProviderTest.java delete mode 100644 src/test/java/dk/erst/oxalis/as4/validation/signature/TransferDelegationTest.java create mode 100644 src/test/resources/cef-sbd-with-identifiers.xml create mode 100644 src/test/resources/cef-sbd-without-identifiers.xml create mode 100644 src/test/resources/dummy-db-changelog.xml create mode 100644 src/test/resources/largefile.txt create mode 100644 src/test/resources/nemkonto-examples/Kvittering 0.xml create mode 100644 src/test/resources/nemkonto-examples/Kvittering 1.xml create mode 100644 src/test/resources/nemkonto-examples/Payment_GLN_5798009811578.xml create mode 100644 src/test/resources/nemkonto-examples/Reply - CPR-nr - DK konto og ingen kontonummer.XML create mode 100644 src/test/resources/nemkonto-examples/Request - CVR-nr SE-nr P-nr.xml create mode 100644 src/test/resources/nemkonto-examples/Retursvar 2 med ACPT.xml create mode 100644 src/test/resources/nemkonto-examples/Retursvar 5 med PART.xml create mode 100644 src/test/resources/nemkonto-examples/Retursvar 7.xml create mode 100644 src/test/resources/nemkonto-examples/Retursvar 8.xml create mode 100644 src/test/resources/nemkonto-examples/Retursvar 9.xml create mode 100644 src/test/resources/oxalis_home/configure-liquibase-changelog-conf/oxalis.conf diff --git a/docs/nemhandel-edelivery.md b/docs/nemhandel-edelivery.md index 2b4d7b0..09f9960 100644 --- a/docs/nemhandel-edelivery.md +++ b/docs/nemhandel-edelivery.md @@ -1,743 +1,2 @@ -# OBS: This was written for AP1.0 and is conserved for historical purposes. Find the up-to-date version here: https://rep.erst.dk/git/openebusiness/common/-/blob/master/guidelines/Developer_Documentation.pdf - - -# NemHandel eDelivery - -## 1 Introduction -This document describes the reference implemenation (RI) of the Access Point (AP) from a technical and code-centric perspective. -For an introduction to the concepts and the 4-corner model that the RI is a part of, please refer to https://rep.erst.dk/git/openebusiness/common/-/blob/master/guidelines/vejledning_-_forretning_og_forvaltning.pdf (in Danish). - -This RI is a basic, generic implementation of an AP that can be compiled, installed and used 'as-is' in a Peppol or a Nemhandel eDelivery infrastructure. -The basic installation and configuration procedures are described in https://rep.erst.dk/git/openebusiness/common/-/blob/master/guidelines/vejledning_-_drift.pdf - -However, a service vendor may want to customize the RI and add new features, or implement other changes to the codebase that provide business value in context of the specific service provider. - -For instance, the service provider may choose to expose the APIs allowing Corners C1 to send and C4 to receive documents via the RI in an alternative manner - for example by allowing C1 and C4 to send/receive document via shared folders. The RI is not capable of this out-of-the-box but it is an example of some new functionality that a service vendor may choose to add if it makes sense and adds value. - -This document is targeted towards the development team that has cloned the code repository provided by ERST in order to implement new features and add new functionality to the codebase in context of their own business. To be able to do that, the team needs to understand the structure of the codebase, the various parts of it, and where and how to hook in to add new features. - -The codebase contains a number of key components that implement various key parts of the overall functionality of the RI: - -![Oxalis ER-diagram](nemhandel-edelivery/oxalis-overview-packages.png) - -These key areas are described in the sections below: - -* Data model - and how the RI stores information in the relationel database -* REST API - and how the RI exposes and 'Outbox' API to C1 and an 'Inbox' API to C4 -* Validation - and how the RI validates document in C2 before sending them to C3, and how documents are validated in C3 -* Signing - and how the RI handles signatures and transfer delegations -* Async processing - and how C3 implements Schematron-validation asynchronously when receiving documents from C2 -* AS4-endpoint - and how C3 exposes the endpoint that is capable of receiving documents from C2 according to the AS4-protocol. - -In addition to this document, the REST APIs exposed by RI are documented via Swagger and can be reached at /openapi-ui and at /openapi.json once the RI is up and running. -The Swagger documentation is also available [here](https://rep.erst.dk/git/openebusiness/common/-/blob/master/guidelines/api/ap-openapi-docs.json). - -## 1.1 Technologies and requirements -To compile and build the RI, you need: - -* Java JDK 8 Update >= 252. The RI has been developed using Eclipse Temurin OpenJDK 8 -* Maven >= 3.1.1 - -To run the RI, you also need: - -* Tomcat 9 -* MySQL 8 or another relational database which is supported by one of the Hibernate SQL-dialects. The RI has been developed and tested using MySQL 8. -* An operating system that supports Tomcat 9 and MySQL 8 - the RI has been developed using both Oracle Linux Server 7.9 and Windows 10. - - -## 2 The codebase - The codebase for the RI consists of two repos provided by ERST: - -* https://rep.erst.dk/git/openebusiness/nemhandeledelivery/oxalis -* https://rep.erst.dk/git/openebusiness/nemhandeledelivery/oxalis-as4 - -The content of these repos is based on the Oxalis repos: - -* https://github.com/OxalisCommunity/oxalis -* https://github.com/OxalisCommunity/Oxalis-AS4 - -The Oxalis-based repos provide a RI which can be used only in a Peppol eDelivery infrastructure, whereas the ERST-based repos provide a RI which can be used in either a Peppol- or a Nemhandel eDelivery infrastructure - depending on its configuration. -To enable the RI to be part of both infrastructures, ERST has added a set of new features: - -* Support for MitID Erhverv signatures (FOCES3) - allowing an RI to identify and encrypt/decrypt using FOCES3 when transferring documents from C2 to C3 via the AS4-protocol -* Support for NemHandel eDelivery transport profile `nemhandel-transport-as4-v2_0` -* Synchronous Schema- and Schematron-Validation in C2 before sending a document -* Synchronous Schema-validation and asynchronous Schematron-validation in C3 when receiving a document -* Support for tracability using transfer delegations when handing over documents between corners - -ERST has also added some features that to allow the RI to be flexible and technology-agnostic and to allow the RI to fit into various infrastructures depending on the service vendor's preferred technologies: - -* REST APIs to make it easy for C1 and C4 to use the RI -* Storing documents and corresponding document-related logging in a relational DB for added control - - -### 2.1 Guice -Guice s a lightweight dependency injection framework for Java that allow developers to decouple the implementation of new features from the existing codebase thus making it easier to maintain the implementation of features as codebase evolves. -Guice is used throughout Oxalis codebase and has also been used by ERST when implementing new features on top of the Oxalis. In the sections below describing each of the features that ERST has added to the codebase, we refer to the Guice modules that have been used to implement each feature. -We encourage service vendors to actively use Guice when implementing new features to make it easier to maintain the codebase and easier to absorb upstream changes to the ERST codebase. - -For more information on Guice - please refer to https://github.com/google/guice/wiki/Motivation - -Generally, all the new functionality is, wherever possible, added as classes under the `dk.erst.oxalis.as4` package -to separate it from the standard Oxalis-AS4 codebase. Some minor changes are necessary in the Oxalis code itself, but we have -strived to separate our code from Oxalis' code, which simplifies pulling in upstream changes. To this end, there is a `NemHandelModule` which is -considered a "top-level" Guice module for all additions to Oxalis - meaning this module is the only module that is -directly configured in the `references.conf` module configuration, and our "sub-modules" are all installed via this -module. - -## 3 Key components - -### 3.1 Data model -To facilitate the REST API, a set of tables a relational database have been introduced. The tables store inbound and outbound -messages (documents) and corresponding log-entries, as well as account information allowing the RI to handle basic multi-tenancy where multiple C1s can use a single instance of an RI to send documents. -Out-of-the-box, the code is configured to use an in-memory H2 database, but this can and should be changed to use another database. - -> **_NOTE:_** We do not recommend using the H2 database in a production-like environment. See below for instructions how to switch -> to another database. - -The RI uses Liquibase to handle database schema changes. The database is automatically rolled forward on -application startup. To maximize database compatibility, we use Liquibase's XML-based changelog format. -Liquibase uses the same JDBC connection string and username/password as the running application, so the user needs the proper -schema privileges to make changes to tables etc. - -In Liquibase, database changes are added as changesets that represents some change to the database schema. Each -changeset is an independent change to the schema, for example 'add a new table' or 'add a new column to an existing table'. -Changesets can be qualified with a context to facilitate changesets that should only run in specific situations (e.g. -environment-specific data or unit test data etc). -Currently, there are no environment-specific Liquibase changesets except some for creating some Accounts for testing purposes, but in case this will be -relevant later, the code is prepared to use Liquibase's contexts feature to allow qualifying which changesets to run. -See the Configuration section below for how to specify it for a given environment. - -Technically, Hibernate/JPA is used through the Guice-persist module. This is configured in `JdbcModule`. -This allows us to work more logically with the data-model instead of raw JDBC Result sets. All entities inherits from -`AbstractEntity`, which handles equality/hashCode and audit columns. There is -an `AuditEntityListener` hooked up on the entity base class `AbstractEntity` which handles the -audit columns in all entities (created_by, created_date, updated_by, updated_date). - -The image below shows an ER diagram of the datamodel. - -![Oxalis ER-diagram](nemhandel-edelivery/oxalis-ER.png) - -A short description of each table is given below: - -| Table | Description | -|-----------------------|-------------------------------------------------------------------------------------------------------------------------------------------| -| Account | Table holding user accounts that can send/receive through the access point | -| Account_Receiver | Maps participant identifier to user accounts. This is used to match received documents to user accounts | -| Message | Holds metadata about the document being sent/received, including status of the message | -| Message_Content | Holds the actual content of a given document, both actual documents and receipts | -| databasechangelog | Liquibase system table that keeps track of which changesets have been applied to the database | -| databasechangeloglock | Liquibase system table which is used to handle concurrency and ensure only a single liquibase instance is updating the schema at the time | - - -#### 3.1.1 Database connection -You can customize the database configuration if you do not want to use the in-memory -database. The following example shows how you can configure the system to use a MySQL database: -``` -jdbc { - url: jdbc:mysql:... - driver.class.name: com.mysql.cj.jdbc.Driver - user: db-user - password: foobar - hibernate.dialect: org.hibernate.dialect.MySQL8Dialect - # connection pool size - pool.size: 10 - # liquibase contexts (defaults to none) - liquibase.contexts: some-custom-contexts -} -``` - -## 3.2 REST API -A REST API is exposed to facilitate both inbound and outbound message delivery. This allows C1 to send a message through the C2-part of the AP by calling the 'Outbox'-part of the REST API, and C4 can -fetch incoming documents from the C3-part of the AP via the 'Inbox'-part of the REST API. -The REST api is set up in the `ResteasyModule`. This REST api is implemented using the Resteasy library (which is an implementation of JAX-RS). Resteasy is integrated -with Guice through the `ResteasyServlet`. - -The API is protected by simple HTTP Basic Authentication which allows for simple multi-tenancy. This is achieved -via a simple Servlet Filter `SecurityFilter` which handles the HTTP basic Authentication and validates the supplied -credentials against the Account table. Password storage/hashing uses PBKDF2-HMAC-SHA256 based on the recommendations -from OWASP (see `PasswordHasher` for details). - -The logic behind each API endpoint is implemented in a vertical fashion, meaning there is a handler class that is -specific to a given endpoint, which implements the logic required, see f.x. `SendSbdHandler`. This allows an easy -overview of what code is relevant for a given endpoint. - -The API is documented via OpenAPI 3.0 and is exposed at the following urls when running locally: - -* OpenAPI Specification: http://localhost:8080/oxalis/openapi.json -* Swagger UI: http://localhost:8080/oxalis/openapi-ui - -## 3.3 Validation - using Schema and Schematron -Messages can be validated using a `MessageValidator`. The default Message validator will perform inspection of the -message content to determine which document type the message contains (e.g. an Invoice or an Order). This is done using -a `DocumentTypeResolver` which will return a `DocumentTypeConfig` configuration object that specifies which XSD to use -for schema validation and which Schematron files (if any) to use for validation of the actual message content. - -The `DocumentTypeResolver` will attempt to detect the document type using the following -1. The namespace and local name of the root element of the payload (e.g. `urn:oasis:names:specification:ubl:schema:xsd:Invoice-2` and `Invoice` respectively) -2. Zero or more XPath-based "identifier discriminators". Each discriminator consists of an XPath expression and an expected value or regex pattern. This is often used to match on the `CustomizationID` element (e.g. `CustomizationID = OIOUBL-2.1`). -3. In special cases where the document type cannot be uniquely resolved by the above, we need to introduce a `ProfileID` element (e.g. `ProfileID = urn:fdc:peppol.eu:2017:poacc:billing:01:1.0`) - -> **Note:** If the document type cannot be uniquely resolved the validation will fail. Thus, care must be taken when -> configuring the document types to make sure they are distinct. - -The default supported document types are configured in the `reference.conf` file. The configuration for each document -type is loaded as a `DocumentTypeConfig` in the `DocumentTypeModule` and the set of all `DocumentTypeConfig` is registered in Guice. -An example of the configuration for an Peppol BIS Credit Note is as follows -``` -document.type = { - peppolBISBillingCreditNote = { - friendlyName: "Peppol BIS Credit Note" - payloadRootLocalName: "CreditNote" - payloadRootNamespace: "urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2" - schemaPath: "META-INF/Schemas/UBL_v2.1/maindoc/UBL-CreditNote-2.1.xsd" - identifierDiscriminators = [ - { - xpathExpression: "/sbd:StandardBusinessDocument/root:CreditNote/cbc:CustomizationID" - xpathExpectedResult: "urn:cen\\.eu:en16931:2017#compliant#urn:fdc:peppol\\.eu:2017:poacc:billing:3\\.0" - }, - { - xpathExpression: "/sbd:StandardBusinessDocument/root:CreditNote/cbc:ProfileID" - xpathExpectedResult: "urn:fdc:peppol\\.eu:2017:poacc:billing:01:1\\.0" - - }] - namespaces = [ - { namespace: "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader", prefix: "sbd" }, - { namespace: "urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2", prefix: "cac" }, - { namespace: "urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2", prefix: "cbc" }, - { namespace: "urn:oasis:names:specification:ubl:schema:xsd:CoreComponentParameters-2", prefix: "ccts" }, - { namespace: "urn:oasis:names:specification:ubl:schema:xsd:SpecializedDatatypes-2", prefix: "sdt"}, - { namespace: "urn:un:unece:uncefact:data:specification:UnqualifiedDataTypesSchemaModule:2", prefix: "udt"}, - { namespace: "urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2", prefix: "root"} - ] - schematronDocuments = [ - { - schematronDocumentPath: "META-INF/Schematron/PEPPOL/CEN-EN16931-UBL.xslt" - errorXPath: "/svrl:schematron-output/svrl:failed-assert[@flag='fatal']" - errorMessageXPath: "svrl:text" - errorLocationXPath: "@location" - warningXPath: "/svrl:schematron-output/svrl:failed-assert[@flag='warning']" - warningMessageXPath: "svrl:text" - warningLocationXPath: "@location" - }, - { - schematronDocumentPath: "META-INF/Schematron/PEPPOL/PEPPOL-EN16931-UBL.xslt" - errorXPath: "/svrl:schematron-output/svrl:failed-assert[@flag='fatal']" - errorMessageXPath: "svrl:text" - errorLocationXPath: "@location" - warningXPath: "/svrl:schematron-output/svrl:failed-assert[@flag='warning']" - warningMessageXPath: "svrl:text" - warningLocationXPath: "@location" - }, - { - schematronDocumentPath: "META-INF/Schematron/PEPPOL/DK-EN16931-UBL.xslt" - errorXPath: "/svrl:schematron-output/svrl:failed-assert[@flag='fatal']" - errorMessageXPath: "svrl:text" - errorLocationXPath: "@location" - warningXPath: "/svrl:schematron-output/svrl:failed-assert[@flag='warning']" - warningMessageXPath: "svrl:text" - warningLocationXPath: "@location" - } - ] - } - - // other supported document types -} -``` -Once the document type has been determined the validation will perform schema (XSD) and schematron validation for the message. -Schematron validation will only be performed if the message passes schema validation. - -> **Note:** The schema and schematron validation will only be performed on the payload of the StandardBusinessDocument -> (e.g. an invoice or similar). - -As per the Peppol SBDH 2.0 specification, the SBDH must contain a business scope called -`COUNTRY_C1` with the ISO-3166-1 2-letter country code of the sender. The default `MessageValidator` validates -that the incoming message contains this scope and it contains a 2 letter country code. - -For Schematron validation it is possible to configure one or more schematron stylesheets. All configured stylesheets will be used during -validation. Additionally, for each schematron stylesheet the configuration will contain XPath expressions for -extracting errors and warnings from the resulting document. Note that it is optional to specify XPath expressions for -extracting warnings. - -#### 3.3.1 Adding additional document types -All that is needed to add validation support for additional document types is to add it to the configuration in your `oxalis.conf` like so (some details have been omitted for brevity) - -``` -document.type.myNewDocumentType = { - friendlyName: "My custom document type" - payloadRootLocalName: "CreditNote" - payloadRootNamespace: "urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2" - schemaPath: "path/to/schema.xsd" - identifierDiscriminators = [...] - namespaces = [ - { namespace: "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader", prefix: "sbd" }, - { namespace: "urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2", prefix: "root"} - ... - ] - schematronDocuments = [ - { - schematronDocumentPath: "path/to/schematron.xslt" - errorXPath: "..." - errorMessageXPath: "..." - errorLocationXPath: "..." - } - ] -} -``` -The above configuration will automatically loaded at runtime and the Nemhandel e-Delivery reference access point will recognize the given document type. -> *NOTE*: The Nemhandel e-Delivery reference access point can support practically any document type as long as you can specify sufficient identification discriminators. -> However, to be of practical use the document type must be supported by the Nemhandelsregister. - - -#### 3.3.2 Supported document types -By default, the Nemhandel eDelivery RI supports the following document types in terms of schema and Schematron validation - -| Document type | Configuration property | Payload namespace | Payload local name | Identifier discriminators | -|------------------------------------------------|---------------------------------------------------|-----------------------------------------------------------------------------------|------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| OIOUBL Reminder 2.1 | `document.type.reminder` | `urn:oasis:names:specification:ubl:schema:xsd:Reminder-2` | `Reminder` | `/sbd:StandardBusinessDocument/root:Reminder/cbc:CustomizationID` = `OIOUBL-2.(01|02|1)` | -| OIOUBL Utility Statement 2.1 | `document.type.utilityStatement` | `urn:oasis:names:specification:ubl:schema:xsd:UtilityStatement-2` | `UtilityStatement` | `/sbd:StandardBusinessDocument/root:UtilityStatement/cbc:CustomizationID` = `OIOUBL-2.1` | -| OIOUBL Invoice 2.1 | `document.type.invoice` | `urn:oasis:names:specification:ubl:schema:xsd:Invoice-2` | `Invoice` | `/sbd:StandardBusinessDocument/root:Invoice/cbc:CustomizationID` = `OIOUBL-2.(01|02|1)` | -| OIOUBL Credit Note 2.1 | `document.type.creditNote` | `urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2` | `CreditNote` | `/sbd:StandardBusinessDocument/root:CreditNote/cbc:CustomizationID` = `OIOUBL-2.(01|02|1)` | -| OIOUBL Application Response 2.1 | `document.type.applicationResponse` | `urn:oasis:names:specification:ubl:schema:xsd:ApplicationResponse-2` | `ApplicationResponse` | `/sbd:StandardBusinessDocument/root:ApplicationResponse/cbc:CustomizationID` = `OIOUBL-2.(01|02|1)` | -| OIOUBL Statement 2.1 | `document.type.statement` | `urn:oasis:names:specification:ubl:schema:xsd:Statement-2` | `Statement` | `/sbd:StandardBusinessDocument/root:Statement/cbc:CustomizationID` = `OIOUBL-2.(01|02|1)` | -| OIOUBL Order 2.1 | `document.type.order` | `urn:oasis:names:specification:ubl:schema:xsd:Order-2` | `Order` | `/sbd:StandardBusinessDocument/root:Order/cbc:CustomizationID` = `OIOUBL-2.(01|02|1)` | -| OIOUBL Order Response Simple 2.1 | `document.type.orderResponseSimple` | `urn:oasis:names:specification:ubl:schema:xsd:OrderResponseSimple-2` | `OrderResponseSimple` | `/sbd:StandardBusinessDocument/root:OrderResponseSimple/cbc:CustomizationID` = `OIOUBL-2.(01|02|1)` | -| OIOUBL Order Response 2.1 | `document.type.orderResponse` | `urn:oasis:names:specification:ubl:schema:xsd:OrderResponse-2` | `OrderResponse` | `/sbd:StandardBusinessDocument/root:OrderResponse/cbc:CustomizationID` = `OIOUBL-2.(01|02|1)` | -| OIOUBL Order Cancellation 2.1 | `document.type.orderCancellation` | `urn:oasis:names:specification:ubl:schema:xsd:OrderCancellation-2` | `OrderCancellation` | `/sbd:StandardBusinessDocument/root:OrderCancellation/cbc:CustomizationID` = `OIOUBL-2.(01|02|1)` | -| OIOUBL Order Change 2.1 | `document.type.orderChange` | `urn:oasis:names:specification:ubl:schema:xsd:OrderChange-2` | `OrderChange` | `/sbd:StandardBusinessDocument/root:OrderChange/cbc:CustomizationID` = `OIOUBL-2.(01|02|1)` | -| OIOUBL Catalogue 2.1 | `document.type.catalogue` | `urn:oasis:names:specification:ubl:schema:xsd:Catalogue-2` | `Catalogue` | `/sbd:StandardBusinessDocument/root:Catalogue/cbc:CustomizationID` = `OIOUBL-2.(01|02|1)` | -| OIOUBL Cataloge Deletion 2.1 | `document.type.catalogueDeletion` | `urn:oasis:names:specification:ubl:schema:xsd:CatalogueDeletion-2` | `CatalogueDeletion` | `/sbd:StandardBusinessDocument/root:CatalogueDeletion/cbc:CustomizationID` = `OIOUBL-2.(01|02|1)` | -| OIOUBL Catalogue Request 2.1 | `document.type.catalogueRequest` | `urn:oasis:names:specification:ubl:schema:xsd:CatalogueRequest-2` | `CatalogueRequest` | `/sbd:StandardBusinessDocument/root:CatalogueRequest/cbc:CustomizationID` = `OIOUBL-2.(01|02|1)` | -| OIOUBL Catalogue Item Specification Update 2.1 | `document.type.catalogueItemSpecificationUpdate` | `urn:oasis:names:specification:ubl:schema:xsd:CatalogueItemSpecificationUpdate-2` | `CatalogueItemSpecificationUpdate` | `/sbd:StandardBusinessDocument/root:CatalogueItemSpecificationUpdate/cbc:CustomizationID` = `OIOUBL-2.(01|02|1)` | -| OIOUBL Catalogue Pricing Update 2.1 | `document.type.cataloguePricingUpdate` | `urn:oasis:names:specification:ubl:schema:xsd:CataloguePricingUpdate-2` | `CataloguePricingUpdate` | `/sbd:StandardBusinessDocument/root:CataloguePricingUpdate/cbc:CustomizationID` = `OIOUBL-2.(01|02|1)` | -| DK Peppol Catalogue Pricing Update | `document.type.cataloguePricingUpdateDKPeppol` | `urn:oasis:names:specification:ubl:schema:xsd:CataloguePricingUpdate-2` | `CataloguePricingUpdate` | `/sbd:StandardBusinessDocument/root:CataloguePricingUpdate/cbc:CustomizationID` = `urn:fdc:peppol.eu:poacc:trns:cataloguepricingupdate:3` | -| DK Peppol Catalogue Update Response | `document.type.catalogueUpdateResponseDKPeppol` | `urn:oasis:names:specification:ubl:schema:xsd:ApplicationResponse-2` | `ApplicationResponse` | `/sbd:StandardBusinessDocument/root:ApplicationResponse/cbc:CustomizationID` = `urn:fdc:peppol.eu:poacc:trns:catalogue_update_response:3` | -| DK Peppol Order Agreement | `document.type.orderAgreementPeppol` | `urn:oasis:names:specification:ubl:schema:xsd:OrderResponse-2` | `OrderResponse` | `/sbd:StandardBusinessDocument/root:OrderResponse/cbc:CustomizationID` = `urn:fdc:peppol.eu:poacc:trns:order_agreement:3` | -| DK Peppol Order Agreement Response | `document.type.orderAgreementResponseDKPeppol` | `urn:oasis:names:specification:ubl:schema:xsd:ApplicationResponse-2` | `ApplicationResponse` | `/sbd:StandardBusinessDocument/root:ApplicationResponse/cbc:CustomizationID` = `urn:fdc:peppol.eu:poacc:trns:order_agreement_response:3` | -| OIOUBL Reminder 3.0 | `document.type.reminder30` | `urn:oasis:names:specification:ubl:schema:xsd:Invoice-2` | `Invoice` | `/sbd:StandardBusinessDocument/root:Invoice/cbc:CustomizationID` = `urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0#conformant#urn:fdc:oioubl.dk:2020:oioubl:reminder:3.0` | -| OIOUBL Simplified Invoice 3.0 | `document.type.simplifiedInvoice30` | `urn:oasis:names:specification:ubl:schema:xsd:Invoice-2` | `Invoice` | `/sbd:StandardBusinessDocument/root:Invoice/cbc:CustomizationID` = `urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0#conformant#urn:fdc:oioubl.dk:2020:oioubl:simplifiedinvoice:3.0` | -| OIOUBL Invoice Response Transaction 3.0 | `document.type.invoiceResponseTransaction30` | `urn:oasis:names:specification:ubl:schema:xsd:ApplicationResponse-2` | `ApplicationResponse` | `/sbd:StandardBusinessDocument/root:ApplicationResponse/cbc:CustomizationID` = `urn:fdc:peppol.eu:poacc:trns:invoice_response:3` | -| OIOUBL Message Level Response Transaction 3.0 | `document.type.messageLevelResponseTransaction30` | `urn:oasis:names:specification:ubl:schema:xsd:ApplicationResponse-2` | `ApplicationResponse` | `/sbd:StandardBusinessDocument/root:ApplicationResponse/cbc:CustomizationID` = `urn:fdc:peppol.eu:poacc:trns:mlr:3` | -| Peppol BIS Billing - Invoice | `document.type.peppolBISBillingInvoice` | `urn:oasis:names:specification:ubl:schema:xsd:Invoice-2` | `Invoice` | `/sbd:StandardBusinessDocument/root:Invoice/cbc:CustomizationID` = `urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0` in conjunction with `/sbd:StandardBusinessDocument/root:Invoice/cbc:ProfileID` = `urn:fdc:peppol.eu:2017:poacc:billing:01:1.0` | -| Peppol BIS Billing - Credit Note | `document.type.peppolBISBillingCreditNote` | `urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2` | `CreditNote` | `/sbd:StandardBusinessDocument/root:CreditNote/cbc:CustomizationID` = `urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0` in conjunction with `/sbd:StandardBusinessDocument/root:CreditNote/cbc:ProfileID` = `urn:fdc:peppol.eu:2017:poacc:billing:01:1.0` | -| Peppol Catalogue | `document.type.peppolCatalogue` | `urn:oasis:names:specification:ubl:schema:xsd:Catalogue-2` | `Catalogue` | `/sbd:StandardBusinessDocument/root:Catalogue/cbc:CustomizationID` = `urn:fdc:peppol.eu:poacc:trns:catalogue:3` | -| Peppol Catalogue Response | `document.type.peppolCatalogueResponse` | `urn:oasis:names:specification:ubl:schema:xsd:ApplicationResponse-2` | `ApplicationResponse` | `/sbd:StandardBusinessDocument/root:ApplicationResponse/cbc:CustomizationID` = `urn:fdc:peppol.eu:poacc:trns:catalogue_response:3` | -| Peppol Order | `document.type.peppolOrder` | `urn:oasis:names:specification:ubl:schema:xsd:Order-2` | `Order` | `/sbd:StandardBusinessDocument/root:Order/cbc:CustomizationID` = `urn:fdc:peppol.eu:poacc:trns:order:3` | -| Peppol Order Response | `document.type.peppolOrderAgreement` | `urn:oasis:names:specification:ubl:schema:xsd:OrderResponse-2` | `OrderResponse` | `/sbd:StandardBusinessDocument/root:OrderResponse/cbc:CustomizationID` = `urn:fdc:peppol.eu:poacc:trns:order_agreement:3` | -| Peppol Despatch Advice | `document.type.peppolDespatchAdvice` | `urn:oasis:names:specification:ubl:schema:xsd:DespatchAdvice-2` | `DespatchAdvice` | `/sbd:StandardBusinessDocument/root:DespatchAdvice/cbc:CustomizationID` = `urn:fdc:peppol.eu:poacc:trns:despatch_advice:3` | -| Peppol Punch Out | `document.type.peppolPunchOut` | `urn:oasis:names:specification:ubl:schema:xsd:Catalogue-2` | `Catalogue` | `/sbd:StandardBusinessDocument/root:Catalogue/cbc:CustomizationID` = `urn:fdc:peppol.eu:poacc:trns:punch_out:3` | -| Peppol Order Change | `document.type.peppolOrderChange` | `urn:oasis:names:specification:ubl:schema:xsd:OrderChange-2` | `OrderChange` | `/sbd:StandardBusinessDocument/root:OrderChange/cbc:CustomizationID` = `urn:fdc:peppol.eu:poacc:trns:order_change:3` | -| Peppol Order Cancellation | `document.type.peppolOrderCancellation` | `urn:oasis:names:specification:ubl:schema:xsd:OrderCancellation-2` | `OrderCancellation` | `/sbd:StandardBusinessDocument/root:OrderCancellation/cbc:CustomizationID` = `urn:fdc:peppol.eu:poacc:trns:order_cancellation:3` | - -For additional information regarding the XSD and schematron files used for these document types see the configuration in `reference.conf`. - -#### 3.3.3 Overriding the built-in document types -The built-in, supported document types from the previous section can changed by specifying the relevant properties in your `oxalis.conf` file. This can be useful if you want to update e.g. the Schematron stylesheet for a given document type. -As as example, assume a new schematron stylesheet has been released for the OIOUBL Invoice 2.1 document type and you need to use this instead of the built-in one. As specified in the table of the built-in document types in the previous section, -the OIOUBL Invoice 2.1 document type is defined by the `document.type.invoice` configuration property. Changing the schematron file for this document type is as simple as adding the following to your `oxalis.conf` file: -``` -document.type.invoice.schematronDocuments = [ - { - schematronDocumentPath: "path/to/invoice/schematron.xslt" - errorXPath: "/Schematron/Error" - errorMessageXPath: "Description" - errorLocationXPath: "Xpath" - } - ] -} -``` -> **NOTE**: for this example, it is assumed that the new schematron produces an equivalent result document as the old schematron (i.e. error messages are located in the same place). -> In case the resulting document is different, you have to change the `errorXPath`, `errorMessageXPath`, `errorLocationXPath` accordingly. - -All configuration properties for the built-in document types can be changed in a similar fashion. Please refer to the `reference.conf` file to see which configuration properties are available for the document types. - - -### 3.4 Signing and processing the Nemhandel e-Delivery signature (ASiC-E) -The RI supports processing documents containing Nemhandel e-Delivery signatures when started in Nemhandel mode. -It is currently not mandatory for C1 to include a Nemhandel e-Delivery signature in documents handed over to the RI using the Outbox REST API, but if present, the RI will validate the signature(s). - -The RI processes Nemhandel e-Delivery signatures only when started in Nemhandel e-Delivery mode (`NEMHANDEL_TEST` or `NEMHANDEL_PRODUCTION`). -The Nemhandel e-Delivery signature is contained in the header part of the SBD message in the shape of an ASiC-E container. -An ASiC-E container is basically a ZIP file containing the documents to be signed as well as the signatures and metadata. An ASiC-E container can be signed in various "formats" but Nemhandel e-Delivery uses the XAdES format. - -The ASiC-E container containing the signature must be placed in the Standard Business Document Header under the `` element with the type `NEMHANDEL_EDELIVERY_SIGNATURE`. The content of the ASiC-E container should be Base64 encoded and placed in the `` element like the following snippet: -```xml - - - ... - ... - ... - ... - - - DOCUMENTID - ... - - - PROCESSID - ... - - - COUNTRY_C1 - DK - - - NEMHANDEL_EDELIVERY_SIGNATURE - - - - - ... - -``` -> Note: there must only be one `NEMHANDEL_EDELIVERY_SIGNATURE` scope in a given SBD. - -The contents of the ASiC-E container is expected to be the following: -``` -asic-container.asice: - | - +-- mimetype - | - +-- standard-business-document.xml (2) - | - +-- original-standard-business-document.xml (3) - | - +-- transfer-delegation.xml (4) - | - +-- META-INF/ - | - +-- manifest.xml - | - +-- signatures001.xml (1) -``` -The following files are of specific interest: -1. The actual signature of the ASiC-E container is contained in this file. An ASiC-E container may contain multiple signatures though we only require one, which must include the following 3 files (1 optional). -2. The Standard Business Document (SBD) to be signed (thus this is excluding the ASiC-E container). This is the file that Corner X will send to Corner X+1. -3. The full "original" Standard Business Document including the ASiC-E container which has been received from Corner X-1. Note that for Corner 1 this file is optional in case this corner is responsible for generating the original SBD. For C2 this file should only be included if C1's SBD is signed! -4. A transfer delegation documents the intention of Corner X to transfer the SBD to Corner X+1. - -> Note: C1 is currently allowed to send unsigned SBDs through the C2 `/outbox` REST API. If C1 sends an unsigned SBD it will **_not_** be included in the ASiC-E container as `original-standard-business-document.xml`, because its contents cannot be trusted by C3 and C4 anyway. -> If C1 sends a signed SBD, C2 will include it as `original-standard-business-document.xml`. For C3, a document signature is required. Thus, C2 must always sign the document before sending it to C3 regardless C1s signature. - -Each Corner X will effectively have to resign the standard business document since it must at least produce a new transfer delegation between itself and Corner X+1. -Further, if Corner X wants to transform/convert the SBD in any way it must also resign the resulting document. -This includes producing and including a new ASiC-E container which must include the raw original SBD (including the ASiC-E container from Corner X-1). - -This is illustrated in the following figure: -![Nemhandel e-Delivery signature - resign example](nemhandel-edelivery/nemhandel-signature-resign-example.png) - -The Nemhandel e-Delivery signature is recursively validated. As an example consider validation (by C3) of the C2 SBD (righthand side of the above figure. In this case we assume that C1 has also signed the SBD). -First C2's ASiC-E container will be validated. Since C2's ASiC-E container contains `original-standard-business-document.xml` (C1's SBD) then the ASiC-E container of this document will be validated as well and so on until all nested ASiC-E containers have been validated. - -The following is a rough sketch of the algorithm used to validate this container - -1. Verify integrity of ASiC-E container - 1. Verify the signature - 2. Verify the signature certificate is a valid MitID certificate -2. Verify ASiC-E container contains the required files: - 1. standard-business-document.xml - 2. transfer-delegation.xml -3. Verify standard-business-document.xml matches SBD (minus the ASiC-E container). Example: this will verify the C2 SBD on the righthand side of the above figure (red text) minus the `NEMHANDEL_EDELIVERY_SIGNATURE` Scope element, matches the content of standard-business-document.xml -4. Verify transfer-delegation.xml -5. If original-standard-business-document.xml is present, perform step 1-5 on that document as well. - -The [Digital Signature Service (DSS)](https://github.com/esig/dss) framework is used both in the creation and validation of the ASiC-E containers (specifically the dss-asic-xades submodule of the framework). - -The signature validation code is found in the `dk.erst.oxalis.as4.validation.signature` and `dk.erst.oxalis.as4.signature` packages and are registered Guice through [DSSModule](../src/main/java/dk/erst/oxalis/as4/validation/signature/DSSModule.java). - -#### 3.4.1 Signature certificates -The certificates used to in the creation of a Nemhandel e-Delivery signature must be valid MitID certificates (FOCES3), and must be issued by the appropriate Certificate Authority and must contain a CVR-number. -For MitID certificates the CVR-number can be found in the Organization Identifier (OID.2.5.4.97) attribute of the subject. -See the truststores with the relevant CA certificates in [src/main/resources/truststore](../src/main/resources/truststore). - -In terms of the Nemhandel e-Delivery reference AP, the signature creation will use the private key of the AP's configured certificate. -The two added modes NEMHANDEL_TEST and NEMHANDEL_PRODUCTION are only applicable when the AP is configured to use a MitID certificate (see [Support for MitID system certificates using mode detection](#363-support-for-mitid-system-certificates-using-mode-detection) for further details). - - -#### 3.4.2 Transfer delegation -A transfer delegation describes Corner X's intention of transferring a standard business document to the next Corner X+1. -Thus, it only covers the transfer between two consecutive Corners and then a new transfer delegation will cover the transfer between the next two Corners. - -It is an XML document containing information about a sender and a receiver. -The following is an example of a transfer delegation from CVR 12345678 to CVR 98765432. -```xml - - - 12345678 - 98765432 - -``` - -Currently, both the sender and the receiver must be identified by a CVR number, thus only `SchemeID = DK:CVR` is supported. - -When validating a transfer delegation at a given recursion level, the sender CVR must match the CVR number of the MitID certificate used to sign the ASiC-E container at that level. -This serves to prove that transfer delegation is, in fact, created by the sender (and thus can be trusted) because only the actual sender (as per CVR) will be able to sign the ASiC-E container (and thus also the transfer delegation) with a valid MitID certificate containing that CVR. - -Further, for the top-level transfer delegation (recursion level 0) the receiver of the transfer delegation must match the CVR number of the MitID certificate which is configured for the access point. -This check is intended to verify that the access point is the intended receiver of the standard business document. -For non-top-level transfer delegations (recursion level > 0) the receiver of the transfer delegation should match the CVR number of the signing certificate for the ASiC-E container and the sender CVR number of the previous recursion level (because the validation is going in top-down order). -This ensures that transfer delegation "chain" is unbroken and no unexpected CVR number appears in the chain. - -The creation of transfer delegations is handled by [TransferDelegationFactoryImpl](../src/main/java/dk/erst/oxalis/as4/signature/TransferDelegationFactoryImpl.java) and validation is handled as part of the signature validation in [SignatureValidatorImpl](../src/main/java/dk/erst/oxalis/as4/validation/signature/SignatureValidatorImpl.java). - -### 3.5 Asynchronous processing -In order to facilitate Message Level Responses regarding validation errors in C3, Oxalis has been extended with support for asynchronous processing of messages received. -The validation of messages has been split into a "synchronous" and "asynchronous" part. - -| Validation type | Validations | -|-----------------|--------------------------------------------| -| Synchronous | Schema (XSD) | -| Asynchronous | Schematron, Nemhandel e-Delivery signature | - -> **Note:** The split of the validation only affects the processing when receiving messages (C3). When sending messages (C2) the Oxalis reference AP will perform all validations synchronously. - -#### 3.5.1 Simple asynchronous processing (thread-based) and Message Level Responses -A simple asynchronous processing mechanism is based on the java ExecutorService interface has been impleemented in the RI. -During startup the RI will create a thread-pool-based ExecutorService of a configurable size. -This ExecutorService maintains a queue of asynchronous processing tasks and will execute these as threads available in its thread-pool. - -> **NOTE:** the simple asynchronous processing mechanism is *neither* persistent nor durable. -> Further, it contains only very limited error handling and there is no failure recovery or retry functionality. -> If errors do occur, we will do our best to mark messages with an ERROR status but even this may fail in extreme cases. - - -The simple asynchronous processing is configurable using the following properties. - -| Property | Type | Purpose | -|---------------------------------------------|--------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| async.executor.pool.size.core | Number | The core number of threads in the thread-pool | -| async.executor.pool.keep.alive.time.ms | Number | The time (in ms) to keep idle threads alive waiting for new tasks before they are terminated (when the thread-pool has more than the core number of threads) | -| async.executor.graceful.shutdown.timeout.ms | Number | The time (in ms) to wait for termination of in-progress tasks, after a shutdown has been requested, before forcefully shutting down the ExecutorService (including all threads in the pool). | -| async.executor.runnable.factory | String | The named Guice binding for the implementation of the [AsyncProcessingRunnableFactory](../src/main/java/dk/erst/oxalis/as4/async/AsyncProcessingRunnableFactory.java) interface to use for instantiating tasks (e.g. instances of the `Runnable` interface). Use this to override the default asynchronous processing. | - -The default values for these can be seen in [ExecutorServiceConf](../src/main/java/dk/erst/oxalis/as4/async/ExecutorServiceConf.java). - -When a new message is received via the AS4-protocol, the AP will perform the "synchronous" part of the validation and save the message in the database. -If the message is ok according to this validation, the message will be marked with status PROCESSING and finally be queued for asynchronous processing before a synchronous AS4-response is returned to C2. - -The asynchronous processing will start once the task reaches the front of the queue and a thread becomes available. The default asynchronous processing will include the following steps: - -1. Perform the "asynchronous" part of the message validation - currently Schematron validation and signature validation -2. Update message status based on validation result -3. If no validation errors (only in Nemhandel Mode) - 1. Create transport delegation for C3 -> C4 - 2. Resign document (i.e. create Nemhandel e-Delivery signature) - 3. Save the resigned document -4. Generate and send an appropriate Message Level Response back to C2 if necessary - -For implementation details please refer to [AsyncProcessingRunnable](../src/main/java/dk/erst/oxalis/as4/async/AsyncProcessingRunnable.java). - -#### 3.5.2 Overriding the default asynchronous processing (thread-based) -The asynchronous processing mechanism can be overridden using the `async.executor.runnable.factory` configuration property mentioned above. - -First, create your custom implementation of the `Runnable` interface. -```java -public class CustomRunnable implements Runnable { - // your custom async processing implementation -} -``` - -Next, create a custom implementation of the `AsyncProcessingRunnableFactory` interface which instantiates your `CustomRunnable` for a given message. - -```java -public class CustomAsyncProcessingFactory implements AsyncProcessingRunnableFactory { - - private final Injector injector; - - public CustomAsyncProcessingFactory(Injector injector) { - this.injector = injector; - } - - @Override - public Runnable createRunnable(String messageUuid) { - return new CustomRunnable(injector, messageUuid); - } -} -``` -You are free to inject any registered Guice bindings into your `AsyncProcessingRunnableFactory` and provide these to your `CustomRunnable` - the above example just provides the full Guice `Injector` to the `CustomRunnable`. - -> NOTE: When using JPA in your `CustomRunnable` take care to handle the EntityManager properly. -> Prefer to use a thread-local EntityManager and perform the appropriate cleanup when your `CustomRunnable` terminates. - -Next, create a new Guice module which registers a named binding for the `CustomAsyncProcessingFactory`. The example below uses a -`@Provides` method, but the binding DSL can also be used if preferred. - -```java -public class CustomAsyncProcessingModule extends AbstractModule { - - @Provides - @Singleton - @Named("custom-async-factory") - AsyncProcessingRunnableFactory customAsyncProcessingFactory(Injector injector) { - return new CustomAsyncProcessingFactory(injector); - } -} -``` -> NOTE: the example uses the name `custom-async-factory` for the binding, but you can use whichever name you like. - -In order to get Oxalis to load this module, include a `reference.conf` file in the `src/main/resources` folder with the -following content -```properties -# the can be whatever your prefer your module to be called -oxalis.module..class = com.example.CustomAsyncProcessingModule -``` -Package the code as a jar file and place the jar on the classpath for Oxalis to load. - -Finally, configure the simple asynchronous processing mechanism in your `${OXALIS_HOME}\oxalis.conf` file using the -name of the binding from the module. -```properties -async.executor.runnable.factory = custom-async-factory -``` -The `CustomAsyncProcessingFactory` should now be used at runtime to create asynchronous processing tasks. -Thus, messages will be asynchronously processed using the `CustomRunnable` implementation instead of the default [AsyncProcessingRunnable](../src/main/java/dk/erst/oxalis/as4/async/AsyncProcessingRunnable.java) implementation. - -#### 3.5.3 Message Level Response -In the event of validation errors during the asynchronous processing, the AP will generate a Message Level Response which will be sent back to the sender of the document. -A Message Level Response will list all validation errors as well as reference the id of the original document. - -> NOTE: currently, no message level response is sent for documents which contain no validation errors. - -An example of an (unsigned) message level response is shown here: -```xml - - - - 1.0 - - 0184:22334455 - - - 0184:12345678 - - - urn:oasis:names:specification:ubl:schema:xsd:ApplicationResponse-2 - 2.1 - 76ce2330-39b2-412d-a76c-02dbc6164066 - ApplicationResponse - 2023-03-09T08:16:37.216+01:00 - - - - DOCUMENTID - urn:oasis:names:specification:ubl:schema:xsd:ApplicationResponse-2::ApplicationResponse##urn:fdc:peppol.eu:poacc:trns:mlr:3::2.1 - - - PROCESSID - urn:fdc:peppol.eu:poacc:bis:mlr:3 - - - COUNTRY_C1 - DK - - - - - urn:fdc:peppol.eu:poacc:trns:mlr:3 - urn:fdc:peppol.eu:poacc:bis:mlr:3 - 76ce2330-39b2-412d-a76c-02dbc6164066 - 2023-03-09 - 08:16:37+01:00 - - 22334455 - - - 12345678 - - - - RE - Document rejected due to validation errors - - - a2806f03-57d6-4aa1-b1fe-907f3a024bb1 - 1 - 2 - - - - /sbd:StandardBusinessDocument/sbd:StandardBusinessDocumentHeader/sbd:BusinessScope/sbd:Scope[sbd:Type='NEMHANDEL_EDELIVERY_SIGNATURE']/sbd:InstanceIdentifier - - - RE - E-REF21001: No Nemhandel e-Delivery documentsignature found for level 0 - - BV - - - - - - -``` - -The `DocumentReference/ID` element contains the id of the original document to which the message level response corresponds and the `LineResponse` elements lists the individual validation errors. - -Since a message level response is a new document, it is subject to the same requirements as all other documents in Nemhandel e-Delivery. That is, it must pass schema and schematron validation and be signed according to the description in [Nemhandel e-Delivery Signature (ASiC-E)](#27-nemhandel-e-delivery-signature-asic-e). - -When generating the message level response, a country code of the sender of the message level response must be included -in the response. This is sourced from the `Account` row that the incoming message is related to. - -#### 3.5.4 OIOUBL Application Response -The Message Level Response mentioned in the previous section is converted to an OIOUBL Application Response if the "original" -document is a OIOUBL document (e.g. an OIOUBL Invoice). For details regarding which response type is used for a specific document type, -please refer to the `messageLevelResponseType` configuration property for each document type. - -The conversion is performed using the [MLR_2_OIOUBL_ApplicationResponse](../src/main/resources/META-INF/Conversion/MLR_2_OIOUBL_ApplicationResponse.xslt) XSLT. - - -## 3.6 Misc. information -Apart from the key component described in the previous section, a number of minor new features have been added. - -### 3.6.1 NemHandel eDelivery transport profile support -Oxalis has been extended with support for the transport profile `nemhandel-transport-as4-v2_0`. -The transport profile is defined in [reference.conf](../src/main/resources/reference.conf). -This transport profile is similar to Peppols `peppol-transport-as4-v2_0` transport profile in that it relies -on the same AS4 protocol for the transmission. - -> **Note:** By default the `nemhandel-transport-as4-v2_0` transport profile is only enabled when -the Nemhandel e-Delivery Reference Access Point is started in Nemhandel mode. In this case, it will -be prioritized over Peppols `peppol-transport-as4-v2_0` transport profile. - - -### 3.6.2 Configuration -Configuration of the additional features, e.g. database connection, is done through the oxalis.conf file -that you set up as part of an Oxalis installation. - - -A requirement for this to work is that you install the relevant JDBC driver (in this case MySQL) in Tomcat's lib -folder. It is possible to distribute the JDBC driver together with the Oxalis-AS4 plugin but this requires code changes, -as you have to register the JDBC-driver manually (like we do with the H2 JDBC driver in `JdbcModule`). This is due to -the way Oxalis-AS4 is loaded by Tomcat in relation to the Oxalis war file. - - -### 3.6.3 Support for MitID system certificates using mode detection -The RI comes with a mode concept, which defines a number of properties for the trust store, certificate validation, etc. -Certificate validation is a recipe for which validations must take place on or certificates that are used in AP management (i.e. AS4 transport) and in SMP management (i.e. in connection with SMP). -These perform issuer check, certificate expiration, certificate chains (root and intermediate CA), CRL, OCSP, etc. - -There are two modes TEST mode and a PRODUCTION mode. Both of these built-in modes are for Peppol certificates and the primary difference is that TEST mode supports Peppol test certificates and PRODUCTION mode supports Peppol production certificates.  -At startup, Oxalis will automatically detect the active mode based on the certificate configured by the AP. - -Since the reference AP is configured in either "Peppol mode" or "Nemhandel mode", we have introduced two new TEST and PRODUCTION modes to support Nemhandel e-Delivery: NEMHANDEL_TEST and NEMHANDEL_PRODUCTION. - -NEMHANDEL_TEST supports MitID test certificates (which are issued by MitID's test CA) and NEMHANDEL_PRODUCTION will support certificates issued by MitID's production CA. Like the built-in Peppol modes, Nemhandel modes will also perform CRL checks. - - -### 3.6.4 Receiver exceptions -If an error occurs on the receiver side during reception of the SOAP message, exceptions will be thrown. Oxalis -automatically wraps these in an ebMS 3.0-compliant SoapFault. This is also utilized for exceptions thrown by NemHandel -eDelivery code, specifically the synchronous part. That is to say, if an exception occurs while processing the message -synchronously (e.g. XSD schema validation error, database connection issue or similar) we throw a proper -`IOException` from `NemHandelPersister` since these are automatically converted to an ebMS 3.0-compliant SoapFault -with error code `EBMS:0004`. - -## 3.6.5 Automatic update notifications (version validation) -This RI comes with functionality to validate the running application version and the schematron version. -If a version is outdated or is soon to be outdated a notification will be shown in the application logs telling it is time to update. - -> **Note: If a mandatory version is outdated it will NOT be possible to send any messages until the version is updated.** - -Note that the information about new versions is delivered by the Nemhandel SMP service. If another SMP service is being used, -there will most likely not be any version information available. diff --git a/docs/nemhandel-edelivery/nemhandel-signature-resign-example.png b/docs/nemhandel-edelivery/nemhandel-signature-resign-example.png deleted file mode 100644 index 78306ddb22e614ac6826de483f586d275e6830e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 207018 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]u&v5(uFKr_1Q|+l+aD1gU&N--)a+@hg7IkNh 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 z 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≷^=8tcg7L9uv^Mcv?@Tnp`#!~$Q~H0C7JubftrJ@BGp?{Ka>KMGwBqR zAvZHWym))qHZ@v~V{WFfV0Fn5>o8jGZCUErivn3cHvX9f^P)XH=GQ(xU27Z3NjzrZ z8-QSWYU^1{6qrY>e#GTac>?ZuOtWHju#SronaS@S5n(P%60k3^)V6jp3K!Eok>q$2 zp%;B6)V0Z=#M0!2C|@N~EZ1x?z0&(DemvP%Gtl<3TJB-l+TGe)?pg0bu3|#ov_>do zb>=})L)FBGBgcBlS<@a&xMa37*Yc=Q<=h!l&yr0zU=LH16@;uhaZf`CxteJRvo>bEFGB4(&@>$U)(2JV=IvF-3!9CJ#^(OB|v zjk!Eb(zUIQzZG>CHOv6jopeQYCzo&As}28eJJ#b==ovn;QLXR(Y5`Kp?Y8K@x-(4u zXdjr;lR~SZ_dA-jP4%gM9P`8$>lZ{;`RAd0y!R&Tu67xMO7&kfTep@H*5X@jjXak& zD@-b90?*siEChJcO7^ZSa$m<|pQ{*%;xE>JBpV|*w$oZF`nHB5*Rodhb{IqrmP3OR zO`>Ub1Q%Y}C60?qZlgGcMb4?Y4cvgKpmnrJ!o6}FDlLadar}SLYf)W6rgJO=d>arH zmzHEnv0S0FA-KlANPZG=c63Yj_Twy)N-xIV9P&E*1u2AVbKcUFMmj(K&jH?cu3e-a zZK)=6UfM7sg#rz+2n&B_C(7)@nKZi#p zQ29P0aarJwM3$-F!~B{DT}W@WweAgKkTO$HuA_CLGYb62KX+Ad5E_KI_ zyW$S+Y-l*~F)8V_VR{+q_NzN+1RY%gBmzawbIowRqu1WgpjyflZIzI+YNQAk((in) zr^3BZSDQxW4ur9j7=H^fGUh7{P!<>%ttJ@ECisMeIIki{{^#K>Dh7e~WP&j>95JE? zghw#bn;9E-{*y+dS^f4w3{z%avaC-(I<)jdL&$;(q&k(?cdLs#=t4wbuz@mjAG4azMft-MK~il> zuK%Rc1Z@yR`0F2GtiW9$=V-<7gFR(yG(Y^F6SJQj$I?>g+-}orha^N!xkmmle`?9? zbr~zql|=P!_HbV482O`sik)1b^iR=5wO=v)C|6hAh~~}yPf_T<=w=kW_Ov-UWlSY3 z-tKkNuGgTK5jAV$$RqR^cQ9hQpm{y!QP?n((GexDe)E%9;p*f|Eb&`_o>B@7>^#6- zeVRhney`yAr{ov66V;YCFXVk}(ci2;uDQI=tU^pmQ^jN>h$R}DP*lwM@L?p*DGRe+ zR9?jRE=NIPLI#;HA_892@TvNvE{U>g%xZotoYL~dtktj4^h{>Q1^Gp0U3R_Y*rP!^ ztE$JRY#lysdTjcL1Vw#83t2gEeayuV`*M<>7BCmfS`^|;TO(y_RNfzjNb$m9hVt-l z_cRMMAI}-e*l+ZA3+rPN5#t}b9z(N;#G7}c+Cr|$Fp)XEJ+-MjUCV6J%U6RtU+>qu zO>ilIFw%q)bYd-z8gb={->v4G(|280n{I|>jPDm1f@+SePX?*EUtkYQIz0APQF!lj z`leIw{lQGXny|Onl=ndj#Sc<{IP0s6BqpX)&EV9p*U*@mFcE>ocG8%Shcx5;vJReW zWE`HndJu$@zVFoS>?W^rAV2X~4iy zl8WNzH~0H>Pxkxy{Z2R|!i{jZ2aVCkIB!T1e~r$9LRp~CBo8JXss?>;o(*M7r*)S+ zO=jnXDH~MfoUd>=ZH(kT$W6*qp(HB?wJ4hojg0#)=X2AVED>U|68cJR0$B2Ae>OJz zcZG#nBE6Pu?IE@7vE->KeL%~Yn>6C7+n9oc5k%hjR^f~k%z6$fz9LT}B`~9@lo_fjX@Bip!oDa9 zM+43u&K9SSd?_KCFr9i z)v;G`& zI(h}Vo>kbvjWD=og#Sl9^;2k2C-Od!Noi^zPF0vB1#rRxh?uKpq#z+>&HFbPZwy=u zb0Ou^LCQ?d7ud=YYma)b$XMKm>hzQk7`PIQLq72GkXk3Kz7gmUn7c6`KVYGRQw@|L zKTNJes+mbD6GUuo&H)90Z)+;m)vZaQ_V?7IPs0lO9<`1oh$PT#5%}P~wI~K-?Im@d z33Uw#|mJDE$4t`Joneik{G>nEyRFZT_>o_k>Jxi)C$g@OWc4XSw7gQ4XYVDBP*56 z+WDPTKADkXLIYqU`uBgI7h-hB@W3JXmx_3I269+3gINs^G1{#;UPZ~pXJPo%`W{%6 zqy!kniBu3jW^I8ar3m6AT>UG`Uvn}(83J6U)!XwA0JA3AKKM@A2;?XqsL!_ zRKL{D{@U@%Vt{>SQeTtz4jG%U%W#HhKW*cl(7SgxD&1n%ZFUJZE7&#Id&y&#H2E3| zj>&@`=RySymS+V(Kh4>t`%$orV5$kd4wD9YIm-a`94Rr*!P=m^7c~7?hcbyB3dIRf z<80u&-*{5NgzXjiEJ%g`hQR>ex<`}L9kWLa_reIhIpbE6tPP%b{`b|X{uKgeXfSvS+u~xW}h^`8#!RyN}Ny<>VoJ6w>p__Bn#3|la6Dg zf$nP!VsJTLetUBVXyn*BQb`_t%$J+f{m6XIGMtjw3bSUw&-dv&7&>T^j%!hh%R`E6 z@wb+(72jESZLBQHiLO7_FD{2Vi1i}>^MiP0^(PRMd8pI5BE-e&tbL&?WKhEHWm>2D zw$iF0DgRnXl;De{?6z2MjVJ=~nUQe!Jh@Yml8-_IR_E;GV-)vH7GLP(Y)3N2eDnH7 z{Gue_^5PDt?8{xb8I;AO3uIuEmq|Un!NrpOMeeHn7rc=mK!4B!Qw^FEZNk8tXgjk z5c2ixFuR4`RD^^w*qLLT2CGwr1zbB(9{Ilcx$IdFb%4*>Gg}$O9#g&DLrb$=h2lF& zNg{_7o=&g_NPk8k2hk}QOSSa`8oflh{6QkmHoZk$yO2O z`x1(kiWw{&G93w`i_+Y02Bu}00-tf`?r?&}OPaDI%WZR!lIMN&rh00+R41>-b(P++ zGOf0l5HE4A!lQKZsby&$Tj1}n`Qf%UliUe_5ht9pDQbFz1`-paD=FYq)1ynR7xu^v z!F8cp-oBqI6)+2$sc>q;-V>lHW&iwouXVrkIZ0@P$D25W!c|P1SnM|JM6AgdESOX1 zXxDdG?4wepzeG+FQ~6Ks-r97)(YUBwd^rw&_*cA+Qc_rNKGcXVfW>DUjZ=gDwdtTN zl5K#qFscfXb{ktLB=I)w{HpL2p@5FbGyG}bnXunB?>=@hPEf6a!uuwTjJ#ECZ%eVx zP&2Xfk5aVgNv#ei#WlYl9VaM{ctnz4c8tY-%<5&TM_6FWvD7rN7Ka!*;uA{@9rL$# z7w-ybT~%gcGBl-`MD7S(Z^WxxBJ8d~5 zj2`(4r&YzJ*lh?bnH$#aS=>L5W5y{+q znlb$6*&#)pEN}o4(7<T5o39#%lpu@Lw*YLs9 z^wP0=KHz_wX*r(TJeqOW`CO=(c1T}b_&2I$k)V>?^M55d!gy3B`tLz6sOa49=l9Rs zDCEh3^vaty4CIxIG$yS7mJ0wTGTixks>vZh0*y;KCuK$Ifq1^mDzS zRd+o~W>*Sss1!6RDBA}HfO<19pYCl4@J$?pCs8g={T<;(uKkLqxBnGS!+TIESp849 z1@Uo0h*5UU^FhGHn-iM!#xf zR5ixB^hxN+mVqa5Ucgzf(Zi2Q4OPaf*&Dwa|Nh}AGIa11J9k0V-JV_es|-{K??G<^ z=;sge5Mlx#h^`rwBv`%u8NajM;H|qGnkTEVML#ry6*vR3UAn>o<+>d8-D5U}2gj_n=yqy&wPlx)di0HO)^*tSf>y-@DU~ zPBOAsp-?WG@^{tnf90ZNMPGf`dLf|0aX?2D7;Tx!1gZ&{awo zP!%*CqksMIQm1$jNc6?ULFMBKtOHHJ{4<^%aB;dls?O+v>T%GDK1>;w4FS5SN1&c8 ztZn`I&3U$uBPWZ%yoT~`dLu)91#Eo)9JfAJ4iSS-FeIk(f#lDB*1d|#e(z8`3VG4* zWcp7WDsY-9?fGU{7$e9Ph)NsgT6}u8v4U;hr(8!afSFD7evoQFc=#xV(uvt3;V&*} zB^_e$gEZiKT~xAyRa=$)jlgR$xZX?+Zeo#MjuG~q1^=vj)~%m+8~BAhULw6;8G|;) zY#6Q6hLp$G6PrNRk7*>hXkCd;=a|{ku9VQ&7hR5BFHPUyR247nq1v6e>~thSE&Vzlb0^ zuq9>e$Ch8GUS)aSVHITli1RO8pe9b(=SyM_IVpI^B`ZkbP^k77q9nHhLHckqJ^8`i z$ms)=Q{d$25a`#l-G?L#1?;`vXFZv+M_NAIZi8>N zj`Ui>H47+wdY5bP!9cp;Hi$0#@Nc@dJ1xgYi2kyGpO17lbeSx63*cb2HFGDJ>`@km zod+TJw1F$bZRb09EupLHPCJDPPKgqt590%{!tI4k~83F>Ry|)fexxt z-Cwy_54dj-gGGl1FImjz$YBSq>Z87|0$Eu+B|fUcWI0N!$m@)`nzlsXpGujl97hUOaT*y8l?8vQsgcHCUw{6;#~glaeo()o z?b~Px?pug-j!bf@dRx|JQ~oK>($4|ec~9=~1S*`XNZQ9tczvt+%#|elNrR>rf>&n4 z9hh+1#qYm(0lY?yymd2Umr^&z3<5wX(isd@9s^@P`q+}#w?5D)jm>y1$FjxKC5lX1 z!%BZ2g6AH;lGR(+p%4(Ce;oqdFS`-Z5_|rUY`*{BIqchOY$Bo zV6&IIJa-p=_>uE?F`O~gt?#4KI~jYGG3#WqKd~;TRvlHy`Vg|dfhR%Q!NLEtV_ryi~@rLdta+&S~tgYl*S)hmXMEMINXBJH~;@$w;TpCU` zs`Uhv+)86vpqJ_dTwhKAnjA*6K~dRoT^1fEyp{upjI=LIGtKX^}GxBs5| zC5t^%@R0naZym70*kauKvo#fPRw7WuV;hQfl&&ce##CCJC4bZ;dgn{nzp{R~^p89D z?ysgA+B~~^KT#v&Zs@)2-*vizv=XQlr;|k>0-sHN96dWp)T>$hVDfho@xN5$7yf-| zt^gH+Ks##SU?4JcBq3vxW4aNM?y?Ys{lmppleZ>?F-y}ofu$piz#Tk(zVn9$ciSAD zupeG;UE1Pt@L;fvv+btg@Y~C73n(wI*o=!Zpcy#iZ?zcz9)t@1ZZ<2*f%=6^fE zy};YWg%q#|&Xn_O^b|F-iWR+a?Hu4c&z^aWBMIj@nK~dGZbDJRj=+neF@5x-Z7Gfr{H1|lN&~Tj%DEt-o37@F-Lej<$=?{!TFOpS0_iV zP>gd2)m%mSs(nC;{W(|+klsh|vo>;Tf(^FyY&HiOlI}LerEG=PNIIp9-EjGnir(Rl zsf{|bR{zp%t=qON`CtBY0BD&}$nFG1HczW=06j{}r`g9JwBS#S>--|Eduv7i9{(Hr zJ=9TE#G_;q!>(ABRbbd3-IAY^G;L39g?%b9tn!togK%>0gkTdLbrN8s zX)rlyxSmBDTP%0S9;l4H%<=yUycU9U5<$5-;0q}3V4^ZX)RwLgTXUDr4lF^)xq7 zrNl@e!0G(+hhR6&<+T@5@_8cW-ooSc_<#2GW;$~G-@al3HVh+Iky9Wd9dsnhCbx2c z;aR6>VNj{rf>r3r9NcyX7XxN_)$(fD(}aY`wSu-s;VP*y6ysHLJhc^}4#G)mpiAajUF~xptS!NkYqsN>R?b-4IBH`8=xHNIU6+h zZBRMHC!+C-j@q1qG4r1sl5mdV9RIStBmTy_Y>6X1$fqW&zo~$=&S*gGkwF zpLZ%S{DYA%pI)bN$e9A*!+7jD@?3FllZ_{Vty2IPex_%;X`Qz&$=U2nG=dpJ%6x2 z;n4K0mT;+C=FznoAp*AF+m8U18~0=5^HbJ&`8K2xH&lCadETBZZ3Ca*;U#+-Mx&nk zb)kKmY1p5dDsTnG_5C?^O3yiep}xoe(UQIRbkFQ`!~z<6Gby<{A{NB(QkI5Wu?3Nt zxej#dhjy*8NaKngoR8Lh*4@bI960KOUQ_ZF*+>4FR6pwGOPcMpJnd2cp>C|pi# z!aM}s^QQ7|?o=TkFupCm>&t+6e~;H-*Xtwhx}>tB6{1btB9OH1(b^;lHuK z^#1@Dx%*)C)6Qi0DY;6fM1EQEfq}mPe1w^AVC+QP#1>4}ek@4@l%EVT1L< zwg*TNLye5`fc?3JE_a~Ist0F&%CUM>t?*p@s#9jzZ*npS3)STY`3zyyvJi|nbNJ;0 ztrGf!mBHG+izB{jZgmri+BQ`z)7n-fSPj8TrV$v&H|yzEf!Cv$J)~0M8x6yE2Oyx?Rfkr zSt2lv3}CaE*2 zgn_-rVv5N8Hq+qF=U1b~0p48s&VSoK|LP*BJoVhKRH%+&kex#DPjgi^U|_|5C$B(5p9j(bz!u3d>JQ2o z{u_jyfxYV9;&c3DU@teEVIL{K=;eh5T71LF>?u$v z!FMxo3z>R-HE91#`rs>KDZZCESqQn9KeT-ml2u$SmMG%EzVdr@pNF6bDPLApaBz}u z#A!sQ(%I+q8bg^@k1un5_LBQKH%Qf{c9Z98R1*`$X}|u3F?lOHHDHE$yv%+?EA;c- zRlpM-q#SbT)m9}p(PQ!zK@N9TReA`Ed>{UasX4ucJqO@xK%g4^D>Ld z1sI`ne)U0>p&@JDOBZ`cX7Xf^S5HDR97JvI*Jh5|=9(9_e6rK^#ts#s(%{|cse+WJ zGg|I}X*9^n?RqAWKkdvPur$-(P>3Qp2pmPZe`CVMTNq=R`EDgdBdO`V$Sp zXNYPO-^tOc61-R>3fL7>WBKj^qdVhBKmPjZwV`qH@a50>%kT3!<-0?d%Z!&B-_pjZ zs467-Ns4>;n!_D|4(D71S)oq}0+7>B&D`Li(AzuQ(vfvC)KKMG`)nmQ9Y zLyJ&~^W0L=exTaE&Eml1C@~U_Ah@()P$dV=Hr>BPGdXhrEF#L`b@6N??^>euO;8Lq zoIO1o`Y>XAz7qN}KEqPNNeaZ1N`OIjp7C1& zqw$je+apsjBU#5UCF3fk!Sm|4fS;RYa^k|FaCeJ|s6>&KTc#jeXtomd1e*u*7y&dN zMNy8(Up?J2_1)`2LN38XpV>11@7y}6;VY?Hv9=4~$Byk8nQk$4Fx4v!eAxsc*wtPX z@Bn=76+|c;mR6JfWU7z1dPGU?E71vm%i$Q32WqKj0KPLPr{ed2@DQ9zaDErb--wC{ z6QK~U3UbTocsF4FNFz|x5{g81+Ma2Edr-e-B#hH@rrlo)WgcU4GRoJ`83=pzr+j*j zbE>HXZhY7JHtr}G549S9P0}oSTu_So)YIdS?7!`8-jPdA5J@#V1ytmro&iepLPXoj zp@iQJSK3fN;7eAAZ%tHemsAZsq!haZJs z>QxlGy}x4TC>cE+U^>j_Ep4jr#T0am@`k{YP=Xr=E@B&bFapwV?n^>RK8wm=1PSX* zIHUi@Zbqc{M?^LV#>P&&3vJu^@pAiLAmc!^VPhqGwpC^Ng6ZKzI53^0Uc7NPHNk~> z{!1Ve#FBE1o|gZwe(7TakSJc>0$&%W{f9<=nZ- zr~U_j1rl$R|3dRq0L`1CqrsGY54`bh&%stn(V%5@#>uvr6C;-JJ3G?C4?XPcOb~s< z^amyPvII1*qox#j0w+ros-z5uL#u~SOam4H&5HZo^jx8=Lh5p!T6yL$FAi6-$^GlZ zEJdi=)&|m3zc|2q3_{39+B!(QuKbModbbw@8CI?cF&50w6DY8hS$^Us!V$X~Y)(Kt zm9imqLjP#-g-Ew(OyLTN`8nWwKbO`oMakXPn-S#xW@LxSL3CA;NPtr5#la;+yDwg` z_<=xK$~=Y6m3C1>hO7N-{od^+rFyId-@7utgzQ|e>%$H0jP=>YCz#TEmJS*Q;!o7v z+bgkTa0H$#Uw?OTZ~CnM`$JjWg)WI?4xJ~L=j-ybsHzHuZZ0oJvqVbJB+Um`&dWtr z+y{kBAGe&x5rfgaea@PvPx?xIKqJZLrlVP!v6G~;gc>qIQ;CbZLBWno$zd_K%`=K4V{@l z!=_vCu!u;pi*xK&aVqW?=PfZwLU=bOBcluSCP*SUfYK|6MD~fJTR>xis zGJ-_PFvB9mEgvIiuxNAPaWclugrj@iSGxJc4o(fD&vexj+moS zq&55aC4fI09okAq2|ht@4K@KzTZ%0QL+2{P@suH0s!p%nHIXL7=d`SRYP+t>-Ed`U zM??O3gC8NCLB8f&fZ+Mua`9ucqNk)*$L2W)If;d*|9*0g7iokq1!SN4gAy?tv%P={ z>8nU*W-kT(3bmNJ75OVo^iL+*bY9cIFcz=8Ar?+hs~yfILAJDcMY?(=Yl-cJsYXck z52I0s?@{qQlkYYpePdPKv(RT+p0hG35aKxFU1L4}LxE2)E0{H%MTeMng-*~JWBlEm zRXMiLTC~YDO|NR(yg)&?2OP_QKUDPmZ2t1dQ4COoq$H?OeW`4OFSXf{uy_QjtN20O zZr~uA@KNG5%yUbcP=+w&FUm1VLq1Pxk`5g>WK=M)x~0GD>`hBOqO!z^$7m+t>_t;s zvwMp_jlHH&&#ys{%w!%*q`R$w6=2LoqY_b{b)?s2z9?15TIkQ++daSS%#vqku;s*O-m>V$niI}nX`VJQBeuCrN zz24S7$sJ082Sf)<_Fmek*->?1a-ENU*dFswpgn6Nb){KyS{xb&qn83K%mF|zg;4T3 zkg;u!G{E234+p@ahu|=hv$y{LsGzO&=J2v!{3#v*Z?cPuv{XST7B2V$@;f{JD~kw` zi&$G?Q!%wUa2s&#`uDYl9s4s@H?Aas``tdCxx&g?9rZMmW4rM<4D3W_oAPc5BMon{GZNQ4N>2nAhB2OC1+<4* znLQ*(80454!gO=Dwn%ME;dv;mWbaijK>^KKJXK6+Qu_jRSd7qCvF)iO6CNNc8FLC~ zS&J#0W|Xfeq$?u3O7CqN3UqL-1X2eV$1|tW?m$vw3z&I0y>Ek}@h3xO8u4c6V`~C6 zoQxi{Zyn!*!N~6jq90eNRwQs?9THyb3h&F!Y$VkA26_i~5@oG3Fb`^pws1{(MrE!; zcEFt>b)lO=N$f)x>;#@R5)0P@kFUYS-es=O?ZXXn4998H3W9K!Bm?WO#V#NDE3vna zm>bTOrIKM)vK`VDkH1W$F9~fwKnz`lYf13TCFj0;K1i)ocH?RbK_0@^0SFMd??ft( zO&}H6)5Gr-3VN~k4!VJkh>xW(bZcwUQU>o?*fIHv?3}@2bR89xbEhz2V9~H$S`h!L z4U}l7%-E@+tMxV5QcRJ|H<@~Q&a;2M(XX#dxU%}J;5j1^MBrWWS1-nAJ<%4FZ1y}c zhNTI1HP$+yYocTGk9|~d2kfJ~f9)eC%oQv%JuH+~@tThMhfE}V?U~J-*bIb7xxiw# zsAty+zHR^L=+o#*p=gEv{3&a~-HVulHuEqN(tJZZ6@1$aC_mmeuF&vG0bxilFO<+D zHjv4hs{0`xTgSIYQ;$L4K!gJ4O3Y`-$YAmd$~-;R=hV}?Dmam$&D|o)8~yyPCPI1B z=K~tUm&?UL9N1W%H$2mD+a>SGx;BH zD-%p9k79Pswq)nv%%{=ej61e-cb?8Y@~R+FBS07DhFPfyrzN)UdLGGzr*p_4Lv4h8 z+prD8yr5a^D>)P^BrLcrT>@0F4@oOj$H%|QgRXDkYVQLTj8X7@lZxj;cd zyyU%NbbiXqz{ZEkac{&e=y1S-A1sBUzN^0dmu7E;XQ z)DSrkYl*xRC{yB13Y-3TPCrchqpiGea0_T|L^R*VC~l)BnZof@2uhkmygn;)S)HuU zGbfVLh~-P#pU`3{%P?H*TwV;*E(^|BaI!h1QSS~;P!1Y{2F5w-r*u8ZR}V17JFf2- zHxt%hXG|-oaIy7zYtF-Vlh#MIh#@97*R_TQ7Gm+Sy9yqHH^&SF7EMH(4^(K>Zpak1 z2e(bMJMSg$8V-c-8m_Pb-_d=Upl(k@jbVlE1Kv-sk8UoJgsS77e%|FV0MtX~9Rh+D zV$}qVUds)7%BP~5<3o-CP79xftUripX=m8Ad;YL^gPIEYevLBhCTk4SS*JT|e%dhA9kxjmS;qObzkc6NKYq=#+aLkDay?k3$C#LOT0nL+<(3I~h>O3g zESTlodoa`lZ zCgi`ufNuU5trDsbuFlv*aa2W}ktjwNoL@(B(Ag||{K;;A40-&Db!c{F7$3Gs12dRO z8YRuA3G3KZb3GJu6-~lY77CLfow~*I>?cZMMUVx#-0Job4Kev>0A^k2Gz!4n>ybAO z-Z8<7zzwg-n;}XX>KYjMZ*tRAYiQ4END=BiC83}>D7w^zU!ry`T@X26w*ph9kBNj3 z8*I;(WiHEX63 zD*`m?6?F(|)_JnUDJAr>yE(4onTP^G>PS*@;;NAM7L&wCt;xG=#nS!^2RDUmj8-}0;YN(3w0Gf0 z_v8H@v=r*aKYBPQ>3S=UHf&}jngLjF7IYgT@!mR{oV*Pp)5PlYdKR8lI5_(@q+>i(}1Z?=T0MsV^KJbbd@R`X%2s2JI#GPO!<(xJBPkpV z&f{j}vKaSxqHjqEFLa^l#ctj6amvf3SxykCt=lkBi|J;z=c;*KO{u{f!O0r8RPx#S zT?8CVT$Fp6r$MvU=7Saw+dooqqy%1@gL$F1I+WL98(i03_j*GyAS|%~_oPc~vaMw6;-k%E%M{%owc->!m5*6Pge{F>NeZ-E_dN-jc9Xv?wV5D{ zM$erwe-%2IP7@Xqxe4oN-i5xS4f70;C@Vr6QL?ZYiJOCzrIZz9$k|KcFU1&Y>fel4 zvkjDK<8HsUxg%py*H-#%q#PecF=&>QTA(Pd(i${SoSKYv zpJEx>?SS6|dCAw+j(co?6fnC zB)yngMn>6hrY{_Xf-1=)BWv1|Al#7ETM_T@RGNc*fb#?%={$DYv8o5)tSzLP)!hLL_B<{nlU!{byV z4(;2_--+yD@EI^msS=0-RhAFY!Axe4nkH0B!{l=Y(LvREkZq&Bb_S3?cncB63rAr6 zSbIhdCdm@HtD)6Jl5za-6q{!m`nXcCC>iUG<7SvxCE3W5OWGE^LgnuLR|_D@E~!KN zM$A)*N6p<)FT6oWZeC&~&9f|#d!zWN+ozGC{TUa-cWeHw0yIlwO=zT+kXa< z#A-@rGF{0pYV>L=MY$uvgO zH;gti(f96G+TXP`C)^>o>~@5~U-GX-C^9z}8)(9@F`Ot|dsoH_yHvGVxmBzCHYY_p zaImp<*_rJ`SLxbg(nsm;>TY$UpHsf1u_C?!|CrC-i5Ws<&42|Z#0G+AC*fWkig$x5 z5g7t#+)q)P`W|XiKjZncsc$ry5;miV;TsArG;lI@^&zQYZ^s%+%jumbYBgsHyszqkEbumy`$Q8OLt!Bdade9< zBm}|~Nn|F~QOJW`uSIGLUShn2%!T*QuIQsxR*!WROPGa>yiTaH1=Dpivzg%>`{xQD z(|hh{uFLokDI8^}v$_|13|YlCH^DD<9aiX#xNkb9H-)+WIQ();N?4fPlp|j~p6|Xx zRTYPya-gn6YbD=V7$gV{PTQi zgvOxLY2bv1kuYx$=A@dm#;RNiXo62B?VIxI*=Et+oOq^^=_V_r>u@U|q{1|JKxJ3>sdZy%& zE|gH&*`%uA;}P%bbYvSc*j6O+xxG504WzVChn>1AgDKr9EH22ENZ$s`TR&cWCm5wH zg0K#xUKSh(k#`h$GDZ~UCEOeiCBk}$gK?-U&_+ySy5FZXhFB)Pzz>l*L+c*kJ`@ul z!?zNzUtSFPEauV1=$A^n;=1|CyPoTN+XE^ba{Pr}C2nkO@^>H)cZkDsiC&X^mh zF_s{>C-T#n#ciy`S_pM~Bgps;n!(uki=Ee{wHYX@q#K*Pl2X!X)w~#gbA>XxKqN;j zNk{?(@!hOQCR=5z7Flk}wHRT&^zd%{Z;*0kCmilOybfk_lJ*YBPa5nOLkFRI$lXtr zfS}hKAo`ko4=*X%bLp3*n0$$B z5|@8@Pfl5TZ@jYoEY+5_!7|5doks5-uO+%8(<5fSWR;uc&R3{Lygu*Gt;_bEUBmyz z@+?%+Dn@{afPuI9vq2eLKy|**B4<&$Jju8Eaokl_Y4NJH-DZYa#wIAtpM%xrlpaC7 zx$IL1#?t+;W$NVTV*SLzzOdNqli7<49@>+@=iMoP7eP`oclnaQPNr(xR`PdNio7YxN(g-%sPdrKi|Ah+K}i*xWzp! zxb5I!TYvoY3LQjOrA=7GPW=J;YK+&X&Je;L3j$x9Q?ISUUfyhgL@NIU9rSRc&mI;{A&;0AK3?#GK4#FKe<&wy8WUc zye_fGHj27##49X8x%_?Q9O&6&i_I}U=@?jxOX{;|C^YC==xjr-ceV#jo^U#HUeH~6 z#l}{Ocxn-SOb|XfA|3Lh7gG6NV!$0-{9r$t+>Ouv#IA}q!ZpRXNm8S?u87;a@N^n< z8lr1lSR;?0`eE2v-H^gc++9nZ#bhP1D*C9Nb9%#XO>qn%9ty|874PPiaS{owV|JX4 ziJZ|-bJnhQv0Im1m7jwj&Eh1}8BdVM+^(^tIFMb*4l&t9)+F$OK0%=Xs`@Z`stVvA zgX;HB8Jnw=9uV(}B2fxS?MF-ZR-{Dko~basKF=LqP>qor@QmCMocB~(K>U#R77jCf z40U?tZn|t@y&k2T7S`Fei~GfV?kx~BJ_#0PT32uXIsqrnP8J4*WXJPCtyRGDpH^Wt z(n?)hbgb_hm>l7=sV>mzrmxBbBuHEbtZbP!@EOZ&h+Gcqq1Q531 z$xil)O=k*NLuzR>dQp|f6>2Kv>wF;$r)KO3WMEcc{GV)RFTUP7 zs>-bk+n3(-2BbR#K@gDcjtvqjN{W<(G>EixcPlA^7<36J4H8mX4`}OToM3Fv$Hq!!6KNbEY$6`%IZx zT)k8)yrdK1(Rg^1Dv#@n;#Y%^$M8+cdPJoedKpIZ?a0sfZXH@yN-WX#3SB!jt^jIn z!{PA%%Juv>zl(9-F#KG*ms9mZv!0i;xz#m7Zw=^B+Yd zBJ312v}fK$GR6?11WR6*iF~}*gn#(sAsi!s5M$%pO@E4O%Mq3JKs9X#0H$DohKBWB zv_MR#K9ui!!*QqyI-!(cK?*tBd3I~IFR!3oEATmZtnupKpR5V;Ejv!gI%`dkzL|29 zCen&AG(MJ}f~h8>DFRnqvsH(gUdayWI{^hHynm?X9^!CK`?`)Y^VbYgs+m#hM4`lc z1~VhCB|l=`Nd0Cio@D z{gpSsgfyP8mJJr;I}nW|+7v;|N9OSC{4umvM;Zf5JZ;gXvM z*H3Q7$@(DuPeG{5gDR*Q}-Pmct4N50ZNW z#y8oscJg#(J*POwLLy5Os6=gaV9(GAh0F3dj?7b|{Eo?PAsCz)Q`=^Hj){5GPUja6 zsr2}V9XsCg7K#DHyxMn95j|kWW}&Fk5{j4{yg#*nbO=1zlb#I!xNoF)QqZ}6k)q1J zkzh8O=@e#ml)OiTx{eQd&o_1c_ny%*K4E-E5wDb@-@(1ztpop9g|WeCfwysgZS-dX z79+E5X*BP@M>Gz{ZNhq7!DW=8#^2=#ueCJ_WBK{&7n4T3#_bG5A(Bg{9Is}CPnM~R znpdvl+uH;xs;;D2puT$hoQ2VP#-MpDZKuETIkW6npa>6jnQ z9izDtuVeP{uxA--NQ82S^4O(s0u7!dLP!X^z(EWb^;$FD4kvWBL{dI#X>}Q_()kh_ z;%G`*j>(Lke}j=9gJ`y;mgCtsv?BQd9)e8l#RHpQ=lSMHr1;k)VskwvQM?&r8cSyd z^U1{vYZf)oQ>EJIc4)+fZIa3oI+)&yq@-pDe~_Vj)axSp-{H4PHO!^+C}ABeKf3;;3r12 zFZ!VEYaA|=NV&wA0=t^H2pu>5^wZxepuWTY#V`|J+>S@_Cw-<20zq}aBl=`3WS+^+ z?v;%Q5=)$COV9KoNgSp7nyqo&-AA))_V?p4yPHca)^0dav&vRU{wyL-S*>P?a_7;x z|0vyiq$^I*_iJ?OuCVRZ%R^r=(ZFz&rSETHp^XGK_ZZXkXx|XPkP!MD5)*m0yyw| zEH<4(k5#b5w`7tT=^PV6&dUA8Wm&R~s27>6^qypgfvbf`M1{wLOy`?r#h>-;l?Mtg zmZZKaRE_3!JY5oeq(74~jyibntD^MIa8Z>;0y(NI10(8uSJRO6(Vg300a^q2cR*L+ zE9w8}6I^j!&3VJaiaVlAloNSaZT~x9VU*+%Di713oaAp~P(m8+j)hX`Y)?)%u@G=# zdSB2Ivu7Lgr6vS781u{@h;8@tvY1r2XsV3d=9^9{oC=Pe6B z5n2FubFvLQd&loD`8< zXJk^?S>L>1SqkY|v!y0$3y(yt%T2Ps>wVH*k85JaiHQd$Wuzw=XYE#V&!^yE!%!w= z7Bk*Gf{5>_L9du3d4;yDDa)+VSdqFnH1HNx@D`Sl%|~AYiOjOxqIuHO9FarUP+;uA zLX%|zdFSIf;o zm6_gU-1xv(1F_3)xO!z>!YpbA8dRS2JGHyFv2izPUtt+ygeEFaya zYcXs7mAPI3R$22*sa7XnJdb}8Y{@~g18)kixm0F^qK2&Kmde0Zy;09jZl^{+v;u@u zcc<=})!b9jFFcG`%5>&luShsuY$2R@b~v&0#qP82*TCAsE$b8`tAQHuvUkzmcex3K zc^S;UZ1@&n!d!X#{m}6p{yLxnXHh#*`(GGSPwaI9wdUF6$YhFNN3dX_@SLhJ@M2QL zZ(A}x!Tv^^UD$AOA{j@~CZ!rmA(RcKDw4mOS$bwXIC{_mSh9Po9o35n!*4hS@Z;2R zewrAuXE&y><}vv|)(eOI3MF48CBA7MxpbIhm#!os#KUSLZB}?zp(!!Xnf;`BPrluV zUZ07AbA|yS{n}HtbDsgzrrs!qh|<(dM75bo078Z2cqtlUcV=g}tT_W*TOM~8r zPalkH1rVt!r+Vv@Fa(v82%~*6vTQ8B^=hIrFNBY1Ia=eIIeQMPz7Mqr z6yE~n52Nu6L*u}FE;etGg6KQ%Lh2cREO@5i{M@s!Tw~5j0Wt9Hik&Rys2UT1w)P?h zTGRcof(jQckHxpv^N;Cn@G@q_Vof5FJ-?5UA_lf5OOe9A4p{H9j1F8FSFrVw3LP>W zVyA!!N>!PIh2ly=6@E-@t^zo-O~9F*D?I#Z3P7`DhfXJ4Hwu5MJ5PD-#p&-%2Ru%Z z^hP4d4IQ{QDiWr`e_b3YRe_YJYHr|ju5(>T+Q7;X`}$NvYU14J;+Vvii;;}cyawLf z_l#~jOw=~piScMb#CZQ#HbDNkd{`ee`7>{OgT@RjVJAIN|Eem%;!`6Hpsw1hIgZ-K zl{*APv9&rujyUGzhQJrdGB$&;|L4Sl(~iA!ep{3wv3Ed~d%ez=^DbGg#$W>Lvw5{y zL_%_ISnxX2bqxX8_rx!=m5Cl<`$tlrQ1meBaMtPx08X8GBu{z0Y>tq-EhIy;$dq(b zV_|pW6Tdi`qqCk5PTOLJMrK}0~8NU($1fYWTCyHi<_5H{jL59kJW8`1B_N0@}9Ot~**EVZ1-B+ILU zOz^w^mc;|>=cQnCu_ZH) zDfdzBJ~jyZaHBQ|?`meW$%6yn)oIbge+-PLa#t4Cf2-20l3bxd!3tnh_W2R9s(z+M zCZA>#BjkF~X9E>?zm`1b%TsL0=(is%XM0v)W*Cd@*0S{`csHEt8rO9)%=P7{ZIxqa z`lLe;AZ$7`rpLE6+Z4cvQVhFQfExo3E#P76f#UMamm_Ae!UARD^~bY~DdRa(8(6py z0q-#a(ah9=b2XNgf{oWy;1>+MZ*}i%yrh{n1fS@0w8fPl5@R{ zmjGe9X`Jb~J7O7@iygMAw`=_JKz!@2_ICuI)4Z~V+ngh2zx0$iUrPw#p>gj`Fnn+! zVN>7Rw5eAB+VG-21ez}Cz8A9@lOYGiee9%An};W?t;b$tNbFLIo=OGJ#qUR6bFoh* z>lBNmKWV8dK6AIL<}{j0yooUN4p2SflwUdr4yz-0tQT90gAFJFU4^6X(<$Aa9YW*_ zeOqssTp=x;?4$D@tP^&4bGn$IVZwEZh08fD_vUHm137i>P`!Hm182ZhzvVu^3|eDT~y;Um^RVhvi{s|$j~|6UMydFI|-8*Nsbs~pnH=eLgBV_}L3 zUYu!+aK$sFiu3g&ASA;T@na;Iqce7Z^*mKvub$~;c*hBg~prYz?hz!Q!JC;A-SU3!ujj@@X2)vgm(RR&#=G};X?NB(26@yfqP_58g<2+d> z)ILesKuSCC8j)ub4WX7^tD(G@7|IOJ#je9)+y~~#aZYr?P5DgvP^#T(a7>jRUNb9p z=fBeql-Abs6Fs{j7R;NP;s>9ScwF)4fV=Dc-i6uST6M)3y7fABw;0CF+MmnRsDaH> zIw{_Ak&V8!_)}AZ-@34&W_`MzmGi&|MK@O~>lbxTa)+OE#gfg8cetT;y^M0m-*$b5 zs0*li+Jt8^cR35doVF(zit;>{oZc3;9pmRFc?3;)%%CYxB}d_8;u1~xT7K;Q%fk#p zmxEtv_FuRy`d(!9y_%7%{mrUmNTrhEGUPb%V!X%2&`zZ4$CuSFK|=;Vxk?7__x1=q zDbn1^#6eIy4Zg053R0dMPs+XS|0Ev!qn#Y!sabzmA2Rs|K|uTGc#HVg@y7Z6E9Glm zbz{xh0|?Mu`?twHQ_{utve}x*a1r?FocSzz_u8CmHJ1rD(PRaQM7i$L(Wc~PPXWoY zo9P~~+n#CQ8O0)ycuR{cZ2~|EY96{66xx(2dy|e>=4Z5IH;-OaZnj!HZ@z6Yr35dH z2{IsY6u=E8AP!{IEF7Pa(x}Z1Z+BXQIx=G~fN@eb(KwMm9t%w%5XFq_l@Sa>Ka|&u z5f?!cEqBC+x3h*t!6(VB&WwoUZyCYga65{n4*W75)1UIE=WfQaFY}7zPtV=mGRS%a za6szrQ5M)3cHjp;4b4=k_Lor9o^OlnR-Dann+=;x3r61pE{^!i(`WgGi;&XJ0FfFS zYj&|#)w2q~2X>0rShsU|P2gpy%{ZoF^EU6~cgYsUl$qbSFCjpUHT8_` zD&+5MyJm7n9s;R+<&@`8dpSSIBI8bRbC*7Nq$>M5I9KL{dorWX2w~{coo6$pO=8nrb7wi@flEF+sGE*8$jpS;CI zdWtz6)$uj?P;wJ7scqCD^v0qu^PQ$^B++7aQ%=w!)v5qt-tt%eH9jw_NAWSq;z2qw z?l;QJad&mBGRTOd&eRpVcf??PL+yulptrMshj^AeDejp_*OtS4-T7<;w}&pX4}6pA z;F`Y#=)WrgW9?T`C3bs6a>AKJ#AKpby2nTTBaepDUT?Q?+l|VmfC(XbuW92~_5i>V zPX1)ggU0QRv($9x!@D!Hq3)v;?g1&~M1p7X?+YK|v=>uY@~eo+fOA+~Q03=!R0yNu z7kq`1VEQ*oLO03P4m}G@W+m^52>q&Y4DHW=3(s(w5>!!`GfGu|>{WXanWczhRB*!w z-wXxIP@IT?mXjMn9FKXlMb$_hR3dstEyt>;Dcfxn$RCdxASjg79!0%GI7`7=gQD(6 zZO1EjebjbUkL7A{+Alx6_Od&y7FOr6zaw`T@tEEtLzh0+E;N_MS_R)SUQqV1*O4g+)EV_)WF1Az zM8UE4b>rznec3)K`+a&QaGT~XgDp>{>4%#pgw$e&e*uu= zb-+8+GbuB9i7^*F z6YMI$PqHheeQ`7KLzqeTjhG+fm=eM)wr(JgU0dl#1~kW5r|tRE(xqJL&nqE{A>|4<>O9zCiNbH%)Mc$i%o5I6a5# zn>xT`0IbXYJNmhq%5LS_2gsj284}}MwKKtXzx3^0q?QLe>&p5hH-3V!UJ`-)NrwP^ zaL11b&R%gdP{gjYWpDsY6A=yM7QJC`>@V;ya=~xz9NSuXCpjg49%MZ^1g#fN`P+uu z2Ff_xKW;pZS^4uj`oei2vo|;HUp$($1R!-{MnObJX)L)$f+07`hpHEoP00ld&v}4r zDk7I%FBkVGNyGV2orJOfX@hCZzN*1-sx^{W>h$&Hp%TcTGQ9Y$ao*tLHh9l&bn0R# zL^^+1_U4lgs*9Gc*jjfWytyn&+RZl{e&=2(g^uI-(o{MD3qfM~O7_zj~s$=FbR+TWVanmkJ?Gy(iMr z<3aZ7`G(lx@4ep}0crLxx-uMt)9h!0&WF4Y_lNCzU-1@oovePf6Wdru`p8u*))W{1 zB0s7F-uxcfT$#xN0MU_5%!w14m(e0Zb!XOG-W_kA7b8ZryrbWFMvUp!9EfcPD!?w! z)xoSk)G~ew=vSTE0!peNG1Zu2J+e^f>%_RN4>_!UG{UA4a<^HW*n(=%s3?fJ>N0?( z6?*D#0nUng_Scr@PA8k{q+qNdMG2RDYMjuWGxHY1Ld4WwfceZp$MFl02X~xqANV^z zaTgycd9uvB>3H`-+NmW-L`<$OY|At-{>Jb!(7vs#>fiqHa?-%mIfkP>^aBxU_r$1fnkh(snu}rzmrx^Pw3+Y!sUU zR-7YS#`Adl|1NkgXcZ~+iu}cdF>C-XTwdWn{1$-B7yjoxTzxr5;;hSNYju! z&g~w@WG)Mbe+c&Nq-XgKAb6gxz)0*dU<#Z|yJ|=0KvBJ@F0rrW+Qn5>4RN)ALDlMp zJv9t=%gyI}mb}3MvX8LoGdu?%2a$#A_B0}kuS}6=yM3Ga%wIj%N<+ke>v)3O=UDX; zwAOnWL>VGI4%U93!s-k!j@qS8N&#C%RO6Kp{<*!GXNI&_v^xv_-i4WN1vk*mY zSjq6_P6y8=uw;*$8u#@AZpM0hJ+RkI0HpOa;1y$6VtGZ8$9^fHV``P5XI_%D7V zR{>nXD>}je4mn+Kh!#~BupFx>)Rh0BB}!u{LcZC>wi=P7_saZNq+Wz65up|-BaX^LeRwLR3h!5Dr#;H{+m==ISIfX2LFbp^NT*kY? z0%0s6Ne~z+1E9M$fbDO;6`PKAka3WLj<4e_>F%B1!^K(la)?wQ5Y}bzfJG>NR2-Pb z2uO(~?O^L*1=Rm%9(&6(K7dRfq_77WnM_imt*!C~zL-x%KM4G8zfH=Y!@BYijEexX% zUbYOaD6T*Gq#Mfb6b+YDdB+PrF5iH0aoqt&9BUF>CiBIJF&6#6+M56Zj(pU9KcVkn z&(PL*E`1*foN1G|L7JA2B4eAZgIu43X8{@j^Y5~I)<(;{)l-3E;yV;-ElD4}P?Wv- z3~(B-vxl0AGt!7uVsC@bXi)woR^`ka5XttXo_PGeu~~=qktAN~p7hovqrv###|Na* zCA{>!g;!kRSn8{h$(?_VO!RiQbf)j{{|5(25Ca*{xuQYqmYw1c2ouVb;Gk*%3#e}1 z))bvUL`K%0T2}ZYKPD>ZIylwBB{^9XWpg>=_$zqrlWu~omvR^RaS?&W7*K`pZhRBC z%3J}hvQ8ho*%M$l_${HUx0A~Yq9X=$E~uQQ;4kp+zkB2tYg{j`6w9AYuJP{0J#i|z zPZcVE2xOM{SD2J%|GL^=_ja?fvTDU;EoH%``kA}YX4_&bfLs8rmpK3rEi9P$ zSoaU(5(WB9PBH+Oyu>cKpC`0X^I63k5I%R?$_uxdrxXXrg9Um!?LAC#Ki3+|Q^_9U_?5t> zMhmOS1J-ij@nyI|m@)q=0pd*|Va&|^gf@;M^am2L{DD~%06gZJBn6maso%VBJ6Qk4 za{jjhSVd@XdaS!gYR4Saf$2Adz2tI7if^})JY^hc{_)U#`i-z>dFtZ9D4O}wEu#}{X9g)!RU7ow-$*90Tr$Q zfrJ$QJ*xgt#UD|sjMW2td9HJD$jLfN19!t&nfwTayBt9rq)XRU%`_wwwkAjdugn}O zLA(}qCt%3Z)lfv?N=aA+G*D6LXd>y^3Fa5&P_+t_Gp)g z+h0DkPXof319U9VuY$U6dr}-oY{!UXqjp!JE&PkiDl6}w+Yp#PQh^TBeYqv>WG{g1 zfSmiE#=`y*G&MCs+N0$M94JHq;1db{(dDatAD~U3x<34i-BAK0n=4ZM7x0C=9nNfb zTPKy;8wTIQj|XnrMn*zA=76KqhsLzu9}WxL5whr|(Rk4f!XYjV?CbW$o1B2>+mnzg zGzB{0O2A+%fL0`G#7F#IGH_SAg1;&z{@^A6zkm~}HHw0_86qUn`kZasw)(^@enu>er7l{ zV{d?#{r>)x&!u-2;J$o;YAr)b3xYhekgZpqr+>MzJ_+F>fHHBpvvGokiMNI04Prx5 zqsSjliogv3Msj1+B5NOuNT1vmqC@(SbmQQNV-c-DOh-GAWo39Q3D|!I4KL4CaH=Ot zME_HX@!BDUtC}#Wk2WAWSyi9psayK8oWkEjkPK7@DLLfe&g1W1Ve2XI<^8ueAQFb` z{j+sZoR{f@vID_B31TcPwp-GgAGy&vGIl}uc3SYRex);=MWyQPnpTL;Gdx$@_BwAs zgu93hzaw^cv3f4r8DMV@MJ$Esp-`mEwV5;XrQ5ZZ435#_JIWAc58|Y`p|VG?;m-lP zK|uoF*w#$T1jOOPrc$BNpc(>kAByqkC-I&=idXC~ItX@oUi3|b@O#2PP{<@Zoorz_ zBgk!O7#8g2yG#L4fNcEFCQAIpW#SBND}SAFXy#q+y_a%-=LWF1`#3y8F}G{CWDh|N z_w|a!rq`+xyw-RcnD!Wp#=%$LeIaQA02-H9i3KC|4XbCk0gog4XcSpCt4MC9UTj#W zGN*u`vh$O9{rM(i4uf~H zjtRCDxxv0J79+;1b^ULQP8eVK>KZ(jywSfNem2ThgLg|X5#$%CpxGv zq^Ou|0d6cBH)*936_r-^LJy^A@aKVYT-Da4q5o>fSr*Jf z%!33v(2>c=!kEVqDhj2&n?4CDy6SH_-~QBaoR79a{>DIe5C~(uGin+m;f1O+dwicG zNGxF4muCf#-!_@|JQ%l`O^M{(z;Utft@YIyqb49t@?2B$Xd2CjI&xf>OzD;QomM>n zr>OKsn!_l*|G$-M<=;8--83kf&6^7$BC%^)NF)G`q6X>x*oVehd{jZ%cXq3IPNO!W5E z{c8MQP8FU7+JRXJFWZ_D{DwiXn6y?gbV{|O9CV*kYULO}BvFAwe(0+vmN$XIO)Y_< zfmAJ~n1qzK7N5$(cq91I`PtNNXzx8^QG4|~UPM~b5iJ~zr8HDTerI>9h0rxt3Yd1E zmOo}dr$PuYJ`SC;F_q)j-RV*pZ}WnsnY&%DuKWva5v92Ll>E@Ar1;mT%$b7#tnQD; z#qB_=J?V~p8qZy)$s-{TpQzhDg{7D2jP1a;t4$i3HJ#G@OJR^# zOZ^+pYEi(CPoRsWXs$f{r)+ELVN#ujAsi1UTs^t3%Vz8H%3>Ci(tQBzXYIY*(i2d$ z;diojB6cBo?&)U3R6F7`QedzU*tZkm&$u}IYbdkPf0qH|m_R=6$JQx8Zq&Zq1-HnZ zNwHYbSixU7lVX9^%!b>k)xe`OXGi@Y_!!F3*YDAIW` z`qbO*dY64UU>q#f;p2~I&2wKCE_HXmtAh~fVEA$C5@j&wpUX4L)x$mb*TYp-8Hijfe(tJQ4F}jQ_MFKw|}LS`sV&))*(|BIXbYLAj;kyz@aK5G4&FqLb;G5yiv? zF?0o7C8T*Dl>;_uarf%`ffrghm5)AVid*4aH~&-0FHeJS6GU%Rgm!hL;XBTx&_IP# z)pfd_jx@tM4}sm!y5PAaxFUVISbXrSf9sX~`O+#>p&GQo_HXybmwl5av;!fhTxRL@ zSk{}?se|$2(Kv)*X-Ri1?`cjPjsLWe3qFnD0UTFQ$7?ZL{MidoF-pq z3rdsZJ|o%>}g6#x<5UVAe0Pa0#(Z?apr zbNv|nb5WRU?X~`>JEUP|ATY>N!~(tTyQ&njSaYN*4m3Y zeZ)FGo!CtOx~;fW)G%3dIXM%7e~im544OW@pm24*tcxN4YC90_bDHL?Z@Ykea=C&` z)@QYSTz6qzEOOYv+`Hc3^U^!-cbKZppL?Ag*^VrqJt_h=oet7cMA@YG_ zO41`IyT)rv(c%N3ks)_^QctjR5%EMT^D^e;Jo5HeVb>ory@k~#G}rZuqoMpdAfJ(E zsc)IihvJd^JTxa)DYGg7s)P_|iC#rYtBj|N{f$(*2Ju>*AYSVOkN@T3^XvP>3X;)9 z`SfLoRZ&{CEL3dW*ZbT*R_`Lw@q}#7)sxLC9!^@w#l>Xxj5%FFUEgz4FUKd2zf-eN zsTPn5L2841a8Y@*GqaK)8!tmVK!es0O4%^+aqc!{xQc>khhE#dN@p;qGU&@patt`5 z6d$SiMy{cVE_G4u974N@q_*cLy3xI!U!3l_%(z77)>w0SOuuw7b!oklVzN01^4#p& zX*2YV{5F?%i+u!$tP*b(pS{FCYl!17u$^#eum=<0US6y=y%)Mmp!BYJc^==Xm|5up zGBN*+{TK|`F`Rb7rCtFT*8OAmj>7U507(#7UuS&0|Yoxe*wGw*S-8@#!+ zQ8!#ee<>`9K(hjZqh6VL-;m@sy32IW$?KnXlWW~I8SoI4X3y~;<4Kj z&c0y*&#G6!?BHEl&0liW<-*o0$}TH$&p%jrtr8Wpu=HTj^Si%;kKxX?$oh?zA09S{ z2|ZPDe%pG0W>3=by1dtEV=*mF_RT#Qiqg$4Fzu*$m+}G-uG3eMB9Z{*@t(z*?oO-7 zBFwRDK7Pt`K#SSRMk4Svo}Acc`GFET<-EGX7aJdP(#7_*mVg|+>LB41qt8nv{{6I=fT<4M+`m2koAI`iEik!l99jW+bC=fe>TErXc|Wzp zKfI{!@tX4PjiR4Q%(-Rb+XZd!A<&1c;QS%Cvh*DT$QIViWQAD%C!^=3HuI|Gk>;^p-DVOrbiesP+eW*SPo z{xp(!NpXMg_xyF+@2-s$-6ZGgO*Kxmo%P=!U z40Y*Hus5ZT?|^{Hg#PV{pQ#s8pqJ_-Q-T>;0kPgP)>}p`gcgR4vrhLM!PR#O&>-2h zh^ofyzkK{i7}+>aB)a!wK9hew`>2IJHL@v?e)ckiz;;aN)-TzrhfKjdEK`n353)M+ zD$iqr#mWWBADuAF6R&sX%j?`6BJ1flcJmilkF#F1zhk`H9x(kWKlR{uzfwlI0hKt~ zbZUiRp*CUPmTi@YkfX`ZGoOC92~TsvGUJUWlX#ewVzkr~^iKyHiE;Ih}RW?a(oPImJ8&O5l_>d{;V z?f~Q0<#H?Q#w|L@O~=FGMTHOCw<>EDx_`uUXF9eW9&0UjTQMOzPMgJuMdn31ubfz( zgflXeDLfDFY${C>owf66M!jnMIp0c>b~0j-#mcfq>pJb%8$~xEKQG@Ir&ngRi=|O= zV!yIT;|*ko?IRh8R=u988Oo!Z1V02_#tRqz>tmSOeZWf=PqWa=$g zUKM~Sp!+Gx30-!ouC8>_|LwCw*+7=#hlgN~62Oha^y34BNFio0n_P_0;~zI#(VYi= zC(L`17J(s*e&v+)FnhiiHVIAS@o3CreJ>Z0J6%GQ&b&kQ=bxQVH^$AXalyU(shpkB zV2;K~-tC5#<@2o;Ep?x)HEZ?omV|3)nk@<>;i2*e{9Oa^~IDG8)ddBPZ zjo-8{URPy?U`?zS6`efX|8ZWo`;y*&NCxXQ7_$*}yr(4h`3(}pB71Z7N7Er9_FyTi z?nfJ)bfn1>ud~uo0<0IZ0fy8_y7eGR`J9s8Z7}GE8MOo$iCgzd8b4v44C23M90ivW zA-ES;X>@tt0?jYMIe|_)+UAv%r~kHdLm6u*`fA})yX){{L$}$NChIXGZRHkG0k=@& z@+~wjzrvLX1(ZjYx&u6kjH4=-bWj)8umfRw;sOuZ<1oq4$8=Kw5d15(j%Qmm1rGO0 z4#Sr+rJU79npcFwcEi26R#xZ@`3xFL_n+2Zo)?dT&1K@{S67`_lmtht z4H>t;S^#<6u@D!dOD8;8?U5&EkrJVrm|3<}Tri!aZlRCLEt1oGVW;fE_nX~jzFnA2 zZM$dWF(P>|(RBk|heV^4Nd_{uH_<#{{h*sf6)w=vn?z!@93vpQTF~rG-mAtm&=}Uo zF%%_*4Ebf}(buIe-g)|FAmiy$K3~4_oA{7J4uS_M#3)2?@Z*=t_9o;oSf8O&&OMX* z{ZMAwGuwLi4G?zSP#Y%LcOiMUCC7$DZ->h2UknOkPV&Y1NRCmT8T~_xOskByhZD6hEE7_k@yLC|321Pr66r zUKnht-g&YJABr^MnZ4(LEpcAq$_#&w$LU$sW2SDEqm4~>}@lv1c{>VXOBJI%(H!c_44 z@NRftJe~Fi^E+f+u#OBalbY~>y-)rV*T5R0X^SG6YxsCKH|U2MAEcDi!9qtp08)e#Paffn=9lxr=*sAwCGQgoxYGx6#Y}w| z4Q5fB*AIKeUK+rHX83Nw68@|g3H+)XDSORg&IDXu^?`lJf=l@%6}}-I9~tEQZc5vE zAR2x`8{M0&0?Yt+wN)jsb z;vXqT3u+Zl_YOf6@Gs|ahUsp=+1*D9=H9hlNp`u6djvzl;0>52Bk;Fu!DtkkXsId= zX!HwUrg9^YDgKZUJk9>`{p^&M;@Pw-IxjVe6>Oxfj}XP(fG;Wx^;>H~1rz>Mfs&T= zbSNfKPWr&$)0tY6b`p8DJ5%tAwmOn0whIuJd2I-JY5N>aiG_v_d>WA*#+fKAHHjv4+TB~--VqDYk-`V!Ov6iGn$KIVi z=#98E<_h?5JQFhM-SjfFFXa@*i%LZ5$XTe2)^&98)E2I-{A+(nirk#$30AM%GL>NW z6S1*onj^2KN}?go&XWO9(FV`uSoP_swptFM_4|<>c-`1l;}{6smpuxdiYV91^}}cu z__%&?ZqL1?Kz*5M@H}pGr|UE;xKG)@Lb(3z!psq^Kkp@(1A)KA5wZ0BxL^qd57{t& zwe#okK9`6|tQ!OKeUBvsQTz%BH`REqyRf5zg8}-{4EB{z4R(kZPh6_`_-wuAsxfT6WNLizr&{6;mcyVeU$}X#HDfh9nx<2i4q;exzc8 znIDRuZxv#)2}$4Ih9}%*KjP}sFq^p z{hO^2Z1I_KC;0X0^rX?>MKt1j!Kpxk&QaYujQbPC#Q5D?`A2MOUYpY=P42B0bj>u* ztq(~DXjwlZ3a11 zD>jXR$MTPb-uXBA>6SKHcx}8JxL#@@h%Bj?V=8bTjQT}EG9+AZtI0{8ZSnh*u)Zau z4lp!bT&jlghw^SWz8Il4ZXh4OwVf7D#na@D=6OIrt2QVCcg1HBDH@bipkX*N{G_O? z_1to;z93H-t;+2@_5SK(8vfB%NJ`?n zQ~FZD+N(L`ht26e8;}=Sx-lSsI&Y}p(BvNWGx)?b>U_NQxZ5@5aksj7>GYZEY%Vqd z-wOG+$NCGd4_`L?@-%#r3|?Q*1MStecoZl7jyncq&3pu7$+wfZGNd6{C-=dWs|xCq zd+e_)7)C;gqSPQro{LINEB(vx8rxRk7iby;a!$-nm1>ILCRVqC40+31wDTN3zbVc_jFDC6jjX1PEux79AU0!m`D|cmNW1 z)sU~q%VH_40MmnpbOjxAC#6@X+Je7`eMxzFo23q^Xxtff^31+sfLCqDuM4% zK3iG}PB_<_I1aOs65|&jq+slUhQw3gmU!-mOJ{fi0Dxn}3QlBRz+xRe17K^J>9;LRa zzdZb@gF1`L!1VO;)Kr0|YgSRX|JER(j1G>U3X<57O-=kwMjImGVJ>@eTjBKtlU1rZ zCFT6RA^i*L6Z9UU5Lu5(6?rNHI+zb*Z2IDZ@N;PySDkYEo6XUltQQ{lSA<*XXi)px zF23yDf1;XKBWg9f5v`(ti_^^;FG>&_S2&#GrdGk@_G%G9zHHAbhj0f~jZkwwVw1RII=u#Rbz=%Y|gaHA&9*4ZiXr%8y6(F)?MBm$*bjZFs9&3}t zfg(5h+5SUN`K0ATbHQ^Xj`k2Ze6~F7xMsVNGeYm?RdO?l!+D+S>Bqg!(FNu3@01nE5uiK(@T)A{ZoOIF(k&M1~sjA&P z@=f-wRWK?7Zu+CKrJ6*qk~zyHjfEI{3A@cU-E(tYv;5qMx^F!oe*U~=?8dfBkjrq9 z`TMMmh9G*XDs*e5!sKbYkMg;nu)Cn=;u!02?7VPF8avjRol0`F9=da9FvRi4`J@53Wx z^&CNYN4`tlI4KG-%3SU4S!!DYed?p!Y-f2}$qZ3bXj?H__jzp7r_0Eb;Lw-Kwt z_wR=6x|#I)Rd%+J^3>6WEDrN~?n*YAMoAfq6>a#SzSTQfwP^6|9W4r4 z`M#avq!;)e$-vXBNv96pFz-UbMQLft$Sfl!_8<)UR=Sd5V8j4k!<36teQ4l~MC= zxy}^xesP9+Lgpq8%6aab3dev^Vtap+Ys^g~-D5t~lEKM0%c4yx=(P3tF{@Z{!6%&j zt+?rwG!edVoaz)D%obEezv&}2A$OrLpO9^gRoyAeiLCps_OPuxWCQVWr7;XVbAG() zSqxtNN^p9LRo1k`dR7-B*!OZJ?D!D-OFqd1fm2SsCDzpYTxxKs$jgP4x{iuoQH;!L zQv$I9I8srZh1SsP@VW*{rfn1IE_D5LqTr7n1!GgND){%m|| zg}%#eWHS`s&lR@jTueZv1p}cSkr{QprE83Va_E&?x=ogj$qaY~-Y(gS%9(F5_7kfc z5cbMB_CsljhQ3tSAWl1*b+`5yPAU z8Hez*K`LvTd}>KmHDN#wZ^09=rL5^wOkd{^YJVo~y)3~kw!pF(Kx|dE`|I^X7R}Bs zQ`PHM4sJ962%pS~|M0=>5K#Z+imjfgGY`bxiIs2%(KUJ@9mC2%lH|^I`}qcP6msgO z@#%#z1E<$9}) z%f6xYL(_%{h{IZm%;wW((07mW#GCeG zn!VSFVDyrGuR^2j*3k^sri~@%j3R7{SnEa&#@p^Em(K)E0(Bc+8oGst;~9%bQp(`u zMP~4p&=XNrn`_M*UzQESKTy!WCAJ}=5qVF{ruJNe!KB&GyD`(`OyBPJq5Sw~43vmd zjsl`yfuR5()nff9TUyEa!l35#%g9;IAr-QjN-1W--GV!@i6as>b8UP}l&S$MSvRjy zfMX4F22M_Uh_f1D)ROujjU9#u_UV2Ro6oPw$>7|t(S}>2h+4ZUnA99D(6z}9i4Udj z!k19nV;AubH7f`}f{`a9x+Bg^a36i|yQd$U5?Cj69ilM+2-{S+f$cFf0W*~eW>(6Y zP@u&KS3@niN)2~JB_|X9d5M&RVJV$K!hlt%^%}Q<1wXowyzRzMhJmfoX2}feqGrpZ z+J#BhZ}iPfSv7_Fr&A$)VvA(w7i`WnlE|t2wVBl-zOL=W7GUFiT zou-R4!n@FPk-^RQn*z}gSL%JvTr^YKk@xi8dvWL^>}Ef~9%Jk`)K_)}Mc*rcORDRG1+u1k1~4>5eIQ^EykSh^#gQ=CP8ZIudj<0W>) z!um7z8BJtTt7bjq;jC5~HgN9o*vM*=92>zcKx3cyz$N!R+9c|^J%}9VJ0Kf=omLe} zT$rfbBBVMaGeIpWmTc(pyDj+rR74}^Rijchc0OCYFR1GTnYcvpX;d3J=00B?bJ~&D zHuc4So=ML?GtZf1ePYs-aRv+oVkE+--_Yy8U>mE{O>O+8=Vg>BotoVDG=3(0H0<*o zH4hrGL6QH%+*?OQy{~(~iYO&GNSDAMAR;9l(kT*xh#(=L14APX0@96O0Yk&kEj4t5 zK@J^~3P_4{zu!UJd!KXez3;mB{pVeab=F>coeeX;c)ri`sfS@$HTjj~O?cKq{iDx$ ze5tK1!Qu_=74=H#atV}JQON7n3s5crf5dfg0p=Pi8cw#jcnvsy#0f(j+AE3kha}HM zH{6=RN%z711)6`vp8SY`oqs*ctTeKTjo3UX5*o_?lDB<2h+!dg*0a$qFH2mt|d!Pq78T_Ha zh?ghhjZ4KGMaCS&g^=()G3n*3_lnJw!w1}^lkaXuAB>nDZ`dxVbBXmna&BRkt$v=p z^#Nd^7p5;V;zpAcP_>ZD7JYTvNK)v9sAZ2n+-&w}(S2inB}D+2?~pTyZ{6Kr{erdF zvC33QrsAiudnZn?J4$P=I-l6S>Nq_YDH$RDxc}>8MUO%lr4fbUi(&$XHzC32cw2Ab zJ=zE_kR#V=!Mln5`crfb5Q|mXJZB7p@L3NPB#>AEZ-WuV19@bM#I1cPq*P0?_{AfJ z!^ncmj4W=qq=q#C_wn6`sO$#r%++t-pIv0UXcxFo`bvC1i=c(XB{T72_FGC;E&<$Y z{YMOn_mW;H(;G1&?uJZ>(kFKKZ))IDfqlI8QXXykId<2MnsB26lW%*w?oT@-^JjMW z<|0JTwRPas(fXRbEP?7Si*ceE zr*lwX{!Oqg0`Q9{W5hlbvwqKB(XTQ<1R5k)Bl{Y`*x^|5)u}|>V;@x|#n{tT1znDE zj?+~{e!%BfSg43s-vBZlk@c4W4FOX!9F#tXe1kMmX~Pqp~ZkWj)ViZbnC(fj8wPV^Q(KjI5NcnTD*_Nt`&KF*qHfVO>;?Nk}?)qi_HO=JC~m-N`6L|Hr{R(Eg&Ne%a(olAdPwX0q3ceWld` zOZLWn5jo#CIXyW{^K};$T??m=g6G92++F6UsSu@Iz`vs~=U`gmgCk{u&4`uE`vtcv z#ApG5fi1Zo6UW?c4P8PVD=G+ve&*?ZcrVWQHirG+2hDyw-^ku#$IYu*-@X?=sw#op zV^zGD?D;10h7)Ys1?$K(!SP{jsVXi3#*kpo%#fu%E{gq`jr zr2KA~t(Uxi&0TS#Jxe1Mly(|acVPpJ_6|*~Q1Sz16>Nm)DF~Bt3c^@0V?dZR-BS=| z4g|Zjk+vCW5UUC)Cy<%st;zT1gr1TO z59H685_8hWi}Aj3elVEQ)s^saV&emo$}jO7|DY$LN#Kf9a*}iuvajWB1*ieqvIw^8 z4P0Dg3!tL*Q3Bz{OV}?#OJT)>HjwK}ZY`vvfaBiSSmD*UD|gY~_S!0K-H4j47dUmZ z2+H1r$p5M(JJCh5&;;qE+EFy_<3c5^zi$Bb=*sgL&Ccz3s9%9Q%V#_A;o!2N|PiQF{yxDJj zwQx>Oa?N#Qi0NM@=${6vNJd-zoDA$A-HLa%z-O+0!dC`Yd?Pzr%<$Hafxk>?X3~~5 zQkgIsvmjnz)Ql+_pTL>$yt5zvt>YkC6l7*{^i?QdaWV)8$%Qdgxba7n}UZw?z*^w(ziYhh=D^A0qLf`tkO*E|vfi;OJ5! zd6o$Nm3yiNE1@y1w0Ra8sR}VAogw6cU5lK5#r2)C2x4y?Dak8qgxRdFH@fZf!G!+V zo_1g{7w{^A+JIHb+EwVoV7Pc?>uMH>Mn=n1ME267-#jg)_x-dN z`nMLB@~U!v@(E&r%2o&cXOk;dgJZ|w_Cx(=txE7sqwlf^vj&k(rK!zC_fg{r&&oG?&a z-TJCi>=@Qn9iymeY54A{)?J>boI(*Ym8j*b+Ob^;o)EpKn)EbwdDBH<5bCG2cp@l|=Mk{x} z;%FV-7)+OCAjVXimbaiYBp&y};U7viZJN#$&jS~jTAhhC$Z$e-z>pei)C+F*HHF~|g^UVpf3yIiiJa_!@G(D7w-3O|MF{{}WvuFZL58B{XC+?? z{PmzwLF5d2L(cy4kT~Q=WGg;fQW#05v znMe8{16o;Rec=MmWof2}o5w^E!i%(+{LUjOzh)!t)vD)czH;ckhdmR9|7U-VX{#Oc z%92e%N8Md3ZFt&KM+dyzHl)@)s|1qRFuL=#^Uq9^zImuaRh;TXN7)aHguhO|1*_IACgjY|m!XXev)Bbuduju1jrN)!5!F%3XK-a1UNzi2t zVnp6o*0s1ks*t5*4_5ODD1Mdq!r-+eS+9}CX!qn1B>(R(QZsx2pH zSMj*s>p>(OAiOzcvj`#sox?)tp$$cc0kO330|<^04%apdTmO4qbGE=- z1XKkeB4NIKFEMs35@D2cs&!IP0q$!d9r1%@-fG~3vP_3+o$CwX(~rmC>Qb-6M!jmrZdj4?uh^R3^7BQ9s?2I7_j|*$5!?=?S_AU3|v6axdXx0Gfk;0ce`ti>c{aH6(Zv7y_p7!46&E znd5q0LLSAUy94V-0XE2Ko^&lZSFH1Pc39kHm&Q86%b{Nez9q5{ zu?iv8nIE;o6+uM6Zks^E+b(zUisxj7jbZeM72tg0#;#FkW!)#%;D`!v@C6&U{pnn$ zydU0_Gk*O_K;u=^waBfRI(=|cmlEVv7*{RKxr$k{$C?wThrtA2$={~+;9h@-p@h|) zwCl4&JkXxEr-+jE>8(NjpKpz=D8S8dx~x869Dk)U={`TCB$J5nMMJ-3k9uf}mVL!+MFl0!EOs12e=(8zyQ;--H>SrzhgBD|i>|>3BkcV73~;aH;iS^81_IMzs%m72 zy%eP`P!KK7Re_2#^78zJaXsZ=ug&|Qdhb$UX+j)*7@8fW>8M>W;Iry={H}2G5I!S= zJh|egEnNTQFBp#*o-hdNOv){6@oW;_Xab_~r~nD%2K!(R&=>3#`+o^Jc)GdJYi%&|zPQDV zr6P?vJBwK`XD82pd3H)3a1nCQv7hM|N+K|gVTsy58$H{r8C3M8V7zwiX)rf_-?8(%3v0B8VZOLiIP_VFmOpW~sS9g)9qqnLy) z-z76w5Std$$lwv6hMwOT0WnI8UEMd)J@gT@E!Sn4s2h#MgOqk!>045Iz&0nFerhT) zmm)xb`-fi2l;k@H+D##eA9_OMQ8U0a;Gjx4r=mg1A!Fhi{}`HQo;4P8Xn`)>g>|^Yrc(zrTUz_G$}< z_{#+L+XOWQdAxuX31-2`bf@%J)9f=7oSrwC+3po(5<5-0%n~{mV$3I6hu;Rkc}s&i zZ@;|Cds;Tx>pa15w@gZv=PxN9{HL^;nfuCWqJUepZSu(TFMCN~!ddXGV^O&ctl>V~+zwe(lOJs@J#=Br)&kG_&iC!gH>^nil^e8ai!nM-RBvSUV#bVVMaqC$ti5KEc%K z`@0F^t2T{xzM*p&af{U)ununb&HG$c%hiP=trT%(*7}I4F&pqP=qGZ8`1Ny$vHA}h8l~L$7EAMr+o(C2Y1mRGfFD|$DX8DXTt)2(AfXG6djJJ} zUt1#kN1<-DHm>w_HsUn+C5z}F*n|e|!gTHQUGSy5?LGeMugnz%8|5p}RAtmE)kzXR zu4iKlQYYbR++C@du=6d_=-|1!551^c`M^f1-(~OF+9=2h2tX41K}?UgxB%I`QIIe< z3S*7B)P_t4=A1Pc+Bp}oRM3Y4xAErb7u$UO5AOZ!i`_hbez90xkfq-@cn~jS&`PQ2 z9Z`1VIaO+O2L?BO9~oUQjrgvn5JW5f0tGIkvT894bAHzbOLCxdFrVvMcH4fRy|Ibk z=M#g&o3oiE>`dyMUv(f4E?rJ8&m3Wn-tB+VpWEo_W5RedXzF?+99@{CCLuRwuVy&* zEWz>c1PEGc4hlvlPpqAKq+mcK>eNaGt96h2>Q!IJOJXI3)OlgI=BWda$qw6*&E+2# zj)2fg>?@UhvsE+w;?&z5uZ{gBm}$I`e89}nHhaBzU*^HS(9wZ=c__!+S4r;2o_kG| z^+&Mn7LsG3#fA?4XjAvYb=XqDW7~+IqUI$G4{A0d3`4aIc*Yzwy>5BTlHxD@^g7W- zwOypx9ThjCBd#%UX{7&xg2XSx_1iXPUoBL6|6T<4xrC8O*OnJ&z~@b|XWQg{(9&cYe%Z7V{O;w;hCF6cDPW0=!smu9h%@ z)al<6$skEQOd-H!IaSx@{HwY)r=Ab{br2u!tuzI9qX33I-z)p2MhPNhXkX&tcKzoa zJg_*cxr?;asUL?7DOz6D$Wr_4}3NDQeS+3O{#S^OlG$7PiGjrcD#@V1dX-leOajD+Y?V zoR@r-u|N7Y&wb(Q1p6#;(0C}X{cH*$Rq+u5BAwBlN%xeCDd&qX5i=fMmcrHUKr0We z?Zw%2M2L1RC%PYY+XXPNtW@Qm_)7NXXidi(LkXc*h$1Kq`Nm#s++r&9B7%N0aCbGr zOM!I>AFbIfGpI8ZO=Y?6!?~Im#J3ji3t6MDbD=QRJ-=e#!CH#1@tOTf8O&;IUp#xn zhtlVzH_A6I>roZ2UxIx%KbS_m#4TmqH&p7Kf+T?&!%O!rQt5-~utLX=PpmW5zaUcIB`^LvqFUA@G z(2+6N*qZ0x5yVKO#@RQSPw_c8E|3L7ctBSUk+!wFA`7+St>Hg>vFWv z_`SO;hhVUdcX&;~<708@{&`jq@HpG&)=HDwz}xMz?%i*UE1EK`Y7`;FYc>FZLrAB{ z3w@i*Vre#@2^2)s*jLB7gaumFnG!KZY#EO`h)ACT}k~Rb7w-b zi5v#E+9T<4H`wOC`;Si0%{32dsektr*8(+^0WT!^ z{tsM*+=hLTd@`jvwv98@VnGkX-gqRlV7lQF63n!2*!ho{GC8{aW)px+!1cjgBCONu z#09F;#g8=cx)$9kWdf68JNO>KXP5~Xt&OukLEwrCWZ0qR!F<>&*>9vn@-)IrEs^|> zE%KjUw;i6smwq{I)AG_7il1Gs$n;)81*34ZEsXq}7xVMMT_hXJ_12-Rw~9c10s~WW zUt2Fk*0$TSwv3|9X)GTI;a=Y%*;cRA(zUt6b$GP?>S9V!{5cAW@YgHn=$J9zgToio z(idv}d&ly>s0!~{&{Ho`#0iW)jamrCvlYBzFXUV5%Z$Vi-+Y9+w^}z=?HN*5Soe$n z`9s66p^SVj)aDC*n@<|EW$0pE_AAViHsy}uR0Oxm?4>;t7kHH95P7PIJSgE1lL|H4 zcUyK4V=)2^-R-yV?kyjiV?qP3DuriArRR*rgRU)-I}cS6e9rX|WT3*qPv6Ye{||5G zH)gb-F2g#2GA=5#B}>6BT#D)B*WO0(AseJED|ZY|OustPbZx#->mU;Asg^)-!kc`G z>?d5eEQV#fiRM>oN~nHRcpg`#DEiLMz14j2xlnYKySZ31k5MEtHE(=NkySpQYK~Zq zxP1-c4IfKgNG1Cn44CQtULJ!WYv1|@g1qp(IHbst?G))e zPn`7b(}`_ONk!>4{#_!#e}&$;)4cZ1%5&N>?^^g-sU8kgI~&Aet9JkromG_?zoIh zZnXR+klW(B4WaVv`46&)-^e4qb+VjE{74D{gGk%3uV^Gk5v^|a5vZnu%LYz+@AWTS zarX&=_Rx^*N7c(Fv_ftH??UhrH7f#QAgtk^c7)BwsoBlnn`zyokXt zAO--#z>gFZD*MmQ;@4F;ZXOdUt$o*+8N*Uj2cPDxt z;*z8T9(->h4c2~bsZdS-+T7L`E*L84Nzvgi*pQyd_E7zM!{ABJ4fqH|hUiXzo`<^x zQ4787^>GDUjo#TQ{Kq=JDiNJuzKXzl>Q(r}uFlS?r*#-! zAin*{mUhX&L-ssPW+1qI0yIBM)c(EdUy^l(@jq z{C(#>;y|W71Y9afdnjfA$c_33V!8S2k=*cKE|&k-*@b~CD!}4ui`RxqQsQE9`{9i& zI+-2`)OGL($Qt^Bp{FB4 zsEGiZu~ypzJTK0QnF3&NQ-x6bT;ilb=QEyThWBO00Xq|g1%wp{?ek=Cx-S>A^7#Q( zB473Kev=)p3hHg@EYM~3>@4)cG7}dQ*b_t#><)L+oI9qlsd?L~qaf+F4EhdHR88uN z%-X<1M%ngihcma)w>G8?F_p^rom$LH$sFxh>)0inn1aAOWgXV1wMgzC)6xI7OE5c0 zPf0h4<2~ZEB{1SFjrI_>To5BpZnb#N;#YV|r@I)6OO#4X8Jl9bmi|@ETUmKplbk zKH%pqIMTrsGf*eI8UKjNZap_FsjDCifY{Ft9h^28B&kKsclDeTMqVomyyAhAV!Fym zLCh)13K9iHMH5CkaDV=+yQD=haCSsWVk7Q&6L6v$wwZbA5KI{LiQ0oQ^3A-my}3&5 z1?9tEEIG$$NYAmdjB>kx0v1@E=O(9LwOb`=j``)>)mI}G-*`L&m-r@-D4ZhZ^5N84 zt+_w`w6qLZ@2>Vlv*TmG0e#44Q8&;0i+3zeJ$PUm>wDk@$6Gj7QNGvyn+L^=v<%>* z?)-JAWB$xvKRq|RK}#3z^m^u{E`i(N=nG9tf7!qgL+Cw`tYC0m`H3Gn{z1xwNx(;= zGy8L@-aD(l7*`-Tr`}3}+9EHiWGDIi^RSgmr^>W)5wpcftyzajx)^v**hLj z5&4OT?@v9?IJ+0W?+D*rjI}T=0V$L`=#vQA3)07oy(u%%xNnHep@5T7!G8D^^QL0% zZ5Q@+^iv$fpLbRBtWW!WE2x>l#jojA)u2_kkm?3qe@K4hg8I6$pArr~@%fc{7OY%r zOy3877)-H%%JITqqC?g$a}&}R5hR&@um+54 zWyDPK!`?X8hGm&LD#k!|{ur=K*nqg~4`AOw>tB`%0Ez7x+%U2^0a+840Ozg%%G#0R z^;cfk#d2+jF|G1yt7U&V*txP}Zsz5R_%At?&3wAA~QJgJ_Y#gyuvKz`!m4_a+NFdWA7H ziKC~O1fH?6&FW#}P>gc(DMq|tRn&NiFhY&5;U;|xZ2%z+3~*GLceeU91?*;IFdujkNVx|r=1M4`PX2S(XF`0OoAakTE;?DM?F_5mA@3=JR_?PtpJsh=7isyZ=P!Ovw=n*)7h(kIz9kc zsD4ob!{&&RK@IwHUT_5SaP5$RJIF}1f&oSiMX*^=(j_9$PHWHCOZLTCv^sQy!hiX0H=iP9rqUU7D?#soR}+=PSKm0LG#6-q6#P25=lCOvBw6 zTptRDm@)m;$U z+Z8PMf}Ga2;e02Re;W(d7B09ub&O}-r*rw8jcCITRnbn%;?|BXvA*T`q+C`s!vg?9 zZyKv>avInJz2iCu3%X|O{fL2ktW|mJ$aG-bI&;)oz2KNRJMa@$I`>zczP%00N~af` zxA6Kx?7UFUz$qSF@COfOJ}6`k;R6e`lL>rCYQBa<=jx1`D{`ZQ$b{0h^30mAPsiv|_K_~oF$!FXD3P4(~~mUs7rzEfFQ7hqt(-=W49j#^TC75 z!e;w|fSBZ|S|ZrxFqk3}ScNAJsJyQ4y_NQ+U_n*iq8`J2ETfairpt}q{rW-*Ag0u5 z9*M`9VXLaS>LnYA368>()zMr#`dg5VA)8YPuFZLi2EDaG1p@nw*nFf^9+)LBE_Jbg zpF;dYk}dX3@5?Zp`dL0P=1glnYa1$BlZ2mzs1>(TP}-{a1$?w!%szHz&jW|ligY&- zl+eQKXCsWO&g?z6zm@Ji$M*Vd#7NpJ1f5&f2>nS+B~8MOv-7L-J?6*}!!cCq;qA72 zkXs09{DfZX8LpFez=bWIHU!PO+pytBqFVIqy-r&?jR;714YTX9tUQLSt z1RW+xmkxw_awh0N-e&zEwvx(AS;bkUt_$vVoI_Y}&ZCSt;|*Q3y*^br{Fw3ih~1bS zUA)mHI(91Owfy#Mu^u@73I3_)wG(ZzK(|;}sE5vC){Feeol9_@s9Gn@ReIyV>CjXx z#I?V*G++7E$l{L{V6M~gNr0%g+qL^V?A)9cPW{B*R78P2S^m=M349M)&Cyfw$>an_ zA6CP>T{WDq=h-b#SOaDHN$YwkCTgl!&#skB+1f*oZ9dW6`fHV(W=Qd>-Ydr^NuIz? zRiFyFH!LWmffcv(6zdq($-p>-D3$0IYZY&4hzEBzws`2LCA# z6}xn7%SCo-PKg^rD!mz>L@9qR%OHhIPmVViOQYu!?48K@+wRpDa$w%6Lg%w) zr2HdqHvg{)_qxFjj*1wS_+f63f$z~9>gH{eVqRqSXuqWC&tl&oC5d-BVd81SAU~8s zb(8AZ;)9Yn8-eN{EQYh#>!x6AiIA8GGwtdz{HF8SX*63slR3VdA3&Iy6QfTqlpJmI zF7IIW!%dVni$x+%>+j#xWb zJD?WnM~t|duBC?5iV776kVk|LzNM9zSZ<>^5~J+Sh(iFSH-&!FhCAx`_LrFT7tUS6 zupqyVq){2}Z(mq|B5CN2I4r}$DrzV8635vabN|$PfBii_D7gPBhK>%996W0O4@q>Y*ObpCQPZl4t^4K5c`wL7q8Zk%NE}Z2k+vjY_{wV@oA)V#SbwVC zA<$ssQEef?zOv{Ext^g`m;R~4Fx+KPqL z(5nAI8=LnSGBrg<$39e8%INewi966NKl$zf;e3fa2)NCwu0UnNm)YYb$1F=!?${Og z^Y4v6u>xJ1!%pVww)rm5bgt+rj?d%f+C`w=Q8Gyr9w?m(DSfo&@aMT=)4DA-Zo55rgQG8;;`~ z5t|F0yw&Ug+W~fhQBAJe`p>qTuYm3kUOuP`1C@*GA#k<9<`XT-e|ott5e_El~XTn7DB&!$AnU-XSv}5fwT%w6btv0%ebOQi&m*$9lUiDU;OYcO5N<- z@=@#?enBjDcb!i714A7HZS7u$b2&qDkztm<0`<KE$G z4*Sak%cml|$A3h4uaC5W_5nA8DWkd^zcVjCah$&432lU@;IG*`-6w5vJNe zC3v;zGL(pe6OD}hg4zFo&5B*VP~&Us!^8%dPoT5|h1~fL4@08UP1|X=UjDY}mkZuM zTMwUv$F%Df&5_ozrax3vBa6Q(^mY`;X`BvbGoIuVpHt*&qttnm1)-2dTqJ+gFu-mk zfgV#x(W-K^LU}9umJYOrW zKh=C=`Un5BCbU8>Uf4RGf)a8KH*5CEv}*{L?a#^kIJhJ;l!^3(HE06T(&tb@pSyZU z%G=mq3Y{~4hWTh-X#b+b8Y`EmlaMS^bexrT$bZ!QR%dO(vjb&lhJ8c~?-B|1n!+FU zI_O;P;H}lXloDtf&7j-C`)VXv%HUk`bdJ;w>BuJ?9Y=4}YEV8!FD-5eS=Jq*H>7qY zpGp=nZVe$U1|=evX8Tj>-B;J8H7U7_0e=W$i^@-^Oj?+I78)tuV(nxmZF}%unw!W7 zAIVH&sW(9Ey`QC_Fbqi-C72m2bz=kqL3f3a!}Ma4q+^8~Gc5fKzlT`!#M5qwQR^yrQvX9P3Z7J;xP3-?-cb*t zMBO-IrY?bcuKX{`-H_&OlaojdMkdk5Kn;)DA>k+&*uiahJ@VVybI8 z?ub!K1NVQabhfpI)I?47lGT5tldX8+ z#L(+#pmN@)55Mt-K^p77XrKY4V3=@*mRN0@%4_P8*>{r0os-(MC|J+#v2E`83{oGk zu{W8zZ#br2cC^Bs!-;}{v}x*6v#R2NlN$o0@Xy{6p(0YH!;ac@1199I@11@O?r(G8 zO5#f4o@)*mD6IB*M^_ws2yIW2Wv=+#S;br z#zQ?Y5~wQTHtx81gg)=EBa6Be%BT4_%2Wj1BqpPR=#gl;$d;NKzWEdz0kSlfL{gwP z(G1iLH$Ul3g>W|>KDo-Zd996>0{%KL!E4P*4{4Nd$3r?RP2Uk-#Y?MDOF!3^*u3}Q zZ#A~^Kh)ShyZ3o`HWlbd{zo-7nJ666?1Q3kyp_He|Iy7-H;QFB7?6|;E*J&Vyky!S zaT4);V!l71qN+;2+o zlS9-3aJ_T_#e2113=_X~YC(c==x4j2hsY;sm()t{zw)1C^0^2Djmod5Nzib+*Ox5k zC=3>>rfbZ=`hcN>pE8ENte;*SHJf`vRbp86jJwy`3{q896loOjA8%u2FF~xbBs2?@ zknnAKF6!jpAR0cVDb#crT1Wx&CCy3=)irgHPT-FBhafLH@ezXD5hQ)_3g>4t&v#ks zh+r?1YZT3QcE)GTn;^KE>zz&oeK}g}OlH?8;65(*PilV&P&kBND#yF@#(IAXXKIE07bN8d@7W1 zeyUCJHibqrquY!9ADMD;h+VP1JpHM0V903;8KxkChpgmkq~2u<=(yNUT;>9q8h+I% z*DSow&sH(Hr_T&kVY~E(Rn4i3V!uu4Z8{5KM3%ZtAmUEYeGC6hre^|=fN1>Aoxern z`Dg^6Q5bG0a)asX<8cm(!GeB|$hndS?IDu<7#KuV_)k~W5(`Hs+}HNwGeilcW7Cg=mg>wTPAezevInfs{gm8spIPcFv@=jbX>yYkakY3d4|zFi zY}%H~i~H9rCrp!E2bJb%C`pxBqg$0oT;6-V+_BgI`-oi0#<+Sn-mT`zy~s0*{0;Q7 z;azvVp*Hetq}hUF!2sOcv(T+mU`r(`0KwRgWcVn1CSu2qR5}&0cg3bIfz3Gx68hEZ zHSuO*;yNk%Dox!ah2G78YnrT5i9ZOz6?Sisi8 zWIox_*W0?o+;qK(b}9I=Ux0ekMCNK20AW`2tor?x(11zV3_nw{o01!6461@{Jqhe# zMnI5v#>Q|0fQ zLItKoQdmBBcooJUq}iLU`Akjb4jBqAjSg7;oRYpn$r2l=sCkkyuv|7&VvFT_woJPRWPic2#a0W^u1W${17XZUo z|BY*Zw_Fc!u%A}jte|IDtvj$j*ho6j6TF9oMm@OZ`Yq}nln_fAtb54wIyvS%(lKkT zW2UEohvs?=p0Ctbpf5Tv3uN4SyupejdL*dy8Mc+X1IYEYk`XyX zu}Ql~ApQ))@>J`@b^lQfWsdK*TAD~Utv3O7k(X@2Q}rB*bC~v@YI=YnU=bR#vUPzp zDA9I{5tqBK`RK3Wy&~KpXTu>jnXUoAib%8`ntYM5xPEp;M;$oV#+=vhNLQJN9>W;BEL<@+b7ye`Ibg70V`ELkfW?1H<@4pI=d?@MsN!%r4+n zAc21#@J#XSJZ05gSb`YR0pgO{NbGkZ_+bu9Cy?flx?zpoxG>20V+)q9NV2%jy<^M| zuX5ZdGvlo82sKZS=kumu>)D^LIpdAwD#n|xx9{tbEm@_<@ z{b5?#qea#>k`{>GdF)7#N#k2(6alu)V4{gN(f9t6`b%5UENb@L&{aDJZ0!<-1TRgA;i--`sr*uyAf1B14Tk;vvbdOK-NDJ$i_|D z!t4&n+m*y>DGta}Kwdz|SF+O^{ z*jLP7r+}B&2t^4b`O2R&P`e_dqM2G%X@L zb*|)8T#(jqZaTp2c92>}tx8-I?Q1=*P(_mOG;}tQr9&C3qkh}gh|pC6?E2X5wSWWM zz<#5^*aYXqNi~TRJzZp@*49E=;ss8&KQJ?pC4w`e+rI0+!Dv` zj3#&+#vCDJ1qwHw8iKVNTdE&NZ0e8nJeaphj9Un}YNlkVk1-^c^(0f)G^f-;5CQFU z3BcHi%E2CojJ>R^#bQ(*N)59-dz;0YcU&!qJC7v1wi;L&st-OCAFxzKr;-@`aW4o9 z?+wT%$6^Rp5AVOo77_anN<%^abDoEj0hr44R@|e5vu+q?CfnIp<`Uz}ehchfQ}L3j zQe&{uvkzfMo7%x%=;D3hJl=iNX@8GJfi5P;B^5!k#SSDf2ZRK~N<}^l-Q(5PlV7V} zJ77-G-b8f@@QVIYOezRnw?Af%?x!#a3Fd311cF!!Zy*QQxC~W6ETzX5dOfoG==@Ew z1mqDO`UYVr9TmjWfHd5cu)Uq7dCm9J_%orrK$Ft>Q1CSi(;3<*cltcTnc&)5sO{ zl{1HhQl%faOgOo01mq)!RuRRuWzlCO0LTRaPP%`R6L^E zzG?}vPIG6AZ%&jS_5g(Icx^f8uphdIR3+jfSpb>B|DlTH?!Hq|-*nQ31 z2gnq`@!K*1yUBM}i4&I~Fx&T#;iSFq7ve+)!W>MIRFC1zYZ^MP&q_=a*Jo#4-?`b- ziNr*y5)6H*J3u733*amQWMljV<%v|^}rbq&k1nM%b z7;EtHYx6|4NxVed*-aiH(~hbA>7({zpthPyHBCJYhZXa1CD`!PO}==ek2J~)x)-F< zf7@6r2#zvSd`zA=$&BuJNKb(X`#q@KJsnh>bhdxq^-cWOls+vlI1nqOr)Y_`L!G84 zB=vNF=fd1{rsPQmaxFE007{n{s_Te5MJiLn$RCf=4y-KD5XLkGR4lIaZx!Dp6n32r z%V#6AJf>x~DSbnV)34i80*s-??AJve99}g-OR3089ux*Ql)tpjCX_|+qvfM=I7`*T zu2FmeD;0I-Ves>s=20>&z23eAtkJY|1COQSotJImtpF=)xy6~s9t^Ztp!*Dr{=do= z#;~f=Cvb>}&}sKG{f(E9PnnJeL44zyId+uWz5E||7&GGly~s=u)8Zm4|B;r(4!%^- zb!KC}&L=BZdpjQEpLRnx&$=PU|E>I@+}}a@4*6o6pBs0th*WPC2gg$p4!0N~xFYXR zp`ipu2cWyDhd7ubhGNWVKm+kwGp~~CeVg_x5EQ#i^QR5_U;p9%U+F%8M2N&&tKv*a z6UmFYrr!VOhbiarnV9BdEanVN zxql?HKpT!VF-oe_A2TX!um<S@R)P} zF|6emj?OA4{>RSx1}(Zjk^By49|?RgGo#;sF8!}RcNP9CBm4L-=@jN4lEuAG$1>fY z1+iiy%O9CK!D63f`V;O0VEqD_O<-+A$+P!77LyZsDl{cF_5AKXrAh#yJ_FY3#C7s0 z5G0}kIR92SaRi;hqM4c&sdA^5Drgjo4PsI4kLKf40CXMQ-+!%vr!V^r1JSmnOFJG(SgwKsXvu{}ANG+9*!ivMn3|)ff zQ3u!_vpi&YRQ$C%&ZK@dJ2L2V~zzz)y&rxy>-zrt-)t4ny{_{;GvBO5aqldqJ zdb5IA+^gjRigPU}&O0oa^!b1L!jX!7sXGmC*`VFM)28V z{AWQ2M(->1t50pBb>sK|=5erZcRYOsCI#I423(wuARu#l6b7`wARkPeY%G{{ze)&~ z&-v@K!SqmPy!$4B8<2P`oHn_7T2WZo2wFydNGx@s9p7tKEVvx3*NPyMC5gZb=R5Tb z`1#v^tnz;2FOgelAVg054f&>q_xs^Sr)^KoUIv9fh^{-{QuHpLjR1t^kG}Yetepkb z2w*^U?(hrPEm^C>=#(##?a$T+5?VY`V5JSc;jr1;N|6&&o$){=5mwYs?Q`0cVD1wk zMjx2S`u?w~cIJusFN-MeFb`agS|cdxn&n2Pawx^U&{9(qVBiGCqqSO?OrTq|s-mF0 zK8@ei@moR6R->eW>tCJ46Nb^s?wQ>$HU=a(gKi%zn*gQT#i)R{7D)Nfa_*sb+S~9! zx1H@DUu=hORa+jJWT*Iklrb+u4UP#0gb|GqN^z@~?|&<5?kK21Tc(A-R+~z2ezAW$o&!nD6s~(UiUpS)d7FK1oQ)a1 zxn@&7@URsmurH4E4fR)UkN&c6kRz!tKqoL8I@A57s^qpfEm-z`f8Fux=x-{TM}I&2 zn9J&cbvk|XmFV+63n?tV{pS^a(oAZ0McmA(lYhed!|seH7D_eCs04kt?){f>dx|7V zA6yXsj#oY%@JSXSmcSRbk?i}TlTIUlJ10ZSsT3PtmpzSws!|KMD))*s}NUB8oYY06NO65zm-+_?=5Uost8AWeId8 z1^S&pd|o|E#5LGL1I3AW+vwW~Y$HR-DT9}@duI6ILhq05CL^yAYG0mZW65}%qa?Eu z%i9eWH?%BuFi=O4=YyPEJ-(B}%)-Ycw0BuhTn&Xm0obc(+E%t& z(}&S_f)$0A6}q2AyBfjzR60S<%=yqv#=qK~ezD)2RZdy5`#+t~tbd>E;Ptxr`w=NE zq5~Gt2_9dXORV=wr|TNod@sPjK?KyN1h?x?tOZ`6MwGc?kG2Zy-goBY?(+&~s?l>T z(z<0)cmh-Pa!zgy?p4|M3w9^FffRQk8bEpqaNgf;170Z?D#0$x%I_Uj4j}d4x;j7d z^Gd^oqv9Y1Auroj5OY}=%ez){{K!*vu{`TWf75&A<}>X8rV|66ltk8yzbeE3)b9)z zFy5gwCP-DZO_0rexv+kGU?Ao?M^z#?x!DdD+fe;zw;kz_@H$dbOS0bH>@&zqS!*Td z1^aS_a&>^kwHTMbzqt~l{Rfu)ufo9Is%SwjOWkGjb#ys|y<@_?J5>JnF?&R678c8A zcC;TmeF-7dOFvdwLJ{s$o$1feLk6E`q&J8k6^xc>tq;}zU7~Cf7+kkP{364c)n4YN(|;9s^R!Y)CcYH z`CS~`{wfZqqInVbO~L8Olkzq2_#8XzeBBPLoG<55pnE1!v-vt-&suA*y|+bN4^D76=clJ8a*4*#08}kbm{OV7b>4q$}P+8pFHy zR5RvuDy)=Qfv>0Db6;Ka`)q)v{Ejb|K0_;4sFNSDb!kfkcayIBv3;U5_%(Y~OXGdy zZhELzb!ALr83FEJ>+6R2@^820p4av`VRyjBj);1FydKxNSx?%S^;xqYDY_Ime#vP#zDxurd;Nc z?Sk})UK^>UH|U6dFI-@F@%hBCN6Nz%!BH!|OG~ZGZ%eHOu+(05svdi>VzQnI8d7VV zS6BbJsCFqYFWNrrk>8;|N;;aWXDp*Zx8ZaUG{kq(TucwKm<3$`SA@<%o3xg}a`vdK z(XaJ22RbK*o1PUz$!>#qnR>Z@BBrqM^$9d={fP5xIj$Y`skWDalz8*r5q24k#@BJT z=uKZksbJlQ@+I?14Pa6XH^ZWiqq?Sj!`Y9l!1gVd*azi1VwmcyZ&iRD_QkddXzuLJ zngx~8Tpu&h&MC>VP?icUl)>cJlLZw@J#}-zk6q~Ze&g&CU*RhwV2Pobz0zMB&07O} zHJ|_k@87*N-hGlNIOTD?>1jFXb%S%ee8S?mPl!uem!sT7;bdjX$wJOwmqV4f^!SQoUbOUz8HC37~4@PydUi{pj;G7@PP4X%o{e(2}x{ci0uH05G|ySe2FvWVq}15%nFU6%6r^4~~{jaS|t(J`MdsA|n} z-w9$GtgmAl$&NU=xW!>+++9cxoOo&>9by6Fp);Nph+zImjNq=Erp<(9m926QSFS2+ zeJ%%w{d zUWq22$Oy`0o|E+}U)nsJXA!yD5kWoI`2Mm!>Ut$KseS9@U}hpafW!dxeBE09Ybvg+ z6P|}ZaDsM1@U^7)g|S57MA_AkypeMEIvHwqg;ob!viOhR70W;gA0aTMY1 zbTE}adbV?RJVvfjkq=sKSdU+vo5&_Rr}xxNzK}eAfRMag)Q#>vH;+N8RfpTkxd|p^ zeCFFM-2{aVL-QiJB3MU9bq8znXZs~Hz(dfdpAoiplATg+dv?NyKS*9s-KoJ;=r#XL zF7q}T0X&#(d?e?x#+qo?ac{k1dwvyx9?UAOultU7vbLNt_VmOKfd#UfEPO zqLJ8)u{)nY|A>QA7mQJO?dIux^qqJ6U60VYzOt@s509o=%J<{_GM5qcw#Cabla;yy zqCj?;D)EDU5>)23%HBB6p2Z1gXrj}48EJ^Es@5#x2VGBx5ewI*+vU;$7_HmCvsV4D zLG%WS+Lza=YEb;&a>OO~-@oSmLI2!^W2eW>gs+xaH+H&`WD$_M?j-T@9H7d-`PPn1 zMZ0{V1myI&Z=d|5-?2t5*0^qbpe3^5r);A|A~W@@ULsqP6hS_+iPg4PDd{V3iQvt_ z7>uy4)ld263o1$9Rj0qbZy~g})odA`EMQp#kMW+*v(>s`Y^jZW(>MGXD5>Fj_{wx3 z$HDyMOC$y7riOXdJT`*h>g1RQNTR6_f(|+Yrxu;c3OwEjMUA8<6V|;etVW|OBg~YT zBk`xp8GGULYWa`XV~m)qKF2IijwR{JWs0O87rMKi%yW!zr_tG9F7=fxCOCjR?68#U z#)>b>l5qn4IZJ*4XeM1>w?d=oFv>z}@h;w)r`|R|4x}UWb-1nK;7?z_06cslfUNi# znfq6~QHzpvE>u_hIi?Ekw9VK)ArP9np_aUkrRB8AYW5AB4Se!czbEtfBV38T1HIUP zEk2ev4fR#%?;81in_o}eI{JzK*!EdBhq6T`w|xD|#E6Q^w;PFeLH8ypzQfqL>9M#M zH5|vPH-3o6@Ale#I^DXcq_oNY{3T4HO-U}3UW(nr4U0+Zei7)C`L3HXX>0_{jYSA| z8CGk4r+s2GqMd}}Z!3YwfN=YHF{6;4SDB8jM69S~kq^dm8!a0$)>d5Ws+L7Hn@_hW z!*JZ?`3HF7=5m{k0&z~QdRy?FM2Ae;NT44Dx5a&4vSbPq*X{O}|0Fcw&Q?x1df57e zQVp~u)8e}wE7Z*z5XSR^uFY)-)O5JM*YZ*Ss?9dzPmBkX>{-J5BK?a=-o?IVHFwf- z*HQ`Dx}6sjxd+w&ReezRbq!`mv|sh@6_8)792cUDFP*a04n`xEYiqGO55WlF5>0E4 zlQtnDzUrehk7N6irDV36MWT}IBux?URn z@t#;1?(-ILO`a&@R^|q7rjE@U!)}3c`?Kd|*V`_=o znn-_mi`o{I9#S-74*H+W0|Mu9w$sIXgJvW1$bFDH_P%9!3tkDXb#0Ni^xk)~?4ge` z%@+y;?CtN|PCWN};cJ8MyU#PHIojn{O$uXN^r={S9JG-z3$$9 zmDEcam>TGXh=^O{QwmKOd%wZ;9S}NNg~R>$}czW?NkzFRMyrh z*}$)_aYw$f>k{MC+VvLsCGnBYzH@h#LUY*?ky2Y8Mt`rt@#1J>s?l}xM)!ae>(WNe z4b|i(l9Ql{rftT-wHeG%BE(QK^nO3jj(czt`DNzbEg__L6a)~(g~Zyrr1$YN~2LZ9zr`wh!~5zJ|5ry1Wnmk){zo@H7-)?d#B%O z04W~O?O5~}yMvnfnIY_P6|H!|=zDEpVjuj`dvsgwU_0wItRF3faeq20RGj=UnS~x1 z>7~6{pD2_0LBJvoN2U8&x|p*IuGyNySRP}WH7V}^WzX!_r@57LDA6gj3AhvXA?yp% ztk4NmX2Y7OWgUG@q>+3M%COiY;}$MFB<9)Pxmi&`m}>6XT=M&-B;5QNa-y*7D^>`h z_Ob!cc7Q{9-svG#e+ey%B~gEE71dzv4hoB>;8wPfQLX)^&bMAZZw0{{IxlZ?6Ny&9 zNxHZ>h%CtkU^GTj#J4b_aJZk`QPW5gC9@$puXB7`$-W_| zYe+k|b$@tsd&nXL#TNw{){}A`yGW03a2Ax#C}cH4r!30cIkmy; z5Oq$$EjXecm|0Wkn8Czj&8mlDf-#S&r2HlD{kb)rCM-wG3~wjY?csG+*Bg}1jyW=F zykUrzk{AF&!In)EyuBT`vH%B&+;*y3ZqGs zzkao3D4dQbsp&wISwK2Mb9B|j;*9mw4`0QMI5zwD%CS5)%hQBHWNMZD9YZfZAqp?` z_NIA`70ZLBXC@aXK-2Nf0+RXJ16R6jyV2nF3t@?(ak zswv*Y;PskAVeHKT0f>YQHWrCOGsryO`?#t2*?C{7IBcK|^XPZnd2@0yGk=F^bhBE} zUCt_z1{F^nAyu*6*Jy&<5ILb}s^P4sP@nQ7tDw#pbRA2>;?C#bn)DjS`FVo5;^JqVX3@8}I-MyFctrAv#xFJsZAj0TXUHd( zdf4W3Y!aU<_6!rZ*TS`*qc0|1e0Q37~LD}e^v&a z6{X(W2z!F!V|#jfb3pn!uJGe*U1ly?*?OfVg72bzkcjfSiFhG5kS2vm+lO{xfw}FX zr-1F(XATBWlt#Tp^=*P9g+R~_*Fe7sY>|6pQT|A$Yr8Nz*u~)OdebPX$e*iA$in<< zeUCEJOm|K;!&yMd-+~4TBBlVsn+NONKIv#numqH!KfVXzTSU)cAyR~=q+{&1O}Z9P zy(}b?z%L^|xAYJ1z!%uMe2 zce_ENI3vBo^IGal!3?mXf!h+>OC7YNv%cmQgs(Xe@%`1InqRWvyJC1KHrnTw*=n)h zss*gd4urfS^y6aqX{%)s3r&q{S=DvN)vHh6?S+4N;Mi*SlScr*Mm6@24hUht3NhjT z6JjFz_6Jo8T0xPM_*qAEbHI)x4Y*tA(k)(YiCB=a-FgO^axT=QiP1Q;qT#&C+lKK- z*Dex$A#HYJ^^^_sIEx(p5RGj_m#+9tPd+x`U=rG-yIq~vCm0fpukA}(P)~%1Gv^S~ z&svfLmGT|@qGZV%Mt)lc~zq$58|f9XJVZjr=+YVNuxTJ=RZmT#~h1y{=mnLzMsSPUpQE8byiK$emru z;*@Vkf!Ph_sf*uSRkY>m5IgmTPx3j;sKN@9X0uQmCjH{g`hr>*-;YU0f(S(#qCd)P zhfZ^khiR+d^Uy}`=!M>zTB_Alu0+B^Bd>V28^r!``9#5tprX38!)0r88SUk!K!6{E zAuH(ZVq0j_4Y8%Ou>Y~(>s>SJ-ws>ePs93r>TN&coM*Ds>{5t{Lcj=G8&%rn*WUTo z_E@SJ62N!*gCW)3)ufK@tC}iGn+Szp`fbFA;X*3)#EX?xXO4P9Inw#W41@jO=Zqx- za1mL9?d`F)UEF(!0=hbA`ONjSWweN^W7;`Y$S6@{04_x46gpd|^lT4(6{d+53T611 z^P6T1-?(#iPuu)&__2rxI3NatAdwO89ThL%2m53cqRlX|*voMQUCx!EQ*QMwPd5+1 zAUJsoJx&%F+D3z2H= zZdl7ff7atFJTt}ySfz9(kDw>B>DTLv_#EphlF(ob?+XhujL79?ITI+f-M%oFAVEO| zJbu1<*1LuYEllzNQJWGBL9?^5mWXkd025sFM@!lIx(Teh1X^=p?3Ux)b5h%LQR5Jz z<-2J(zO#K6rllVX!hLN^%IFjNnVLjJ%$sjXWu`fcN-5d5v%4nYn!U&|Syhgl#qN7FPzy?vZQ+o4EOHtCZXTPP` zR4-&_7rFyGx_>cj&i+5ywY#F%n3~&Bu9h+kmm!Y1)!i#ad(d@JE-mAg6{i1ox{seh z5SYFArD(R7PNRSJ6O6$=jwLgWFIz^15`A~LUuo_E1b(B6_BCyrjx2@m14<{J7oTs# zSC~apQL>!kFR_}vrh269QAH))fAl0na^Vs224nWiqL|Q|t;c)vI(JbxA=PRYm8|7G zJOe$ilM0L6Yp9Y3j+$6Zzjqp~^#m)cYJXpVD<%;&eOag}W33uRna{|L2Df6`(#K+6WE!i8e1WkR}l#Ej1Rzp4&pg z3_%%9L089%0Lkl7t|WG=0GZMnY7lq~(CmsTQT@>Ue-s<|o?VR1^6h;MH20&tJtY9K z&h=_CCp@?KN|);4o_()iuXr^UBN5QHN$KvhCas5P?u!zcW7R|Pe7m^SQwrq`YLw_x ztY}iLr`#KJ`LfvnM(oBEX<}Q#OVh`YzfxXqma5dlkm6Bc2c;c=JIeTwz-6+$ZUZ&JkU{ zaJ+E8d*Aaffwv9#Pl-L@sTc$}-^Wr#`O7Q82JJ^rYn^nJ45313kH^Y>^=~uGoH2dxk zxID3VfWvD|_ejxDPX}j_$2Ca#RQU$qCLF*im8NuFMsK72!EA$&UXmJ47&HJw zxG!0Lnb)bZn%_!W`!^$@R206Xp(i|VQc0_kEbBcsaD&)AzC3mPLoF`<5g7&0x18jx z{%6zS#Q4R)|yMQis(~iN7{xkh2O0!S_I&cy#b5i zXuJt;-hd?-JPMHd4Js*KV9GWUsCFLhm7$e*nZ*r)>gTi9_))Fv`)QH(o%HfTBtlcH z_SItZKfQ0)$;la?P&Wen1x0BC_0uSx1U5jxPu}XgXxD^ff```sjAo# zg`BgXPgVEE`UJews;Gi(3cy4gp(Pi}~LG8*$;XQ`4 z+8f0@hoRDW1}$Ew%%R*)9w+E}fxs^exla(h4uUs!nv301gMG>Z@HBH<2tlan{0lA1 zc|2-y61sx%ROHg5{nFtj7^?M`QRYbKb`%%3G$PzLr&f`P*Ftv9#|E)8m*>*ji!I`t zV<}ahyl&=JIYGm3BN4lOyRkc7o_OP>Jqc?qfgc1#2BM&LF>X$}5u?98-g|0YKeiI$ zP{j#`EBX3CZBcEOVDn#@G>2z}2bN|7*n&lmN;!tETE<=CM&TO(S_8r!`7aQ7y8dq_ z-uIf?9hE}q6u00~kdB90^=r~EVbfZ;ORDGIkZ|k+`SyoSM=P|nZUjm9Ax_nf4kTO| zk~bVyjk{%K90k+^SXVQd4LLnJ+)Jl=*A^x*flQ$t# z4^i>Bo6r#<=b=q#h_u1P5Pr4VVW@VnBAP51wj%$YXTQ%e-a6ov&i?I#q$r94{ZZ~< zE|}1&)7mn(NABHL2D-2PVdD6qc}^Yogd02v+sp|cmJ*VRDQ9uZadVZMkxDv3AkPWz zhtJ*FJ+hcs46RTgm#TfjUE)?v@n>a7#BS=4b)J#v^kYoh%LpHN%Q18o~_9>TYv)$|*Iy{Eo|gN3Vg2Zfsx)=GQTtNnU| z)DV-0PjLTQAaAJTHL8G?^Z%%%!-|wvhNvjnUHVJFAMew9-@d0!_F*GDE=9f)*Y22| zW~sBRzuO%18RD7_gcrI91w=JTJrsBf5lLQ|;w z!+*d(D#81M6^z@#$VifYSHYWmlpslH=@h0hmMzQo3 znu4|}C4atkM6v77{Yo8mkMb|W0+!;{t`f&#AD}ed-Lor@&fHGS@?0OlE?N4b5cSZC z#IgQ}Ml=9p_VYVgrR`U!O_b;oiO2J88EQdy2>CGl3eTrqi(FTpu*dV^zf0smy_j4q zl1UOI4dAqvtioeb1l)^S$KAo-5hGtWVxf(!c^`Bf-s^-pQ|26Ogk>fRL@CzSnnf6N zj%!-6ddmM8LQN!li44Rhta~}n#P&3;M8ZI8Pfy3#pLMQ9M&s<6@=3w)U9lO4c^dIQ z#vLo_wr)NeDu^IDJ5GCEVU?91{F_mDA(L2N;|-o^m1pyR86dE^^RF?Os0a?ztN)JD zVqP5;pOZn`D@18BKD-mpaawqRVF@kFi5sGz6d55b?2*yf741QvCQFRRUJ);egR2T0_1 zjhYamFS2G!&FGXeB-Jv<>MRIt7Lf0Qu|}Vh>vTQL5R!L5w{CIDVI!VWGn`@j&2Ry7 zcVD84nQ*{w4R@9@Pu#es~0g?F*t1eRkLyM1b)&`P@Y$NjJF3sGnW%8XLj5(&Ii zk_1Y0X%=Hzq9aCswmqdH7`$>RFVn}BDKh`}`0mXcS4Ule{ogInIx`JqZK@}0C!3yZ zRmR2UVQI|-;fzK*hV#<4vG>;}h`lqPHv2CY5NcRRByvixeeI|;p1g^X#A5FHQ}`?h zM=AaJvr~_ickg$<`Os*^*N}|W5t~;#z_(4<4^YlqrAdo@3T#s7sCH-`vefo}V7@aK zLZdYi?6|I2Z3|_G36kEv6zqx8mh)>0m*ykb%H}tO#`u)&HmW?;S8xPN5U6j@<})`j3QMH7XmcmLG0U`OxhH`1CGtw^&8u*jmS{_ z3c6RJm#*m=QJ($p2CPDMiqTf+<|)ay*0pdwcfrvJbuj=M^oKrvs1^t91MT2oe64RX zne^(}7(WD#O>a4c2^8mfyX=9x!ANUxT=hFD2~D7R zt&-*{_TRqsPeeZ04_})MB1Of!>$0Y{cCkVWr0R!ITB9$&`kWC@}f7<(Gl@Qf>STw>N(qV+5NK9y3du*~T?CG)LsBq9R|95i;>)-wLqH6SEBEPBgr5U~bS}9rZq0vD21HOM2)Tv?I zS$#ML+Q!*-vM2N5KjU)>A6rT86KoDv$y3}6qE$?CfqIahxKwTjo+4c^E{m{ykGhvyP|-?}2zwX6BZfh4#5b##l*Iy?Ja$jq>6`G;}GG zkfky330k5GkVSALEkV*J_37EMWZZ}Gyu>5dzwTS^>Fe9it~M{H^)DY6rH$5gTE_R) z-yfnoWgo+G5*dZlGX_-yqZ&tKQeI!l663T}brU0G4o5m3!qt<5wq)Rs zse*RG?C*%0nW>FrWA3}{+)iLr^D6c{3Sk>nM5d3X(Vvf`Fl}{X=}Cm{g>lU__6eEf zw82Q{C>EuhAmvNeIZtV>XveL`fW2N(4jDK%GW<;k_ZGZ0d z?BjpoMiK5;6!P*@#Q*Q{-J74TqY&@!C=@JC)L@3E<$9Sv`dH(L*!=3PD5p6TB6--oIQg(`WS!kAFtWNBJf>JOTbz>eITHqvtzaqnsufK;rZr7Yj%S zSG3JYMordV7td4f3pXmXx4GIAzAPEiCG%w?in-3V)Kf1%9vvk|2tIVc;d2o60kW53 z+AG4ow8x2|P?ODA-SpP=ibPgW=c)-VAJf)k5&rw!Z+^)9w=H+x{sQFH2+y_tdn|r; z`RZVI(ETPnQPT5VJ+CY`ZJysR?}Y_f?k+(uB7}%c(CxlHHJc%mshIK~1O`qbEN#w? z2*Kx~K}z4~N7DVUE$$T5k0Y~}lW5i8B}zuL_DX~_iDD%^lo4>HOmlNl?g?f;wR=*a zGy1b%Z1fDYokxRM=(WJ{$*hlQkR?`5wCcsD_*&!N zatQc}$*v>aJa$&jzs$wz5_|MLu2I*<`3Hf2dagkExNumrkExC1^xqiX8#ud~XT>N4tT%1%E=ync&v)Y_3dLYuiy|LvSR z2M)?**sgv0O!u$tC5$i!!eknM_~MIPqO0ZPYD32|LEH|(iTgEm^;bw0Cb>R>LGbT$ z{#bf1|4+E~av|C#j2-v>%@O}!DCaoqze^BtwZX^Sf77z7Bm6(CtR0|z;13{LC;AI? z-N0^_x&q&8g8v*2#&J}?Lj86u68XmxJ~GjTwxcW-9-wu?+mN@6K`*jX!U5K$0JHNT z4BEW9NR`0&$7nrv2i>Gwc+c~KPxquVw)M-GB0z*9Pe0>~?hZ&v3)#TEBj%E9k!6<1 z^M+TdmWg&-Q3?BR6@>YJtxdrDAOAhQTpQTc+Kd^N5B+o7gicLIk@6MhDAQ6qZdiPd zL_e4cU{cOX1qhr@sqW=rI!LbQkIc1j7rOC0$0YXu6^xEJ+vn9YWo9ZciEm*_f{03I zHy8B&YZiL2YL0K|vyX*fd_8(eYo9a(z_378b|4yJY#nlD~!+)LgFO#aGzT5z@ zkK1Y)|5?V-_4ShtzTAyT!myKG8N^$v`Vf6nM*vZYZvZ0$5GX{VmXY9jcriu^c%(g10xZDDnNwH{rUGeSv z&x&!)oUvC6)(7B!W1IQAA@a&&x3yvSBnpSLt4S)-Cigwgc^nzF0x!Wtj#5T?iA_S# zua!aoLhPkmK#EI}4guIHSZpb`rd=Dy%NkS85nxVaz&;5lq<3FZBfr=fed}lm$Rur6 zPL@+BTAl(n8&!?{X^3p$YVN)9@b>FOHJ6AcBpQ-R+4Z&!zI|1`K^*h)!UK3VZcH~%)(qveEb1NX z(WY=8j26+g&5;S=?LJMpJV%OeuHvfQ`R|1SszyUCwm`8Rts}U~7mA=o7uhL_=j0c{ zC0x_U0j#K`{DEL9xbeB?Q~=pS=C?ez(a>hgY?l>bTN^c50(d$lt}BCRmb;trOU5m@ zs;LHDF_(a>*HDzAzpmsmU2_~#rLo-p;S#A$f5gAj8N)G@-kd~wxXyJtUCnW6YsjA^ z*mpl+dNiElN8~iA|KdBfcwIl~Bl*_rd=M{&<+Oz;=;`p%+}~qV^?15&^r0ou6X0K% zoeZX>Nxx&)hDPN3!t+Ga8M}6vD&lKA8+yc!2`tJC``}^+Ri)fK;D^A=I6+EMZWjwq zV5_(UE_d!f6`mVX&Df8mpc^XGuhdxH(r*s0^4WLaMGwYh4)2WB4cG8NBIbG{KvIa7 z$fOf`CUhS7#&+Juc~#}<;gZnfL;w@$=0!xmj{Am_1gVddckxTCRY+c(zXxCJ5z+Yr zYo}NFc-H|L#Gc^mjRF@=awF?8{4ETGA75{bmpfosx^P#UVi($GP)#fP(&?IO%?8Y$EAafsrspUe5+a!&6mOUgq0p&6Y>G;!@bkBv zAlVMODJJNj{oqN%Gx}2geF7HGdaBxD=}h`3vbh~skv9!saUi$IApCU!bs-KYBBOnH z%WIi{O%`r3Vip=K$(h0@fMTgV-UrmQ&SScYEkOdwEP* zTpr%(e2c0(|15L%#>FLvoCU3QrIm7@(pz{Q8CfKEWJgv^j8rAi`cSY3Ti;?bMDx7D5|D5m`LDtLxoA zVHssj$D2@Eet$#_I>H{QmyGC2fvoVZR)qV6jR{2M2mj&S``V7`w+^1QOLJS^J)1-HR)>-QobwId@bD`F8zy+!f*KT^_SFa=ALnFN+1ATuqO?+N^ zWDX=WmRBTx<#oDl-u?XrEQwerl7D(+oxoVWI7t&hKgfZuaa zTH(m!FOcH#T%0^u!lGu4K06h3?@fOMW4p;P7?g z)WyH09Rk^XvlhPg^r6l1O79agR9os{8eY8lcKOVp@7&yO1mC2zA*MR+xRr-d__`y|0b!IMvmTS za#uzetiH)*DQ$7ei8!i}_vs|l6Xc@|Yh8BtGY6?!p8JD}1noIb8;*>>f+Al?ff}n; zOGz{hr0LjolOsP7XW9+t-t(_4mNsjvn^kSd3=j+;5v9D7j#jKL#b)e0|IXZ7_M0-C z^CW;Kom7UI?`9+THX3{H$%ZDrThqNDB3sR`m}$*1&u8r=3l_C6%LU^^7Q#PxbTQ4o zJUKtn&D|Iu3dBWQ{a%)wgqb9o2)5+STe9&&7ldy$D}YBs^$sLUrL65K7!W)16|019tT>b z+s?yDnx_i8|MY2x+5A|##z<9H;0(XV*hS_yJi1h^H^tYz-Bj}9)tHo|QxEkpzH8^l zG1)`ySMe}HTFvR*SRnXfq4#v`(23*{Ab9F?xMt-IKkpFr#X~3ApD8XUgu%iIJ~qB2L6BOt8bm zYnV{i8_do#go=3|^3Y<%CQUhzGMEW%K*gz>;OY7}2S292bw+ec@26o7`4r%>!G-H2EMYH{lP2LBJO$tIc z>Rw7rqNrr*bAK4lV`CnOVe0+FE%qIQ9K-D-kBYl81ebj93n{kgkEh?dn3E8PmJc$GReR-1LL7Z zZ{vsFDzu^4YQdLxU-VBqnQJAcdVn5G>wW@EX~uoGmIw6!HN#*x{Z`ip^UZgfK?MDw z2r-*Giqw?ml3gF7aus0=#|s%YA()&mJQ7Uey&>Rwlatvb*T!V~#dOUGws5lhu)3Ik zd;03+6lR2aP(EVmuP*Ov;+O=JCv=I3%EZ1d0))6 z{u^MVvUGSHXg@1L(k8*Wce+cp8RKZ-raR}(^Rkr91aBJC2MNAA&urC}zx;GnH=M5P zV7_)CGiXt2c#>+vwM!sdCfW-$n^dzac%f6fIk_8rKvvOU?ySH9yW2cpX1>(jb>>R* z(berM-M0?fq&)-o4eMzIDWP)4cF&JrV=pln#N`Up`(phjcb7zwZvZ$p7*w9tZZ)AH zZH_Hw$|!JEsn8L9CE~^cIz(e2TCzk!MwKzaMrb-BDH-I+{W3G?iw~84vEY{Sw}fus zI;Axh)_Y#qB$K!@HM>5W6DzMF5e$7J6Wyq24Z)^+&N?|ZtX>j|>bpmu%wmn&0@1Wk zD~0`N(O^X(09M?V4NRfWz~91hWPD|4r~|k+H1Qsv{yea_s*RNctn)EIlFiug&YPyW zr%S!9^oBgJN_#V!*_PpVam|qyoDmo;8Bg5C9N#u~bd0i?V&#-tr^wtb8KNF^Zcu&p zo2;SWTvApEjm%k!#e#;LpdU0c7Qc9e1A$p{Qjs7cygy35I$IZJcw_>K+c{5TZA4EZ z{D?MEe^u`iBiZX+mly+NOu4Q%H|mdW#qr?0*O|gLaQ5nM@>vHS1&oswjHMe@0;bvT z7cdI;Pd0nKe|p1*k2cpbTZH5%Kl|t<%k1#tZJ}c0H*Z!U>hwg|1BY{$4XtnQVqpbK z)(ultF-VnYd#(F)EL&S~?rfhosvV|Jx*ugyD2JZ`Pj+>W%U#%;g-|xKsMg+;fHTOu zJ2**2%6F^gP^1iVp3)D}V6HMnICIm|!k-=U1WYe<8*U4oFNdD=c{-kV)qQF@UqP8` zGdpEoY9o<^IE}UjVuyd5Elb9a@;}q~6e4=JMKWf_10I+T?pmK8e8!=2t4v98rfixT zo+@gtN_LIVU#?7{b5`X4COEGz6rvhM^O9M)+wcJa3buW&?K)lW>1j!cl}Yl-)`G?k znO-l!eot+R7IgK6>pZ&D&$Yppu@V)xS;1^9@$J4k`F+8Ux--~@x~}M;E=daVeEci+ zK|6f~829!;3Nvm7exYsVh9mP%z0;ER%QJ^19cuVsBN)Tc?tzHThAr>Sst1!3lNXbo z3r#Z)peJlE*}|E{ftkv|VQ&&)i#dw)sesryZ3-GSH6+V(kR9)m)^dAXLwU!)oh`LE zpbros=8CH0+Rd#Hgx_9(z)Lxgzu^au1|U3eB-6!+4C7^N`n4AA#m?eHj?IO}9rLY5 zHgV!Lvc-4I0EN7@m~xr_1}eK3F4fvaUpU!ri`oc^UA0Au&`cngJ<6D<(yIrMVWVvg z=80#hesbZl&p_+5LX*cjZUer8yVQf3Swz`#D_M`(DpM>xfq}c)A2^g%xcRkms|`EP z>F*k;(!Hrm>*$j zmS2K_!M-0?e87KXl(lUl8`rG38k^h1qf9qjK2wsP5uFWP^y1L^0BNbjFsNjva-^~qfPXMcp|URh;LehSZLnt_<~X>Y z+vCi;UA^2Y11h=NLT!Cz88o8qw-BCt1`{x8z}d@yic)0w1>;SwnzoL(n^cHW41-&n zlXK`3?|cGoCQTH*)vjc#=%T67oddOnz(2jSRd?`kXTA({ckA}frJu9WZhvEtjhkUX zKI6WWi;Bp>yE0cdLwBmSjdgnKiL zqVhFpWw}62udhoGzL+@%WP+?hO-JhCy-%OFRTkQTVOP^H{pJ+C{iR7s)~qWQ+LkV% z7sx?&$fa3|{8po^^C4{*ew6XS$tLvZUTmZPPH+I_)O}Z>3z82CLY6O1p4RU?V)xh| zvkAwWP*K^7i}jnsC%r!qPVSo5@yTvP+j!bg8oNDOdOjn%)!!Ogp9GYNcr6iFLCY|9Po*feo*bx-DxaAez|u9jqoqY;wG93 zx;7igm#6q%s1aH}h|3ifS7Jb5h!zEGka)QFiCI;fAHF*sGvPyO?3OY>)h6T5 z{XVie%=3CGd47+-^j^mEA<8O)!|wtOKP&D{3o_XSBier-ZE9XBXmG^1I5HB#+*SYu zC1$mh=$ILx+3S+9iteEau)ogAnIQ1&Q01E*1mcn`r_O1`qxw_AYZLK%WfMroO4+9= z&b>=cYy)Ed#>~`%q&sM`5>5ysF!FS5mv>aiY9?WMea((2b`bIouag)n%tf{0qqi2? z8>lFZZWS49_jICY@Sru!bSSn$6QBuouSek=(kd&Oy*)D5aD^*OlxE39f+(M(zz)?j z2E7F_@q~^q%76ZDSFSj6wa4K}WB8X1=4rcuQ%3Xf_;6m;5l zoyY-p^LAB;gGSwM_Q|M$wQk#%l(m^RBxxOpPtEG%0snR368vb)@9cO=j81y2jZ?`p%%oFg0(+>I8#S1qg0BF!ouG|0$_x z0kEy56^TqZBc6?oJ2+h|lJ#kP?8`JCq_8jVo!>o@q^3-f+tmNXhRzN%<|Q9JYVxNL z3yr?VON-ucPBr5`;~8=5;s_Hc6@|BB@nq#X?$bVQCS-e96=9*BuYT!N08*ZsXPNGEr5n@kcmIy-p0_Ksg9d%yx}w}GzA=alP#c^DJmt9%#U6s1mtC;QlAXw%pRlBT@ecRO*sx+Fn&8w%*{!!*zGV(bn?b{Q=g&uq;*zeOMOyly{UTb_vQJ{ts& zbhq0HiWmv58sd%a#lSS-Pv!D_O!GHfRVc&od+o{lr-VQGQL5Xg@%gxZ3VzNRN;ndS zkrz&yG${s|oU~#p*n6>>IV&RI7i&uKIXvYg6qKt*h(eN-c}Q!QXnw*}LpJI}D=joK$!R17=iGiT5AH~rKa z;xZ1(NV(d^ij9DCXn_^;Yu01J7OD-;>JgP(n+=I+O&HfmS9RK4)y&%X)&YuNx+$ya z_eX72pUdf=kFiS4%&VV?QAcH^o(HK2fk+@id8x7}ko|ovyHO@2hu6(`gjn_oS?{#> z-+xSzwy5nCpSiv9I!_u0C4Q3W(&7%h_KF8lQr(~UF%RPp>&IT-d*A5X70PIH$igXb zc$X8jh}juY%$%LEB;dxH&~<4+<~1U;`er&-WJ94+ZcXmkupp6XTjQQkzfaW3>iv=M zDe}Z(Ut0pP6${C8OfrklA7=npE+e)UPl}>y7Hz&1m8&(mfRRT|y4YuR*d<2_zXRm| zS$VYLaI$;1W(n=uGP8Z`w7+B^J^#5*+`C$`RMJ zjKwIp!@MNjQghzJb|eG(yyr2OH$`6pla7UGy1G9ub0u8ZQm=%JM-LK#6YeV6bUo?g z#XhboCyV!`J0?kf{$H{Pqk{YxFRm*`Pz!*a4+gKz{m!D+KBYV-;g51cr*@fP3CWbk zv92tE#rWxdVavs8A!K-OcyDm)+Z%)1yQYPs)U#mTKBV`UW;mhn$NvX{n83qoz@u% zongW#oRe2~%nVq=??pyNX=bMNON!jf?uSB1oP`P(7ItixsCSezJLq%C+ge?xc(njy zw4EJDYZDdmdv#x1++Ke>d0{ied4tRKg7pp5u?0nV;FVggIRsdyYjlJz>#Du4VqS6u z*+ZTG@d3-bx;C8NF|T%#`htk6*xE`ZIF2hp#UU?c--ra2$^F(1qiqk7oV|`k^R%}zr=0Bz}R754ZvLg98HLh^;V?pFYTgpyO zYpc%))e1Y5gEl1PDIonoDDpuo1aI{06Owd1%O`H*TvPp)%u9n5NVyXa1Y5;@C1jJ^ zh-Q^^;g=|- zv{zq3(Z}8pNBP@r{m3kGd3Jo@RA0Ol7q9DU;Eg^eF+fWjMU%{Y!qWiry*XUO89LBd;fZ5gM-z#1clPHx#dpCtczym{(0Y8Qc3z;+xu3ua?d_Mu`GO3>?(s#@ zjB|($L~kZS-mNTj-Oo)OIT78v&S;WcF>XWLzZti6y1c7>eSg$|>AuAl+>AEeVE^qq z9~H$v43{zFfUdxjH;`CnD>=gm^f7+o+<(f_3-NDF&&UKe2ErGEnRyz1VFxo2NwP5n zs#5Vd3gUaybx~hSOhz4EgL;E`p=+xnmBX=p^rruu(l3{q@a@(SL5<~@F;1s^9K4M- z@+ifu(A)DZu!r`AqoV^ltMSHPzI>_`pa(9=zG9;+&vMP#)7{6*MZi~-5yz*rHel`u zhEUtyT|ZeQZjMSAk)@MKDvj&O2FVj+^nWwJ{V)95B_+O50Si_U`rb z6Do`hmOw0m`&PSPurBErOcItkycYIwpM3g1UJUPai7Ot1XExbI=uTkP(7EK@@VL6y zcLidej*8iOgm{swj*TzP7-c*_391R(SaC>Wx~s`SX2iwTd8;s%@>Ys=hF_+slEISv zlGj_L$_?LrSaeVPY>NyOGpg=qN4eb{v$apWV(mSNNbKf#*iYj^J}1ex1j_)^Jtex> zrvY5?S)VFF1HX#mfE^cXGe#3$eS`0rgq&y~^FmvI(bh;p@3yJufmKo2#$h=%jvYT< zx~&qix+;-bBzjlqRtCYFID!rtu_6Wyt>9jn<}wrM3N@Z*aeaQT?llh zceUlxN_^8v+ESj$6K!BlD^D`CJ;^M(iQ9IEffii|Ht&inLFmtwQTZqfVlu@Ol5Qu{ zV%H!sLtgY^kcAdgLW$P_a85?4;s`EJ)q&&eCncA$-uv`AS)ufIvSK&!{W$8K8w`U$ zE1@UYk(2n1Z-f!j0U0Jo3zde>_IEUDVVzbdS>e#2EvaswzJ$35IDboi0}+w&_HPnx z@PKPx8*g@H7-Z?F5%4Mt7y-6m3_Mpl*$NCyYkGMWw0KD(Ol5kwF%zNQ z??JR)CmSIP<%Iz|{N35o$_2@6_`N=}Z{t2@fEtnB&|C}~7p%211#o2JOEyuN5ojLY z?2L`5ydBr<=ROtzqx0?WdGSs7vy7O?t8n;WEqn6OCVn9Z#MxOviGrCj>^r}0I!C$J%5 z^Yi}Tr~|Z~I#_QXDu!~~v6lpY%B%|XeBYfL<(%u0ffk(1p_-u6S&g2A^nTcwqRNez z4K`2>rX3>=zlWvdV{qOGV*jM$+4FniLG7ih8c63f zc-+CH5M5_Ajp9r@ZUqs4QAE4fRyV*biQ7`2 zn-x$n_|Xb=<@)@SM^ev3tY&M72FdV74p zq$6wqN}=gbo6+Snhk^*dxYZFNJ-;ZYq9s<%zx*;)xK;6)m40)uj6HO3eU;mTj;QFt zQv1c=%3N78DWBLv+4Jvf5}Kp8nrpVPl2_$EwXFL(9f)|Ze#S|1=kus7NulQ-%3UeN z6G-NDfYpg_K*X1^3?@{NQXmd8hzAgQ)>?zc>$1{iED7U?IyTM}o_5gX$}h1-~M@iobS50kV|4dbIvF3d)#A;I|4cn+ayS6UJ!3t zzA^-f8zEI#Ot5S6lPb*sZWD}iwTul{IhiP01F(hgTn41I0cXF`Tk3)LT_BhI#IRwn zdMFRnp9caZw;P`N`J7Tzo1b#`rw%XBa?cSl-Fp4N)>{}@#69_9k+T5@X)^(rQ1%kP zC+3w$-e11G=dudTZmViy0gd0i(1os+8+>be;6qw=xIFvmk<#fj&x-_4n9pN8dnlT! zj9>z+ec?fCrf59_c+77wLznnxCVg0P0CJtPsN>J!=mol91+ENsREg>w_e?b0`?OKj z3=6c^FrLO=3Qzdd46i+w3D9Nd_S}M@4M(|ph#M!NrqpkJg>Io8A~O$ zIy(;J`P#=5Tj8o{6>{-P5d0HB!RgbKvc9C?EEJw?pS?PrAx$}Xd=K;r7q0k=0Z9zF zPY4n1oS^JQ(D}Qbeb0>>EK-?RuLUNJOD52NQK9wt{T0GraQ~eUJd*LQjlW1h2wj8x zzsOF6FE4H|^ovz=;SL2Fx?fN-^$&lO82%jxC_prk2M`|q`0(%shIFSIXrM>>EVYaM zY-;9Tj4MQ7L(GM^*nCnC;Xk=l|JOh-94-DIv6UqMMF0obXGoV(*{{#>GKfyQHEkro z*% z{t$ZXJHon7!1-}M@x|AVdg z_v-jP&<5RJ8_=E1CGs|KCFUH(R`E_?(F0AtZvMhbVGNz1UsnX#o+&%1t?hH&W|hb!K`gmKHX`Q zR{m{LaJeJfATHJ_mdJAfm;d#E{#qTs2YNvnP5YOT0)^Pcq|^E9a)>Yfo^*e!Mrk7x z?j1NE60q{^+_tHL(E`Gc-`66egu0v9@q<-|-^QoTrG%)jpCQPBi;4a+*^ag~VGWj! zUh?w!Gv3rPAWiRgKGNosss1lBar z8FS~0h~LK@m0Cae&(WfYoIJ;T;i+l_N9sBMhbAROn&41Rg4`-kK>LZeg6iE8J*jq9 zacrXzRWEzcF==<5G(*`RxpiX|BaBw4;SMc38NGi4kBbCZh7@A=j=z2B43cu&?26o1>@PH%S9ti868TXP&ejsT2 zDk?8HsMvS0Y0ZAVjFX!Byp6K{ngZa9Ca};E&OubaorA!l6tG0lxDSPr#c5lfK9-3; zo-H2B+g&fQsGDh^06k~{@>FVDV2LktV~pMS(N`i@{Emk-{K-n$V6;Ntc&O=nDErJ$ zteyr_hiPlt(3!foqEwW&GhMbPU2C0-Y;&JxA2@E2hCbixJ?A<#m0w0VaAEPwD!<;xQ;a!Doa z1RFYa&v_d4Z_{6J(#|$rVO739f{q-YnWN`w`Odz#+N{5yY3MPY<`2%2PZe$F;Ufn} z8>@z=(-^lNYHOT58DMGrZ!n%u1t_aY$!dbSuT(3P5xhCL6*cK>sG8l>FXWdQl zYgxtTU5Y5ee^03p;?K-LcZU#zNvbdrS}9j;l;^3}dNWIcGO(iE6+%U{)TGx!W5pD< zC(CJvN3+AQ`7mK~^RT?5fLPNyM^e7yrw1t& zk~`C60ho4G9dvJYhi2GKdn|0W#<@6na?dS>7G#dG`Z|WB22qcQ;42F?9e<=)VIzc)32Lv5 zo0MDj{%GU)C*hxr4S~_Lbo(#2FEHTpj3Nu!{CqCasJRMoh~dGMp2U2^AGZ+%Qk7>vYw(Hx3!eV?V7_IX1Q5)IAhg*;`Jb-7V_Jr~jUHSKwqMnkHJWFq3?+ zT(uFkA(Y!DOEt0k44wvzOk3{$a3p@areYkyIpB_-<#arscDR&C;arCtI7Z>Eztnsm zLD`gbY`&*Zx%6$T>UEP+hKsYp<_HF!7b*jXzRIvxj29vQGMQwZooU|I^I0M? zq`M6xzxXO*C6kfUldX9ozy!cCl!hq_0|@^-cDvXPa~(CaHuqhaOo-qb^^PCDS?13) z+#OOooQj)1lu$afgImnz8TL~IpsNlp4WTPmSTn&iE`f{hT9@H1IDKq~=H($=w%6FE zPz%k_0XcYUITf50$VHKV;gegJHPi~!-$LMFrND)-SU)%)T-QCq#Bw2AU{A#>kQJ2w zdF-3pqnSw|L!HC{rDaUY9eac0c)z&-8~(g?jkO}frTB+#&S#TpkLlXcU5Wj(HmFm zrY{U8TWW+qb1HXU2B)d^xjSa|m$RUelSj#@88<=VRK1x2;`~%?44Qf@SJN_H2DF== zIQra=ePj60VUTf`3hgr3NAxeH4O@o3dL8_r5L<5|?8B_dK{Q&343O_8gt`X4buZ@F zs?@9XbXnSf+cpQvcEwA>3oQ+pQb|>RlS}@>URxE~pXz^qkHXF_dr>Uu>K9_cS|6`9 z%rupQ^Bo`6T_7Y~MhHnojsSnjpc!r(T#@alJCw6^Hq2^9Slkt77X1bjf}oK;zJ;KW zL^3o;cnbq9)z~=FE3TZjGa&q1Xd)6hX6mHp7FWnN+HlK;>g#|u2#cs4sL1bZwchjKh$08I{sW_ILpOyS$sVD5=os~e^%Dr`o<7PaQ* z79DncGAhOhP~2|2A<#Lpfv0oozUg-4wFe$ryNsI~$)s79Q#0<=!cT}=E57w*;6CW+zHA+4*F z-lHBX5lLK(6(};DkRZY|M8qyjPt$p<4D4R9R;pzKu_W@0J#mHh&*!u667B%8__$YD7uUUS>a)Uq){1WZvM<23kgDIzfF+4`&ghfH? zi{LDEwpR$(Fh73;iC7Ov7!V@2K4A)XKmp6!kI?@jT3_!xrdKFB>%lap!$auOw=KV{ zb&le1ecr`~2OKQaCqNl3$7BC+(g%phX57KTzC%1$4E*)Je{@>#rBuv~Epgai^=gU* zs43T@3O$>SO*qutD@k z?=`@f^n9?Rc<^&Y1#B?wOU=NC%D8pd{Za}t)gJF%QQ(}1=Y_^%N?fkqtCYk1f!O2f z6BL4~sWrmXwzV5M&btfRfu6JsC3f{Tl&sksy`&#?sDXo)X_%xp=wSslB#1wLsM`(Z9I-^JO6|Gh7;?m@0-{q;>xCjH0x0$$H*wtzL=c3lKoI&c`p zaibmOd2`c?hsf1JX%9_qEwb`A?_t zUhxj?8tcJf*mBK~Q)}wh<7xz})@o3BZcGa37?&O_0 z(NyWIx@z0%GY7lqX}HjV$gaE)bm80#kb%9Gv%=@IAHiWya$q!=jHWy&5|}waMJng% zlGW#JOd;j9`$A}9R`o6|Cv(z{=rKGu2a(>~evC+J-0mC7$-T$cvWkAh2fwkmg4M!@ zA)b`)%4YF6Aar5wh5fnn`>vpp$rblWDlNj{(jMfR27RMJN=2&rT?*kZ<~cf#`hn$Y z6YtT?o%ie2a8~oBd(W{~)=REqzq)5X*iKa==1~5GQ8N5zp}4*|Knc$xPxhZUiwpsS z%-42r2NNL!550*rDmd4-KGJrs&IR2W9C$gs26(_l_WO#j4^4jtNxugTBZcj^h`;+= zus4zjMC(tffT_9~xju1+`w)L?4cf_lzK#A~Y8}t4J^Y-aobV8Lwdci;GVBzVJ2P|& z9jd<|ig)55ptd(1^Rw2F<+keblm2B`BNV7*DildSwr7PuUV8sj%^B#KqUh$9^QM*O zt%|oY_l<=j!S_ZB8r+XLOO3FO22OKG#oMv>H?B*gkXdmuJMKz8K^=ZGmI8V2hi@tB zyquf{=#V(>n1fxW_}uM!ahP$#w{G4724AyJ)spg&%56{&eog`CbRh}Z;|tVKF^o2C zyxdyl%4ZSa;?!yE7oK69slgSrHx3VRy<~7$0x3_Cg%pFkhLtuueX))Z3tr=w&ZnL| zGup1ntR`Q6F4#cqvlm&$RSSbWwd)4Q&96&+dBDGRRMxdbKkasw5R4flO_}=faC|N) z-?>!i`jY@uqS&6R*31g{P1-`Zeu0nC1-`z$cU;;O6qK*sO&nzA`IvO#Ic5Mw28;c+ z5N56%X{ACa2?155vMS)Rlr9`-e%h~KanMpfKt*1(QHy|}6R~#EqR`^}NCc*rgc9o2 z<8#p?yA9Z6+_?46)YudN>a7vwJmvI}Gf^ap-gTqSWoaRU%c1ao(OP)o=62Q@4M4RH zYYHx%Theg2zr+CZiuP~@)ftN3$9FxvfgAcbHdLQWY>`rCtGhy;{HKBFV7L^nocWW935=OKsJy75OMscuRlnjb2oO5ZtP#y@M32&Q#5Ud7v*#Vb-bRq@Mu@wWgo zt3(klKAk8)o%F_$=AOR&urhS!UPJ2ktlJO4*MrYQZojF@*NNfHEv-v$I%sy|c*5a= z!vE|PmMjkJNTV6@wjz0*uPoGYM@lnlZKP^cMDwOf-Q+!c^wW#%l)~GXQg@i*M(C5c z{G~kY7O5mk2}o%4+U*a~MD;f<>9)FN8?OR~-+Hg>$>P_uhH~ScROS=jr1G=_ZNGjZ z2)OFdg?ufj)5vWjgB&rOuv=rYQG*Ii1Wf4A(37R8q zz24X|;hvLHFKR7y2d1@-DyB{M&Q4q!ziinhHUauTTb?7idY~sq=ixdN(rV-9cg(t~ zfTRi2t0r97G5;rVqr7Q6YiU80kgap>TY^e`-H+8*iy0afo(;Hk`+$o%NU1#`&5=(B z^s^6VRjwi9jKGCL+wa#`1jv8j1M8u`}(qF4yz^1bU~=B81B z2A%zoAipqX=9Zajb5>J97=wFyyfWYioi(TyV7kTWG?-)kBMXNqnooW!!ThzLqH`h^N z<4h#9f2zYX2w!It37D;Z1D|PlqKjhGu~WWK{=3Zg;Sxgl;ysh#Xyf!dXzV@h?UVd! zy71#QqbEo?~oo@o8-TlzUOH*TPiSUZ;`_=)Da2%adpRv zC8WNo)PpDe*``BgFUtYGpVpHb*CM7<&8&18+cYyFPDvq9ZYQaHQ>OPuwnHk(u&>b~ zsT945lAincDG&i-oIPV1xX(y2HyofZ;&(;B>zbA;!F@ry*XMEP?EDhD3pgVD2_EN- zQ&t%C1~SGV=%qL?gcZ@q1mn_wN4_r1hK7DRB4Wiem}(Bp7QS45^8jy|h3}Ep>e}VxXk5hE@Nw8@^oy~ab6Sdg)6B#frs97s*wHUb z|FCB*%kR$oHj{?u5Misx@Y@FU-Cfe|z1vFb*kQRd2q)JpLTmd-zJo4rM!-+kPx()m`3m%~OX^6GVvGqNEy<>ly02aCSshi}g^ zeRAZ+Oideny`q5etqm}|Gb=waE8Tu33nin#3zQJqT{ML3c)qXZUZ+uG z@4QWz!?bv}K7t`S?{1F}(%s#(ZW_zO3Bb4V(;=gz)QM86H0WJWj4X0c;_wZseP3K>}*RB`#v#K+1pgOkyNd%(bQ)bjVjayn9+j``^n4v6* zc1_MgY*;rdvb?d7ySOn*H_F?K9@5^JbYdzl)rTxDMq=%21xLpHu0F6`96x>}6kZ#l zs=s2V7qd;*rCjcI2_6)I>MwGA#HsWC{?L={j7%A`arWRs3r}PUOvW-pVESZQhdJWD zqm-0xK50LFUB%I^qY{S&c8E^YN+=~(8yRI<*V*7B>pEVVM?4KU1r_>zpYDrBaN|g7 zIZ#HwkFnGig(583E!Bqw>^)f$solJCQ{4H7&t`m7CbIDmBFO1z994p!k;Oa*Een9W z)9f_QG88}KfsHV^f1JSYkrx#YC~65H9i?bw??!Ebx6!Ep>RoSNZ=DowxqP!vojVuy zZ4DI6Jflequlp2$eCN&q1hkF2i;0XH^djhkhfXJyO0VomA8=XG>&z=hLg*h{na^pbqrfQ z&XHE$C3zgsXDD#;496>Qw=}BM7g#VDb=tcvPiGJzL7aKiMNgfhy9>kqrFB@u^RpYaevM z^)^z`2m<;`=vGjP2&-+;%qEQ7Sv^%Rm?u#C_D9gc5Za1oDHBacwv||=N(*`U2fuS)M>Tak;g6Ql+G!!bU z(Df!rDHF=0iHhrUFJH8Wt_eog73cw?09RPFi(%Q}SkGbBHDuYazS2F*-(@PdHDp9# zi{JrsV69o?3=+mIW55?1W;Vh^=*v;S0kNa|=mn^SFg)#v@>3)tpwy^ix*Le>7kFcyt7;>q7C6gNidsO^d@9x0$MBTcqYc6b?`))r_xkzYfW^N{{%U)_q0OU%cIxeKSQn=oq)b%^+ zMm4LR_xP>Z^8t0X_A7(6Ha}`ja$hWI`zeH;e0)sA?@_7@@Hmc|3e>1*uTseo@}Y;E z5&pd3!w9_|oQ2zM`SVGbQgw!D-(UWak6^@f#|Q02))FIDWE!|L-h?7MLeD3#ch zj1DbgATOZ)<>+`>0T${N(_u(TyPyGlwD?vAkv*J%Suk%>z%@{T%+d>1k-W*$4u8-cV+`dI` zP4&GpGXEVCjoO_1r`Z$5TtXZVqQ{ahxe`)G5M1xmZnF=s!>V>=o0GcQ;-yDhn>FU1?YQ{M8q4k)#aDklQ=GJT8Nk& zKpL++=|@ky1%&rrKZdQ8jK8TaPyP9WZp)-PI2z3yp%LKU@Abudl5eU16rf0Qs07ng z+$wsH%I3)9n30CLxAY$6ym{ODy2F=EuUSlhTx=)NKeCKLH_B0S5FQL}T0rMhE)5`$ z=YKi{W)Qe+f`G;O1@LvOo|#mhy8!g+25ML%z3G7CA*-eJCN|yv+HjfIw>voRs>(ct z=iYgA7FgR8)bFgnDs0|Lkon<^$MbN|Ur%fU)0n+0?+610XIp#r)BWMy?IusQLp*`x zo|yVA9n`+YuYh;;tT!(ZxF=i}t_`k3X=qZJ^#(HTsyYti2xer`)j<|C3>0~Q*FNK0 z5O^8G%TuqJWzj@HF^%F_2lIM7H>e-S0;_Ka{b^*AP1G^!%(@oi5=ZJhguj1TBxDP@ zG?mFap7!!(^7g1PE0v}MFc4t!Y#i25{+?}@@t<0f#S<|xi@HF`s9UFdK^UBE3+Gz` zN!nVNy0P}RUYGX3Hf_IFRiICdOzHRw)g_fRg~?0dk|pRj537{vElFulM#s6@eC?Ot zQ>jE4Jp%#PK3&Z?ax!4=em{u7o?nP^3zDWCJ~aoRoR-USR!3&;HB9NFFST2igXT;q7|omnATS#nP@P3vizbURbHRTByri^mNWUiiC)l+K6zb0f}fZl;@@<8+jhm1TqpcB*|$USq~m$) z_uASKd>+m+GKQ1f1-b`|*qTS@YZFMN3Jvh{ZMv#Mw3qkk^PirFFVP*u=iBDpaD34) z$z_0vDgLaMACu)EN6`1?-Gf+_2fnyiHv}CviOmnso%ABA9K$B6EJvVuZT6eHr?Fb@ zP|Hqx(B6?N4&M3nBmTL-vvp<50YyDf<#DxVu1SiddRti2u|g_ovNJa)cuwaMXsql8 z5<C3gw4IFFtvH<=SoHyo5XMP89yV#HK+T78O$zP2Qt*bVjnH0#~ly zxcB_xQ@kAU9PITPf28sCrx^i_n_NHw7XnL5QvQ)yFq7ZZUid8UfLMWEuEwQ+k-yC) z-AFKm4MnS9T|4ppa!S-!ES8K)8FPlW-XjFIPpP#qO+h=<2)>Q4;$IiLV>yka+v2Lu zT?%@IU1O8^%SVT#S*dx%LTEFNM5owDhw=wvb@#8vIlZxI#AzYc+3)R2PZb7tUD1^y zEqzNztykh)D&i7D%-hE@g!9Y)^}g%VUi|UaKV2gsYE9&J9_RNdoiGd5@dzL@ zlKgXCybthX`0LRTzke=s}Doq4@T^ z*=vVFny5tV@Zt(WCezqO6zjj<7vi^NDDUk!2LyQD|Mx_|THF`Sf2mN%`tB;r%M zGs{AH3IDib#hd)q=ns17jsES`x*wY!;?1j&Bz;y3I{4!!h?BHY|DFrPGIH14nH$#8 z_#^9_wyE3w^)DC9#T^GogC#*6!u*Bg_aXr|PJE}FaI&Y zP~4q}o+fGk`13k~ZUN}?va!6sS0DA5q1!Tonvqbd9}1{dZ~x^4Jv zFp1}npSW%K==$%80Ulp)@D&nCi;9l{DfC*WD7&*Ge|&Ea12OSQ@9NzCYt>(Idkh8( z*2|tnl$HO*75X2#$ol-_L5Ex;nLU^IL-fCY;wnFG+BipLaMytmh%^>CVXoKq; z71=hkjj~KbrB&Xf;>I&#&Vb?b*c3`3V=_MsSoJ! zQ@!5T#Hs0&Z(X)g{1AM^&lDzSL+_v*p>da);NvY93b{aQQ% zq8R&jR7#GS3mwo+bgp)_{Y^6ypyPg6z5kFzGC$7{`_~$Ie0h3dP;&VGxrKto29~4V zUxx;NWba+WZySs6%*ka zKpu!cyiuSflZ>!7t#(bv4&zRIXm&B<*jsEUQy25UPrslaP8U;jE;k`uB;ayXEbLWHzU?d6G!UN zbGiJ}HkN?G){m3Q5i({9Dc-Es0}Vz8iy#X6UqrZ=^dqZIGE{3^^n7s}W@F355f97F z?jp31d;I&YJxC4EYbZ2iT}<#YZZs(wkq#nLU47ywy3Q9wleFzR2(N}^y%}MQdUrfu zJQpOLrP<^+c(w-3hDYeDt&>R_H_WxL+SI_awpqtYtK^J;SJukooBTnb1NcJCX~Dk3 zwh*KnlhH z!mbh35b2+#QFnZ&wm0d>I@{%1d`wqu@EMERxQ}hBP_G6S!^OSbCw)+lqhES2y^Uok zd>|u)C5du?H&dl@SkDjN5DJ>HhdRTuS;np8A+{^&gMjIk8FSD;zRT`gR7@T-*Qh?h zY9eQq+^xU*=9M|xA%H3q5nBy8$|Ns#35KtH1YlFQep>au-p&20vZF9$Dg-1m#3qXZz{`lV#d(qHi_?t zNScx?X5a$>h#)ULKY&i0KsH^x+u6W09-uY|fsI=R*;=*HxHIF)O}(SLSey#In2x9* zydkT60aH{M?xm|XuWgSOA+*pIhx@YGybxSX^tW;IAw1@f6s|cf0wKmp!MGd4w_3lB-#hu>wS*6ApCq|jE-Xw*BZdzn$_y!#|BMn6fGWvjL zJNiLctBAJVQxygpn5uye8Fvja^&|*5`%}_Nc|iQ4ytFEG>U>fo`RvtVEbv$?UW&0Dk6U)&igvAb zG5Y5`Zs)a0?%Vl)6-U*`+-&Eeyf%!#ShKoe=)DgWet zed-j@&D@K~)Q9h+c{I*Clx4zRUmdg|HVUc%n3-vz5$1m*lbu4s< zE9oD3J{qG3WH6qq4JglI1V?>hAI+)=pLV80c!3i1(gOH>@F zv2nygcBa%9?id=e8T_pC7tXA~Ck>@5A(G%MMKhIVi&DMaQ!q*rRJ+M8%VIRl1VJ6RZ$X z=o0+UWKGjiwSuWLuT#J?iT?{JumsdOoSGVqv5B-UUt_fGnp%hY7jex8Q#C0*Q`5ze z=4=V|C4f}LI(DushrK-u6dm5#o;4Y44$N8685l29N>!v@1-SI!!4C|1H>KLyuDHdV zidP1~)a>GX{?{VJQsYc{&d&~YS>#wqBwO}WqgYvi?@g2oe80E_%n-|SNUva>9$?K$ z;xIQm_!62(mW*}YG(j26z}MFkKZj{4rw^?B{FvRgKa_gH&0#($8;3Q%%=|0us6+(Z zp~j>gz&$Y%b;w@`jgw*|@c7y-I6O^O4~giNH8Kr<R7dZ@ zPv6OmWat$f&)+drBQ$?rfI?;x;ihUkWg-u{*gJsDl4abONu9$o#g}L6IizqtG@Z0d zp<`#(ev!03dcf@y*>oy8k!h8Pe{rK4lxsbI4%)TGb(5Yv&N#*ktpm5$prvR8ohO(x z*R?!U7&hH6Kf(AqW249YJX(ZEW0svD_orykS(T<8iDJlC!>_6uLDkH!5@yZjvs5=u zQ9=t8J9Hshrs6fHj78irel5yk@>I?#$*YY7=I_-#bH*lYE8za80y?Ir_eDxcyD+~S zoKn1R<)&rvo3wA*SFNJeD6o0K0L$v;T|B-F5`JhPl5sH)=25vqK8{{Hz{*f_P|d1}jcJ6!o}>O3=xSazWKBRW~;O90L>(HthTf=FU* zE{Hnp+HO$O$+INVy2Ad**hH^u32n6MmPFBX)uOZ`pdqTBL#$s;kzuP$h{i6Z-C1^nKgm0Y)!=ZEK11$}< zExNjw-6?#&C9bH0+%aD6H&d^ecNWA~_Ml>7`&^PNTCjL@zp@v&XU9O`tMZ%xSBzEWr)JD)c{<+7S#Gu~^Y9~V>Q`-QZ7P+t^kG|eAnzSm=F zU%I#2f!!PYGLEuReqLb}goZxVsPlG7{B`TaP}Y+N5!>Og9~i8g$BR%;$Mj7Dp*3jQ zl%>ICmgZIp)+o`l&tM;KGOIRZeIxjeQ3KsX({2hWX6{nLt)5ugR ztJq)74O-72D9Zk9a+8HxIWopx@11I{ycENj9v<{<+tmq4e_0q-=4i-tZ z&3ut}W_XgZ)ZN|sv+eT(n2l*D>Mwi_eEap+d|6+5;zVu@POT;o3nqN1dt}0#>)69G zWg9p01xF9?j*IsPTXe8IDWc_sD#<#rJ}r$i%2XsQwqx}+3&PSVt^_pQWy``z)6L9j zK{Y%rGOlsniL?S_O|ukYp#_xpdrt~qkw~WP?k%?*Fq7>~Jg$;v=Iv)A;0!m63y(6B zx^?4>{U!y};HLIuuB1MQ;Dt0^Pew5eH)>KAP38S%pmk1%ykJe_j)O*+K zu+JO3(S_wgk+mK2;)IFj<9&{O7T?VUZe~6sNcA{uU z4eMRHe?QTV;*$A~`z`lG&evg5ief0pnD1X%$-bjY44745dGMe&NgZY`J|OKyXjI%r z+j37r@u@>vtDtGQEm6BKQCuau2N&aR(@OdNiOg04E186-U&tN`O>SW3SK-An@SG{0 zpjU-rQe@2N&o%23C-EJ==u3V0C6qVsax?<$(w}vw~ERBQ6;|e)x)E5XCBieMAf2MG)>k@zvJ#sVOYrwRhVud8k z$ZX*BnANPGA_^G%=EN1O6EGhxr<$B}uvL)GuB3zODx4SIT6&sT! zBa;_zgvx{2Yk-^qDpB&u05aZ-Hd>@}6_YZiR@g>YWHMmXyc*&{kmU&t^c%nnd!%o-q@$ zr`Cp#gtrCbEb&AvPZ`e(0Jt*yAHnj>Ov#&3=BV9n0ouG{!``RjT6NUk0m7k7B45n$ z>TKa{TI25wve9@7Tazjdlp;Xl8Pb-NY^_)1*js3*6NGfPwR%Y6v33w$*^kgu*wo;D zCznYhP07=+CbcSksgrP7kFF%9u0mha@)os&bx1W;noYgu;7Rk7i`@7L{P8nqf`v3B z4PnPo+qXTAnl?U=HFJr_eh~gP?-~}~h|=1Sn2yNkQQS{_Nv`Ogv|bbe{Yv&@M4jq}T;A7X znYVpDbY%7JG{vHxsO8v-NQURw8bP6-<;`cQA*b*32MAIvJv^DKPsHHq$t{?Z>5&vw zt<72$=nLhx(znpqCd0>J6zY#2Fy@?E>wysru(_aR35iP>K2}ACM*`cvAihU(ToEL`el4d()p72; zM?W*7qV>lrNYzD|4CUUnuqa|JC5V|RN*YL`D<-a0m<_zdoe*D`D@RA=?~0&H>R~ye z!ooSGPaq@l9qq2l_)1pQO9^eIR3?@D1a&pFrh94j%~AG!>io*yIb6;@e5ciY~y+!OOmS!YoQG`HB{#4PfYUibkJrS)^iB_kq zh@ujXB6e@yxTM;BQPI$J9se6?j}v;>tME+q4I-0OtSDR?>T=-TdQSDp z0v;+_N~|~Y-EGh~Ok-S=t!QPaMD=hIa!1p7fb%Zl8kHGl^-*!CKb}6wvT8wq6`q1d zVAPjWHKK&@%Rnmz0bB5h_!fA&tj`Td z`%xuP+Qemta}M0pX{$bNRHS4%>o7B(Ud^qjHn1MnPS5KkN_8{^g4#iVa+tJ`$Cb9` za&|`V5R>wR>E525<+9is3kXfQZfy(7AR6BBg5*wF;<_mt664folQ zO7lKr|0HF`4O!7txTFhwwUtMqH!f(iG$5eSOT$QhvB!UI(3(_Ivhkg$D0I6~4$93Z3_fKq8K}qGMr{*uaGZegprGqoFVp~l{^j6CtV!=d>dr*ij;j);+VR;50Kf! z_|`q~j00be6d|>Da*-dt?|E<|GYqHgVQ=Y2YAWM1>fBP`G~7f~aZglRGWEsBtZEmW z1)@t7vjl{4ka2sP@g9QIE3Zwi;TGl73g9Y+raexF381USd|^CGZ5x+uJz%6g9h7Yb38iiDuCAwh5|1O+SWB|~+ z_UmoKUS5$=Wi3+j_bmC@l2dR(TKUvS0!xX4Q9+EeY_)8|)Vsq)uR=zf6>okc?JS%= z4rhRDY<{JgYdO4g-u@D|Cbu~~H3x@~B6!$wQ};^J72&qX^%~cAyC-{h*R7SS32urF zT)eo^9NMJq=?|eC@x6hC+wEexG@5=6yP5okr3s>34NO}lNKIZ5cbaHFT;ojte$zfGgmo~Ht9Ir zj&wmE4HAYirPC<7f%DW3vL2T-RR5Vg(xm#BQ{!<(nprQiA0kw^hkmi(U;g+nmCtAC z0c2IX51)xjdD^b%+H}9jVH`KD8d!_A{F>l-05#}M+QAu>7Qz@!jf;)W>KvDD*uv0w;&S13Sc_OUfDdi$pZ8oK=^79ZP zi>G!?^6JMAH*2OHa>Vu`qoL&i!j1ksdN!?;A)P3Y)1s9w=b!P#x*2dgm$O}6t@Hv?D22KT%wdg;gI5!YQyer-kSCA=;US^Nkzda>iR;cNHnf7eHwB$wP%@K#$< zn}o#o8Wnt48pcD3*&BYa7zEX{RjZSBrrRt+Hta6}z%q+zG;@&GfzelwIzGZns3p@U0D84)Q%QU<`GUgZ7;Gxmd}|j?@Z2eh@WE&Mj9&{#A}UBSp}(9*t9%!`kvP( z)gop`W41q{-||^cl9RgMu%~XVgDoiJNye$mPq(}(Mn-BwD_%a1oNiGQ2obvE3WN?5 zbYFalOO!)FEVGsL)=`n3s*8 z*U9lzIwOXYW0&zIh5E&EnSAxf`S|aM9JK`~hE@<1J_VZXp&BysC6@bHc`pVqBk`5K z|8$+mon=YS^8N_Pl~htRXvvv68(i~cYxle>?n#dF;ACH1YA{o7IzZrS8*6$-O3Jib zXS&ffNnPr_p4p!tgRn3&&U(%b->9Q&_*3a&Do8|!S0YIkLR1Sgb6H_SHB^VW3!E33}qnO-9$f`LczgAP2&_iOpmWs<}}#Ev4?!Lp9rf|v`7qS zSN6x06PwM|NQj{_#1u7$)a)zr@{4f36T>vV0hyCz}wQYm^}-nrKr4qdhL~s}@qLR)-Jd zk(e$2S{pT&YlHE>t&I^6W-I#3L+;`?=bv7XJYb*yKVdf{xRO5o%?0?2AoagQq5mZc z{Y}jIU!u_E{_wv%yNl%SFIL|#o*iXdK+3nehhK1X`pn8(9#+AN6`!f=-F4W@)2rAt zB>U#c$pyQG5=C1CXgnT(c_cey7@X8em%pGu^Dp?!!`68N-me?V?G7q{p>5 zCr+CW61N&*Xwk}mCGUC{cc0q+s(ukJGsRs00I%(|y4gsH^NC7{c2Mf%!AhtU#N;Qs$-yd>FT%%5rEze#wh8E`@i+{7n%Get&Tm(m|uiCK5|G z0TmNdZ`mX`XzD{xqRHA-B$Xy{I*liSD!eY0lI$l2>MUll`%W7ikZx#dh2*c&e8le2 z>>%(i%AVGXQpAzM@_AVHdB?-7X8o+Xht3se-+YXCbT@>#EaUw{*S$bV_Jv2bxwt+V zIn#k!fJS=%;(6zh04g$IM~(!DTsgdeATO%Rd6SoqJ8+(PnXthbLHVSo_6pDg@Ni1Y z)`>>BN@hKpWmq)b>+UIPr$??w(~6A54NA`A0FI6$lPY9wBGO9UfjDcBFCypy_nyBtxuj`j`9ACV zgB(jNgCIe@mVe>Za6!Mkb@u}A}PXp%6V|r13RmQ zbu7yw3{-=A8wu>7(PH&-x<+oMdaQ+U~zaG_GKYpRjb9A>{%gfRj7vTfP>Y>tMafo zGRXo~KzHKPv`e5l-{4v`KRX_@*m`{!-EFCaZDmMVMj57MS_PCxke#uqa?8a#<^7X& zY*SXwGojMEhs?&|6|VaURPRO!T`P+S*t?C`j1-b3r)nUxYcTV{7~ArL?(T|JeF6LgQxx_#ONLeqNIRKzeqeq6hF>zrcUF97O3aU@nF8j&^$WD*DogsZ?g(f19El4z!)I4 z2FS0(@)j6nDlgkCi*>&BuflFTXK|dG{E)Br>`+elt=1f(o}?=R zww-?gTLw6DFI#PVU@}hRh2)LfXH}^Mkj&S{;cy=H`D<>0;-ZGT^@k3!)^`_4_tkU-BTKZD5e^-*o-Yc3q1+^m>a$l1-sYT~HB~`4x^kJ$1 z!ZW_=Qd_h!muO3n8o4CFx()QW|?rt1prY{OX0+zY%J@a80ROKeg@|o ze455T9q0!}-0BCikU9SZh|M+0iz6>aNqTeIy>;fv{s_k;(wAqFQ!&P4AKeo5^7F=< zFIK-e2`$^?XAaO>VEKsj?N;9LD;)wLRgkf%D4|8yXfw3aIdLcPe1TEl`R)a*js31p&iDqz$)I^l9rW(CgBY zBm~U0W8Kp=G_w3+(mYMRbUPx0k&e-Rq_l`z1pOI0u&#HqdvTG0)xZSApPfYkoM)$8 zrg|7Cy#G-`a}+W!;QPjJO^$fmH23MX;WFvXdlhYzdLn@A!2Gs{Zb^-WFUmkKgyOMS z#KwYUVnI!hUV9?DyGgz`;VG~qtj;}wznlW=)xR>ORG0}F_sI3WDNjG?d%41Q>?(V8 zr@XN4H&5btj-|&q@|4beugAwD#$*x^)y{v?C|AN2+Vi;*u!= z)xIEkb8{_A-G|{mOV)|tu1TS}C~{Yi zIX#oBlVr00w-&>au_F3YrPGmID4N-BmHfe0ku2V7!kmO$)`)h7CICgEikpI;(m)z> zm9qOmOdlL|(Fzol0BA;<^+(gc=}16qx@PfW`l>8nYg}h}B!ZN3=KWPgGm!YyA@T+F+q z?yInU&v`?cY53#*17pQ*?f%5-mUAq0@v@H!g|;>=Ss(GG=i2{OBE5~7MNCK7E9_N4 zxWqq)Dig8sPB9*@T1}zXiFAOMwBNPJCJUEniOC3-pfd@-sp#$` zbP<)qel|*_oc`Gf@q$Myo@({xouUALE(~lVfK9j@di2JYD|jV&nouHjWsLSc&gv|y zBTe@w*WahJy5snkB^0iasvp4Lru!{kCB#( zrH*lE8U?!i5JFh2>8#SZCFSCUw^wp5B=6Q=i_K6!?>IA7RlYmwGL8}4UQw=Si$V%h zYK_W~^QeZ4=Pn@^uB|Pr>1a=G>~2vIZB;U)@2{aTylWQ9+m$V2k&WPmvvNCH6`|;Y zdJ&!v60B;;0_k)QH)1HDz>%Y;3mY#d|A_-KJ^hZPaU$$?H>NLlZX@8~VCl}XaOHQN zh=LyE^3_B^@$~QgTCTl4RtiTs>@@1Ok?PTJ>K<{oNz4!T(lU-{+=|v8ZU|pzH_M&r zXbjCAQ?~Q4boRw;jGP&a;WBcdD-Hn&VVd8esr~ld?_OXU3;Cap6+3}i1ik$(?i}e} z*$zNWrEkb1%OiM~nxBjF;JA(*;3&;YNn}2>RRV18>{%+`j^BO65M|5&R}=`=z0WVuB!q48BohM&WO0R zKSBwCb{fsR_gJFVLX68Lj{JpazY+L zwe!rv_>AAD%v3gVd!?7j&Y#8k68Z;He29H!l43zj6$=$(#dZLZvc|ie z0XE0D`nZv(JJ6Rg20CX4a9eZZm{XuiVm(_bqK+yGhwZ|=3O|ANK{qb%&$F_te@KkE zw)S!HFLXA;0g7^aLkxEBp7N;2t*q^c9?7W7Nd(C}2 zaI$Xjk6$!8NC{O-E}?OEHGDIe zzJNWOdlaq}Dq*=9c^InQm_p2Xl}3{P^gJma2?t*KcZK z3vFUfY4Ogk(0kg?#V62Ia1Zo_Z{h=wezwZp5&o(8mJF#X&0mTy{@L3Y8r_%f6D1Xs zmJd#^ed!sC6%J%yDK@h@4OSSa(b|MOhg6fjD{dGDGYA)U=$qM>#f#@mXJ;3Bb;`~8 zFJQ^*y@O*~)&%)0{;S>{ELxTKQo&g*eWc4~4NJu1V=z7bydx?ZAfKJTL{ za-U%kL`MIsB=>l*4a@0j)@l*B`ryf}adfj~tgwehO8c2>JrLEMV}ovxcY`34uHbS3 z^1H>KVE_4!@tJMhxW^XE36zd=-d}eT@?DZ(htH=vXMoPfuLKmS0f3YCRA~I z80pf;=^oB7F>3mpghnc}5#Xjnf^$w@#r!utNgU44)K z&A~cPqH*r^XVX=+yFIn|!A|}d5hf%(s5hUuQzefd9UMvYNBbPxUTZdtwlHca7}OA{ z@-d4K=Ph!G_NXIH^uc^65Dm#ujsV|iYw|mP?!S5&U;OL=&3r4TIQr|=TEzO}&3ZRS z6RqO)dco-vNDFuhUQ`I=6nx$a0^kjBpvcqjgq_gd zdYlRpFt$sF7)u*R9W6D;1QDt3ypU-|R&bxX;-J4|3Ah9rv4qZT-xsCpeM0%~=ZB5*I z@E%)7_HS0$TR^6rA+5$zS|*UXbMDndGkrtdNN_?by}#Hu?fQ`0&x-!FJ48c%>tkXQ z)OaggQ0P&_!_oZ*Ob8(W0aT_p#{;(O+4GYJkTp%9FNgj|`iNn5S2@~5R998M0>X~_ zzVcz<8?mmyw>i^Pqr}wnAu%7;z1g*i6Ms~g``-15X$(&9vDDOnvD0t`)(9MMT%C5_ zJ1q9=8{^W#M3-Kt*Cl(*B@TSCRd6c^2`5DZKpwm{RoMwZFepu4iCJ|hN_WHfGP-qy z1D?Rp?f~NRiaR(dj@}wHwS|;pYt=uq5_Y)$><}#!{&^0{Yvr0Wp|UsVPz5_F^=lBc z%0ny#|KHX#dPxR^SDmttuVj`BZ@OS+Hm-HwRfWJOh9TatWVSt=|9BQ}fb9gOsX_E3 zwA68X8#mw{%sG}`8B^W!v6hA#3U{xWX4c7Q__4tS06!Rf#9o4t$z7AQYR)oDzkj_; z!k)&oUpx_2*?b^mX>Qyz+Uti^x8RP0(>-5v)G$cIpZZciu&H0CUX~wjHDC%KUI%G~>&cDUu`pDT(J}Us=a#QS z!Co#29B&?KRDh)8cvRNd$r!$r?lce;WytX^W3GX#_HJ!g1_-F?F!USbPMWHLN7DbF+Kbyh`hRNV7X8u4O|KO6 zZzK1=jokn8ApFSo1J;Fqc@X~PLHK{gg8-L0tNa%hfbur}cPahfxs=R3`k@<53gjy- zyc%fGx7mO`{NEC60EU(Sj?cxs!r9cg%GEjw660?C{Ai#DlYijWcgMdEZTk=YCo@Um zd-PFzet!NuXs(`Q0C|`BRLDlp5jOg@kN?3VvybPP<&V1D&%d59pE8K$X)m?!bqwb8 zEPOcC7+t7Z8hh*WowpbNkDfWSe9n{~&ZZ-gEiTuiWtQOBPQ&PWBw-xY~t~baIda*X`25ZUj(7j{BcQJ|UAH zC+UPR_1~J=sM{lTHiA;UigE+#2VZ%kO`p z_n8Wn%JT=LNrKu58t1GowVnJ=M+2-kRF*iXZg3VE&O0b1Y*N{Ukbvnm(0sy(|92ZZ zMb!_df9Ub&Un$RNUvTzBt>hO(Ql9gQ?SFjEY447`+Cpx%EF=lfu#DMp;|`$aHySP@U2Dg)~9?ei19YmoC)KC76bO7rY6Q+cX#@TnZaBPor~cMSIM#jV1P#QKJ# zUjJn1LGH*K2)t1}fA+?&^#cCYq75|9;S&m@T1NZ!ivW8XP}y_elj4&=yiwFt1NHOY zw}H@d=+~(cbTO5K&A;`ihso8yOR%7d@*j3lOi7?UV&76ltn{RcQpZyw{y}iHkH%U6 zP!5%wHU&~qK|S6FXrQQF^!O!PU}e{3ITR=L$Jzg6mwK!?OnM#WANbq?L`*nB%YT*ATwbTD*mUjRF(m!`^spx< z>kn-A&kp~6G=RK|I->lmGHQSNS7pCk{mH*x_Sc&E--Vxl7v?W_`TrBkLduCTexx#p zG|>bH@O63>emg0R0sw?COvJjBaC?2Ea3A<+PUTmWZ`dZ;1Bgk9(XlhS<&mmGUtcSf zQ8y}ENC1W) zH8F%Xfty+_#keU`po7DpkH$$$ei*=WFlH_nN&{?2gaS#A&&P43)z*OrCGEdsY|7+d z)2cW^1A|_L)9BODFf`^112VfX(51#gY`OBosoJf~e1y#4ne};#AE+;oF7F2wZ(AMj zfY^T-rUcLiZsn({@muP?&3l7SVqC=DhX+AHqM&e9lX7n@;DDR$u>&T`W>FR|>EUG8;69cbjGh`{JpG02IGYF``5HiK^#%YIi5(I?ttz$5E->6is7w7D zA5Q4T=dX&YEbU(m%I-aZNY8P^uYm;kV&R9p{`$k!A~?DIIy4MJ+u3uqi_oaA5$wQX;o}}&OW@LYTJ92By zso`N$m3hX-qc2bTl2zPn*_{PadEN&J6wW3_TdL}`kwZq#Ep8yI;TRDJXjfyMc3vH_ z9PoWDhtwU-VYbYduMHElkFQ76K}y1Jf0CfO%(LP7QDBfsn{u zzI&fQ3_K_lC<|AaH4&2&CdIUg@Q*TuA z07B(dPybD?=)=!lJ@6+oWH${=|a*+?PF32fMrZThII#WVBmvlsV!2*+xhO z0cGBa(lfG8V)l2lZh>-Hb<5kfU|z9xhDUM~KCIqiZX{Zp7Bbr3`eJMDcHIJR1&1oi zI?nfOs&y#1XVbE`RCWZw;)sBTc@o@P^;Xm_oFQnvOT=7;k;^eOfRNi~oik!PQfNUb z6gdHEIcL|54^?w$2X3L}n6ijlb&UZek9gO9=nr-;5eup9-sG`ld%hEa5O2G<(rtDp zt9H-vehNV$rCM~x8J*+7A^zEj`{51#dF1jL*ZVmAs)AaCgYjIq$LIVOS6fgT-1;U4 zPhK$*AcGvm>3TlbmA~jvQ(K9G~5PRNr{Dkv6UqD=jhAjZL z_a9_-o@uvojPp@=(atxm`j!tz0CUs`^zvI6+f_(JyWh4wtGYu-?Z+1_hG;AtTEzaav9>=jwKM&S6yj_dJsY zGdP+X5`iqfS@4pP)ies8BdU;!G*7kFO!t0X1ujzB)-L!I6z{T3HW-v6&Bo`G*lZ0* zC{su)JJicX?fkc&KgTHb1EZw zGWb$!z_(*YT+)>5A7SKpduC3)^3En^9amrqz0+FjmsmgKN6arCi<>xSGv@lT{q&Uv zW{p_1XQ|`#re#>sGPo-z=4R_|8F~*7pvm}-YfA{&Sec!yd(|w0e^L)1WF>K^YDaKY zNL6h%xG>N-sd$Zh@2#}3xZQUWaxDj~2-Wn)CW4z~*E4$f`Awkpr8rc{O?)lRSK^io zxPg~Y?U6feO7fQ>AYjE@u{gBoo@}pW%a?k<(hN#lqw|^ZV{*BBWe4LL%eSwQ{EP~0 zg<5mw?Bg(wgY5T(VWiQNB0Tr_GDZr=>>9XkzrDGRH;$X@!|ZgWG?$N}TMRM!w(RG4 zh{;P5zuk{8>M1=l1znr1;(lW1=x}srZUHM9cs{CY=X)S{K9>pBP^LP6J=q1P$L`SMkO5fFtw;{Uk#4gq2*=J1gR%#y6f8VZKak8k zE~4-*-0n|C6VyX>1^ah&mZp(PNgs=?oej%xa{Jp&?GWLS5Pv#}KpLZtk0PnF$#vgy z3&v{q(MyW+r)vvH_OTPGfJ4o6anz?BpP#;57+d+?@Ey57C@i&8n6bax=G z6+GbBG*O^SagrNRFk`h7F|gM z;m3woJdmUM7Q<55N>cI}Qr+I%(~Cl4Lj;k4B7j9N836b{T?$|xDrmkOGp+qcYQ`rA z3CH<7-!Db3jYsu4D}28hurf-_Psj~-f}8$mPS-`fUe+e2cikvpr*)(6TSv6(%Vp8^ z=KwxMfCSF}u4hbJH_UhR?!DgoopMeCB%PDw*ImUhY#-6s_+6VW*#+PXy6KDvV<2d)_a}hrT0?BA{L9`T6fW3vT6S|YU&Bt z*=pr5F4^<{nT&crM0tse>&n2_&A!PLwE)Ct?3Eji*eaoU_ zG3oDK4PnVAYKX+~t~^r$Y-<2XpSWI|>(g_!zbAUpJe0j68S?w&hG2Yl;Q~{@11N&N zG7I}Ctsh)XR(zLd=_)2Gr~RauP36?Of4Vw9)Y!pQ)~z4`fM?OcP z_#uYFwy%y}Bh(7Ykk|`|^r63zu3j&=f|=;eRZ?137=3x=<464@U7xUaDJ}A)$*I>m z$qYRQE|IF24#a%|NQEgz>CfJPod?%z!bE!x%k7=b06FngWc$m39cSWrK0wu?AI5o~ zXI}QR?EwGgemXDl#-*;Nt=9O8mn;8qH=YMPt#Pz9!s~d)&oIIC0M}4Mzf;-GJ}Ne< zETUn#oGgI(!P}+9SEPTZb9=1!@+Nnd1LOJh5cLR8s!XWjy+nrNJ$Y`fa3x5Ge+9nQ zdCCEL`^4(ZkXE>7)wY8aUE5{3Dxcoqc0%bdp>C0a(H?- z^8NOn(bx1H3^8*9^ROww2W8Bt#v+eCOv~bZ$+tThrlfLGbaKVxk)11gpGH9s@qXor$R3~K29G1uUwT7SgxZ;uu-2E0BWmr|Q~CYM-% z1Ei?=CTx9h#M}O|OM=aFxjsh>!!&q4d}Pro4KjArwpQ3Dgv}y5kaw^$o$KMeSTBr^ z2PqO4jSx!LzMxd$v#h4+3itwe(GnIp``#ZukF@F)$@L+T@B;wkgW@0pgiOO>Qn5j5 zXv4|9gcNmx(nnT6MZw z0R?Y~V{Vd!7J+;UiRR_|p-qbpv4Mav4rA`y4caj3^jti7ER~|AS})#yfM+60WEWoF zQFF(vX|leu^09lm7aXZo4oUKELEx)wSC0!3Hh0%h4ux~zQ1f^|E6hQ(EI3BrnDd4} z`fj;avaagXh|E$c{}UvWb7@Uarb}&{RC+h=_VtPa`W5^&`ODgm-ep`E<$#++J209T z^?A+TAtHN=-F#AyrYSKXIWzNFf!aW(J`4+;(o$Z<_b{5N22W z4K)Kh&}Nv|^F~OqK}?V!XEear+AE9>wUvz-e$7gbxW2nMqo(5*FcZDp#FR=*7J9To zyfZEloTR+h$afI>IGuO|3iAtCsqd&AcCM~U$yOQ>?os7NRFA7uH#Rhnt?JLR}q5Yxcq*WT)X z0KtB&fz}QNqO=M=Et**dz8Xft*OIH{!>!A4(0IgdqWi`$>i6rBU5GAi+9wSAi)9_q zV}`SsEZ*d%S89Rx5j6wM$_V#}c-}GbH&9co{k8LuRsFf#_Gx9_X}3^~fKKE+aG9P& z9Gj~dwD|eD7HZ9g!TyBVdXN8MN~kf~en{|HFXTSN=x&kDFnvaI`#!Q76k8L>vOumX>hawAv^(4bQp;t8$T zBxSE+R5r<7=L1mI$CW26l^UNLqe1gkMP39_ZWXe=?dIUS`=F6c*yM8_?|OaWwpNh; z{cbIjZOI;GL?)mNERmxf#x9o|mjSA=I#s3yC8_V4LOkx9lPGDP^Xb~hr6!-V3}m~m zrm9qnb-Lq4=;_CA_}EDFEjk2xX(=pi>*Eh&m#VVty|PVG4Ilt}yZ)dc_0?cZfP#Kt z=!A;3oL=iY(t_5&yX;^pC`Shq=YD2>ZQCXmI5d0Vs5tkZjwV5 zIku%VTle#i>D-Jo6Y&=5X3lWaB>`H|Wvm$S*;D6nq~OaKFDHMxV*b|ne6DBSB->H* zMtpzP3&a*U6UVgmDqDm$sz>Hmc=CA9;@av^ZbDA1tF<$)wLNS-3SM>%zMU9N<0wHl zn&3I(cp$o#4Ry<+SPj^Hqn0K>uI`F|;6$BX57&PjZbLd6ZW4K=UO!Dv7J!-dht@i} z`0gx0H%2}?Ues5ex~S}$sE@0uF2lix>V*zum}|q{-PE9w!1}?70)sX(OHm$Lh)>+U zU-<8cUTpP1tfSkd-NP@7|IJzKnILh*w3lvAJK<6{)gWKd^WgOhM zkDXcV#&zArkGdz#Bz$_T%0Zhzw@)ztTnq^x;Or&p6pr#M-Idn_@w9QEZPeM#GSw_r zn2)PY;SYYtzNV_MUY4Gw($NF6tF42li3`i1FGN&M)Ewp@vxez_(i}S-?sglNBS@>} zjcp&u8g2ti3;iuq z4xt;Jc~&LWX#4eP(Smk;_niAJB5XO9rGkq?fUKF3)jYusD=?@BSk&oAqnX6_>lbc_ z;YFbJl6H6Mqs2?W4rOHU<~;0n|HZ(t;VxoWTR1QBVJyxgc8%89Cg8fNjtyty%nwG* zpBUxGiW&QO+PB$``QO*5Q|hR=7|IJRg;RLH^_}0Ff_4kW?qc~7W5qhNg5_8Op#Sk_ zOIZ{zRe8z#bxxlAXfd{EJEIQyXt&r1v}&x6Tpke`j?(T{zVlJ)^2+a#>UE|9-IVD=0j0!>~)-UuIo7VBW zt^#gI^%6DPq=k)-+VeVz*E@)R+-T6uhRcoXS|19^F^FEX$J9pN?ve`_Jet0}iY zxH=q%xR7vLVGd1ikE~`i)v-JKW($84g{&}XO@vBSCBkF@`_c2Ufbuo+-o~<=9tt^e zU^DY^`5k@}M}`aUTzRf&^YA)EDO`SWy6@7hk5X5@@Bi-f$c{EckAXSl&80&|=WbcP zG}o(c-fY381vnC(vx!vA{IBFE*xB-Yuv+)xoH@9c79@w70t#BZ08W; zhtmx|s*FfgnUnY^NIUW{S^{ zJpFLS*6r!bTK-bJCTIG%dym)$`FXx#?l~&m`DwZOdcaQE%$yRUd_BsO`K;pOefwz+ z9yxyY!DA56wuE2Y{oQV=aq%N|H`B;(G3T&&I%;xn{oL!e{ud&9sR4xke!qB0D7&N! zYQS%jA@o%LlGGjZSQ?p)2o1(ZxPb86BM}7g;>W%=(Rto80(0#{^Tx~HzBkP|#ZHMQ zE)*3)7)HBb-BS;vWm>&>AGCaXXExIzY8U@S@WN!+aAnc1n|gDf$6o6`kR+0GIn-1* zX-Ir;Van@x2OBDro`cOcTN|i|ffbrXW(jl0x^eNQ$EjA8>=&~?B><0+Dk%BGeSqD2 zI>wmMATqP?JR5PiZI$m#0>|QM0DLfH6z<#=Od_ zj>1hpE-R2uTqMI#Im^}SJ++;wx9Uw_Kg-I4Dn$yWkNYw8#Y^(RW4>G{xldkuf8c;^ zkD$@AqaL*Tx#AYG^e>J*Q0|1Kl-O!*k=|nCtVw8Ev6;mlhq%-jL;X0vvX#g< z0PFYA^BBrCgAROX#@@gvA=-R7-Oa6n+TLBDC1NeTezWe26aOGD6LicTVa5W{xXQS_ z#J@#z7YnG>bd#h=xkPASBm61y*;z%_;)f< zKbXU7x;El|u0CtVQNeNlj%(K8)kpJeF0UoHA(aR7<15B{ndA*x%9qAVvT8yTt|z~R z`VeED>`JFU`kK5ac;Kp`)$Tk~K;^m)P3ZEcw93MA)`+BQ8_8_G&OoU!I+iI3LWp$(6HMNb>%>E&G- zRGi@I^fM?}h;X%r2dt;9(S+vx7Il4POpM@JjY1>QdplT>y(B_>iy}FUa|W_L#!B$? zxDyXMe_)qzaZpVij(Id%E}DW^)vm&aZLP-6EpN#x+|9i7>>Wu7QM!=yQW(j#*{p?R z@!u$MXg8h^#m8N#$n4L_S~l)QHjT(WLxr>*s+ntG$nBWG^nb_hOpJY<hb+W^2s?>S;rj5f}03+`OXbv*%K^;?zp_*aC=KbNWr@aML(7o#1z* zQxXchgn|OI^+fzPF=<@hU4*C@!(yD;Ox)TNrl?O7OyKGQ*Zdf^qXZ>s!qaD9~{hmM!-xPv)-hDTD`A^ z`gF20z@HGoP0DU_#*ufi=6gm-TYRyIl}i01GDLVo{Df{2WZWzc5`TwRBxD-BTyaRp zYk2~WeZTX2K-&3q4)@p5Ym3^RMQS0n7Rj6+g2W*OA^ybikL0ez)v!QuokQ@eR12r^ z;@NbzKHI8q2yjNsSa$tMVs;D}_(VnvL2Ja8| zVkqBn`wZwaRRX?Q_SZ8^xVB4@8XoWFF$##jO`hVc@%AuG-%VwANnm&;+7uC{VEAFYbM$aKh8OMhMOZKWY@kvnoxNM_&k(w@sakV9LC z%2Lhw61F*}WCp_o3IuVuKAft%F$Wnj7=IOZvKu>u! zw)AbgXxxQVy_STGgXUq;*XJy@%XVZB5wr??9Q(pE86g{E9{N>>df;s9t?vWmRw^#1 zWOvH>Re}qUHQYp5F5nuhOk<>dbBLsA3^7LZ{+5^0(=j|9GploK;rX4`eLfSFnwO&# zFA)2$3+U^~6jZ*ep-r~ELEalL7P)c$56Ih|&I+0A#WILe%E4?pT~W(($x$DDHY@5(-H%Jmy^$K?MsLk7zI`do z|D-Q1VAry0u4jOmBj0TNYP#ClcsEF8&2mX{zFwkhORYTV12?j5-N->TvVO)GyHW0H zZiPubDWD%SH?1r+uB#L*?dx=RIZwu2gtVGO)U9G){uGy3oj&`qE;!-C z{W&a7-oa7y;}vyPuX@@UxCb8u?E$NDcFQ04Ze+98Yg6M>^ImPnB=aO{mQB47P|qHB zsrH_2t@B@bR<*C1JRapEdtqQPR%X>#qq%R_hcvd}H38OkZ%mg&Kzv^S{QOv(T;lP$ zPsSCD{)QQoes5UsF^36zzLr={<{ES~d(JjHe8o|w?3%b@8O9j3e2kCP# z$>p{WTxZlS%9T9YC5zr=OL=)yo-XqKn_|d#vUABZkemo3e^x;Xt|#r!@D+U!?ZNM9 z=I<5VqeWK|R{ixw)u;Yg>BvmR^h1R4%U}bQvR9Y9=S#Oxyy7F5Qqd*h=Y_uzd!qH| zySguJVy&Y?OnZy^7ygibxU*4w)}zo%@J$FRB} z@iHA;Tg1ufwvf>e3aKcQDUZzv{A_Kob4&hBb%olIxBxPvs& z@Y(ogtVl-VT8zZKZcU@d9&F8Y=0VFo_g=(y8eOR_z?6IMx&#RDC?2vFV;2!?cHP_9 zIf_uzsjYy!ED%kxWKYAFyvYX6L&X^_Xy%vgn;=MxkCsFC(93A%Ae+QQ*_~puTc(+q|c9T`F5RhrewwqA#L?-}kaXvKD&_lt`=_`UtH|8~(q zA(A|Jqn{O+S#>p?LAQc8cmIf%|7sk5dE8iTbq5=Z<2x``H79E9LSn5mu;K6UlTKuI zow=5{v?{2s*4FkoTL|~%nzh)PlA>2F*pQqoVa^l*7gMo*+x4{Cu-Hgksi@@&Vq4Kv z`*?IesVHzHIIVrwg^*=s zF%V@$SdD#M$!dNMeD8_jsmqZzF+M7zRKH`OtEf6f6Bng8;oW|Q!|TAc646C1;)u}8 zT5I{7pka~yG@a^CM0=-20&>hip(f*_i9BK3=rwsk%Q{H^6djR=^24^tkHcq&2eUf< z`144j`1rlsEk)I}goVYNWIJ`i4EF)$(Tdm}NHAuj3~aBXXw^faKMmBs^|s^;-}hvI zp%2#B2V=KhN{sSu*}mFj?$-W=AYdf&g>t@G89RBrM;5*_5y$fP^zASp$4D@}+Ium) z$HuVmD#L4A8_2+eN0S%jwFe$tbti4dIafFScD76O2?@r&G*nZi9#WE55KXWHXO)K& z+h#&C0__(lvLB*RLL5?+q|D>zaZ2%?B<*wYl3@{}n}2`wSW#?04@B&u@o#@iaQ5;u z@J+UFCm_H7`f5rM5bH|xF}h!ddMg3!lt=hu(BFfOpDjEHK4V_- zji!3)3^0^nL0#G}JDn{Q2g7=HedDQyS)TyI&ebYY53>TpBIuTW&1&D18D=mnwq}Z2 zON?OH)e}q9Q?&+D6=bM>Mm>xd410TaDTwN!3k*l>GhLSdpq{A~n7zD)50wG$wE$i6 zJ(J;}(wili)vM^<4l0Gb%>)KSjD%5n$oop*HB+a`6sXjj$$K_E?o8PMYOmQ3L_2tA zDS}$GBS5sNZoOf6E#{OE5RF43oTz5?77E18+j!+k{na~Qrs1Ct zQ7fbnC?rTmrI+ff1}5N5Nl!KPly>k)Nn%p@uQl{2jKOiDrfLLqBe|K>FZ}wCvt4>X zWMd!AYAVB0tsr1NJz!F!albQE3-^~|C^jvHDysGlB=tH|8IwxBvx+JGKp;JMawFAq ze<_lpJ|_^$O3UU-siHBJw*Up$l>wFYk7s?SQU;X=C_Yv+243d0bZv?EpTGQTyphn^ xbbAk?z3e}K`PXFsRmI<``&SkJH>!fnotfGEQ8q>P%|7r)?WX1pl+wMY{|od=45$DA diff --git a/docs/nemhandel-edelivery/oxalis-ER.png b/docs/nemhandel-edelivery/oxalis-ER.png deleted file mode 100644 index 48fb75be9d5239efd3d6f1d26ba7123a03455436..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 80058 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 diff --git a/docs/nemhandel-edelivery/oxalis-overview-packages.png b/docs/nemhandel-edelivery/oxalis-overview-packages.png deleted file mode 100644 index 8406992c4974d5e9d552906d57f6c442896ed7f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 42333 zcmeFZWmH_<+vW*LfZzdwTSAcFQn*zT+=B#ncXto&?wSM-?oOeFdvJGmDV#3yBu~H7 z@4wgdnl2zMNp9lkYHe7P{l+AC{m6a77 z5;ArI=j-CQ&Ee|_R&e;ZE7A(tne&RXl0gkPjs$X3hq_U|#e$O{U-7)<3D zKCoPWDFui9_uBV9qnH%@J1X9&VVUg=mJTAFeLr%pHkC!HcNx=P)l#ltbCm{EnrORZ z5DgPO?kak5w zlX01-I_*4w`q%>kwFT;DQ(8>`nKOb1m84G-caj84mJekPCYjD zB=Bb|ScEi?msd5C*|VlF_3C??rhblCU1Lw8dcu<~;a<_vQ2Q#z1f>8%7bXO*@4j#- z1~9&*oN&$KxLsKt_dEO;Uw?Y*DLwl#)mk4>KvnU3QXzohFeiV;fnCHB#q!o@3B~mn zt(1J3RtNHN8qAlg?k9HX+C*QPTahqsbI!8A0V)DSM(x>CgxcSQ)0flNhA#oNxJ@lKB zRaAeje++ZsgzJ=0%tCh;GMze4bM!IkWh`g_%b5`N%o1F))4m&$S$<$nXT*WpZ6-~1 z!yrm-4VLD&T9v$Kp1Y=X<+`Vx&LLfF@}04J_@?;z)z^dP$C_9}?Xfj^uBwm0y1l3G zPZKY5M0d*&=kRA50KnS_?T!LvhV<#aUqkWlwP9Ah*6zK(65S2cp=mVrklLkS^1b(l zOm++0xdzmbw)rb1+TGnTD}wO5A908pzu+VdMyg5tU-10@G;(VMUQzi9C zM0@v6O!SahTE;=AFE>j)h_7q*E5iViOJG-GThosGqPlv@@Fm!KXH^BQ1W)1&(-FV+ z?=0AoD&&8DTiY0=cPOBHz#Yodpv^QL4KnrsYq*8ulh%SKU^Xpu26z{IAc6cMvLf%U zJye;hoLm&?)kW07UysZU#_2XQ@+CeOVsa{)Ni46WCb2#CSRj+A8wrtWH8u0uoaJ{> zDb1Zt_sZ8LB0%A<@AC8)fZ4*4f^ z_hiu)n|w=7r`+A!OC?-rVU*-r@_R?$F?=TA<~C%0?Gq&pEW15=p-B^+nLSR$>7c{L zS6I}svbl+qKRNzLFQEQdO;t8*ZMiR#-jhUw}^{qo5PO^*7{pp52~b< z{HyL~mUVoFZd0*C8Jp_Sc`oy#V2`bbO`oap*Oefb(BM^y=K4DNfIaAIy3t1OGcYfi z#rY32V(KNmv=-X7#ZG=uL$tN3I7x6LY=)jG>&@^}#J$xjU5DTSgc}8QhgD5(BeCVz zJ{z_G<8*up05r27u5%agxftRa@QLQSS+Q0BgeI01%{)(j@r$Xaiw>{H-*msI`{{_Z zfJd9%cxPAh;JDDISgS^o{);=eUaoOynSZb1RHn_=c z>!NJYANvt|C)+V6mc`YbOrKv)G~v*B*{v!(Q;^`N*p#OVqF(oJ#K44j3QwWSoZf_3 z+$oP@%des>7S8(M!T8cnGWb=VbnG>|N@cEck{$oLt>e zdj^vt>@`GHSfZ;{iIt8tw9$+Kw4)nB5<9uOzoqdEk<>t-i|8phI_*7;o!4L+?7Plc zxzjiA4qLdLt~OIT-GGFLd0gVz8Cf;U=F!>hn>l{-y(Qznvc~UTEmkh=F0&fvA5v*+ zF?>^e%BX$56|F8QzN)*v6_#=&y4R}8Ibked*H4yP6x8-C2|Wa#J%4)$t#QJYZgGW%k(P$|fcMtVlBB&qCGr703J8bxV?c7*CsQ@BW7%*#uL z3}71<<94XDuPf z*VZ939PE@(6=o&!U?)lxioBu`J33%=noYOOIe>fA@ zkKC?8>V?j;@;M!XBc0p!@b%ZZZCkZ6YJ=olDlZ!w2-vX|38o^8Vks$!Lsb7%AbW-{ zyM&mw%goz^uEc+wXPOSrivLPz0~{G!!R{jfe``u3@?pHt{c|3kzXY3+=gG=|Ybhm~ zw@0yOm}Ntv=EToa^DIjH+tKHs)82P;MdfbEGkLK5{>ZDl8nG*hS_Y@Dy!HX6GJkU{ ze^5lk5cJIPe7~4w|5?vLL8$P)Zzb3!ExjcFr1--UzE_7GKfnFW{bVjOH4K`su(9KV zx&PF8F-jhS(&pf4&3#qOGJR17Ys}EFstNw6vDy4?ji7{{0MjDYKQK zsy|h73){&M=x8zO`Sbp9J8N>W#~R{Z$3c_;NiA}xf9uZEHx(I%8=)oVo?cTLVx@Im zrLWX~;x?`VFuI2jK;Ms6lyo<7<*b^!ETj#^K~Yl}>#r z%HoxSd&HTY(OKYi&0I0=G`ktTv<0^uvlrzmUUUA)_vagYc5klZW6Mcp3q-ze_{*Be z%F5O+_!Gqz58pNM8Y%vB8P*?Imha@Kerjp9rl3yASyqGZdQ!BSj*mX4@R73SWkw== zg;Rf7a)qhWFJm)xmBW*3rW(A&A6@>X3DKTbyc8#E+UcQ2M0vQp!*F0v@DkZ@BicBj z`jjGDK?OX->&rHwFk*IV$W*Rr;**w+czUuGy5m+4udUgi%@U6XJS7&sn#zu}GP!~G z>K`>L;Gxxai<|AB)TE9=d&dwCwhV2$;vZ+Eq$bbpNvCQhmtDN`YKw@Kr!HcR&tg5VeSt zKVEnMvC_EWCt>a}pWm;hXlW+WdiqN%KLnI@!8{5{O^xR@9-a9(uzuHvKh!MVoV?-j zB5m(31h#j$M)`-|C-XN_klJ6at`LXD19AfcHXU)vV_6lUmFd%1|GFWV6Jb#n`O?UU zPMfPQKt=66HjyA=EP_n6g%G|KRT%i$YxF~z=Jx3QB`axw>gpt%xwNr>b}0=fzEmAM zfn-1U+R{S14w6)>JFKUt4)gK(oi%&1bqSyMw^lRrq{d`H7(K+@b7S8?zU9vWKT$(% zbRlg;^eIt+{$cpOm8pb7mX>sQJnS1sQOtU_jb^$6H4)08At7?Ca{@ZN8QrM-r|ZMLc%n#Vy)p^%!y@cQudxhvV=f4XQYf#ln>hP2 zGcSn~h;5e=nb|7RqifFuX>O^h%g;^Bs>Cw1%?B4x!`ilMIuDY0hW&`WT95mg({bm!P7=LbEaYUGv>`_T1sXS4#6>6Z;BQT&ZiU@zVW=q zWqLdlEuo~aVLP?k9CXT-)~3gH28p;>ocJ_J4qN;aTcQ!t%I(z%CW-o{+~&rz3JBp_ z6$(j9*$#S?CVtA5IqRrr{FJyp#djZc`YDok%rCjUBeNvC`1-g={2Cl-cL`qD0awwA z+&>IXA1mU|I@ac`QZ+h1PR!|ULGsq_MkNoiMH4Z$zFQmYhNZ~l-AG5-1hdzN<1jiq z;}uQrOSlOO3XU|nob4n1DIw95(aFg2bB_GURBE*Mr~kP+<;EXXFD6s)QJlg|=P)ta z$`H06nYg(Leg{6`h>@Vbkun-V@M&I4DJMR#{g-*q&<~-pS|LQ^ zXpyMo=g(=G7hi)rN7>2%r3P}>9Mok@F%dFbC%tN$gOY{w_#IAeFAEwDCYRXGT0Mt5EviUCp;KPzW-U_uJIHq2n+@B>Xd!qC zeClF>@Qd$jZmhQ~9Ia8J7n|QNMM5+CkoBLum8?(Uc`FE3JfR#Ze&4Jbrn_`q6(FH< zZ8ZLVkzU@nw|_|6tgus00?|w*S-J%7<#EYC&!}0wIoyC*vO+HAnh4rDsJd|EKDlBYWi_c-MS`eYH!dgc2dvfY%*HDXoYBmPL8NHM-g|QCuwtpe)_QfRonwbf(De(A zo8qptc#r<=yFC1}hJ!PEk^QRS(c-NRp0MLmG18^6bJT}~tf{eimtMUJwsIU|;`H>D ze^#X+3za9I&Vh*a#l(iY(*s`CVwhfskDl`3>_~f#)+ZOBGnb6C*2k&bMJFonfku^) zT;xVtXn?3JA6R51s^v;gVg*ui)yFzVS@2|J&&_JMSDF!F*LwBUhDw~>EaGiy@b=fz zg5@gxu-GUEeuz5aJuucLw6 zs8bRyZG&gegM?Sb52N>ab6)+0!}YqYA#3s)&U)jro8S!H{SUaFs4jtniR1i&!KvmK zzDN*X3=mr8(xbMasQvUaRpW_8@b_Xic$KSO#N&-T?P2%dGG- zAFFh^4c#-wxI}spE}_Y$R2VX$mV9-~LO|jbxl=hjEBQlw@vJpM@u>)i_M)CBJ#hI5 z@O?Xui{Vm5ZmH3_>Bs)!@~2PisGnI@Ta|g*(NK7i;Sl|9m)nAqywjqb&)y8lm-ssR zurGOPBRbo5uWeaZm&bj+EpE(ITN6*ghAm!QpydX=^M>{HeeS3faWb_Ctk-2XykY49NLb&$qCBbrFWv`#z0qhGnO}&o!L2}(dr^Hg1;&XHU3$NfZK>7~HK;|6` zJbH%dTnNjeG+sE|dy@G~SO0f2pe69jDngcpVSC*?;&&0gdY`VdE<~EH1i`T%Q280x z9@kwbACuCGB$Xk2YNyq?ny7(v2WkOZKxm%nl0G7W1!Ml&8HGo8YyFP$g%D@0^U#;v z08i!g*@Q9{bW#ukH#j9y2`VA zZEB%VMjPLT+M%8^@1f_GF=oG5jVBv=;$`UdFkr|;`b$+Bf15vYVY0FT@g;|qWSvhz zA9jh#LYwl-#JK?dnJ?%Dt+xWneEH00KTuy=&891X{pGs&?Cx7uFIfHM37~!2SZd*% zZj6W;zJwf4HuJca=7*h9H`&P@u;HZgk37HAg(+Gt5MmoV&Q#M z3373RF16@?GVirHi}sc_8S~q>YkF^7J}utFQ9#2GI$M9{*_DGHKN9&}Pia2`f0O+C zm>6?OzCJkU6eU93>wu>@B>uUwCS);rt})+Skv)^H?*{L zu9Av7^G<~I&gp#A%C+J5xmr08EY%-)#irY*tl3}+b```e_(uOBHg zBb5cnv|Ghpt&D}8WYUI7w%q%cR|SWNc?4h?(Zjqi#%S8%?|o#th0}=Gk_Nq~5F}=A z@z;R7Vh71{Nv?PA$FtrUZ<7 zp&1)D6&;2Vy+LmMj#rlH-3bD~O3P$~;hN$w4IBu4)>)?P!1<88@Rlfx;`dMfPsRMG z-3Mq4B6%f7lnDq0{R|wt|H9$u$0){tiE8p%yaik!vx`Z@6i8c(&A$iuux-BzNjT5p_CWq4fR{vZm5hq%(!^>Oheo(Gp+kA3o`?@Yg#VSi3bgun z(hwB1H2OIjpqe05q#Vs#&2(bTNt>{w5!e4Y8=o)1<74?TjIeuy?>dc&4N=Ms19Dzd*KIYFDxa#YP!BPka{GV-m02Zv^8@njn1xwFY;Ib*uKw{az6vR zGbsP3!s#(2-4G~$LESuEqnBjuVZbnseq^*-q9Hk7?fdpy?04^f^NDQ%i781ZAqx2)-@| zW}9~(>-j(Os@;<&nL;fukT(Ybpd0oKFqeMh#-Viq2~VTwworv>a@#% z57O#R=|cOJycNhtX!RP4UCC`nN4# z-7ouJ@S=8KRW}1kBi&O@-kFhmeG}*HsUfyH8J&%!UN!c=T1vuXio!}yU~+Lloxa4# zQho7ivoX}N+Eb?SAQi4MbwKy7Dd06Hc3WIRdXj=6j}>;M1JV8%CqMh{T(%SM>LkXb z4$6tu-GEbbMrV@^-n$gij8;+S!sX?%$@mR>{B?iilQrNr4)KU3zGRZPyIhgsG(K~lbW8WtyJwzJDOdvG6E4yS%pG1T-09;UIoZ@z z7T-PCrvX!Xo1g8ltn$7tkxZ98+fe`l6Fs+kr+`!6ZxcQs&EQ)T@&wO%mY!bhb2rVk z{HHRvOQtY$64~jzSAjNt+N85=*s7E5aWEj}s(XgSHwzh#51CRXuNu5izXSs>V_H+# zUt7wMwJsu0?YLGVu#H3G!+F{sABcGqbLe#^F@?7K;51})yDK9oLjP7-DI=W6y z;@QMDfxdbG_trl>;m-H(eqs2rwtU(nZ&D*o44LMce_BWgzUVk@eGKmDcvITM7|c6x z-s1GAzo#(rhZoq&5xI5XzET4Q%?hDt{|xh*tGHt}L+g&U#8JD{dHe>pKXFd0osHC; zGC{^`%3m1G1hr^&)@S$cCpD+FANV-mZAQ-SyvC|YEb*4uEPuxL)Bhk52kh5DbS zGn(jiW6T6FZS$toYP?a@c680Do4B>f4b9AvDp%fo#``RkxWM+G49F=J>h z^0;to`I>LBt8Ess^O<&3|-j?w(C*KI#5U8-Hf z6}}b~w|LS^E+0Pmi*;bjD;9!bB}H~wE2CV&!mx(0NTu5JJ2MLY^JtrRfs)$p*;ca~PjZ2PoK5-w;rE{iA^PqQEzQ_&N?T6u%EzqNl$(72vOD(n;-X9J?n?O|M#J5}e z+O#u~@FKANwH05=h$^;CIoqo~g$UQdQb41n4O$xf5zagOE6u?wpk$8Hb`tam^RykY zLl1mL9YuiE8eu8L$v|{z)NNL?H&_kqtMT72oq2D$QPpsa!Zj=vl^TUYaiJ@{$M?M& zE@RLi%n^MHZ6n)UujCg`=0q#=;aK0NJs*EQ2<@mIq>$*sve(8V`;KA~Rd7ECWbPj6 z+b?0RZ)Qu-YrZ+P9=#nCSiOX^H07r`vWSd8IPhR(WOPg}e&ib+9sMmG_jow^=a(A2 z8h1yjwk0dL>?yOhToY$VF!3wJRlhSJ51ow5ISl$ypQ7N)6KgasAW&`mHE3N2hoO|P zyVkcwf38HHd>zudCkD(Q@0?`K>Zuu^aK$!F^{Ct9@bE~SL zC&yb=glh!IHAlaEZ6Xj!!59Us0Mw%8&eV^NtPK(Z5j6s@+~S&WkD6CnSg$ zsR$SML~5CH6@JKIb-4PyF@Hy}6fM_o)N==la7~5nk+-*NA-uYADLBykFEF94__?p1 zZgQm&_l3lU_OqTAem=CLfwl1u2k#dxNVL+qhv$)TlkdAtUG3io3RpVz2h1tKN$ru5 zoibGr&2&ZA&9d&V#LrI@1x>Eo?=k!j!69_p&T%*&GwhHx}fg-2dLP)0llGg?|Pi@ zR?@90HJXBGpq1+llwtmbGCdt%Aiz~SF>u73zy`G1I`{YE;b1y4{FutH zUvh=NHVkHQ8@Ha{wAUTG!z)6ZyKZqXa=eLHj+HMT0G7vda4V64+M)@t3`eu$7^S>| z3~!gQNOYC z9bfJ+e`v9iYGXz7HqSsPwgyoDF$btJ3Wys&0BuZcsmn_lRA-6KX^R(Uc1$J_Mz2nl z8loR3zi!;wmIwE-xxdnE zg?qIoppr6|jW`hGmlz8xz%L^{QK0XZv!{SrJM@1HG1notjngX&8)llce5o?`T4ei@ zMmMNuwJIfTpH~eCeognycIA~nJ1I1*oM4NBpNLcJH*@#!uxmu-68_EcdwG5n^u$pTay<4F)iH3oQnSSbd zhM?{<+YPv0yK{SJS05cHA%}^>(})0>YP;1ynf(_n87ISHL^pM{h!Sh=&PH+ZL2PI+ zklb+Eij|q}A?{%Lf)H)WhZ=-LfEPwA423xgIzYQ?b6)CS9O{SZ2os#f;~4NvlSIW8 zRIRBhH2qEMcxO5&HVeM8egox>*8(*iuzaU0^0Vn4ajzDa+;jr#7pd%Fn@1#W`P+ZE z+g{Q_Jxks*|7B}#6y_*jU9J6}2FMK@wMnOofs=Z=Q$*S*XP z#;~_Y`B{V9+m*C+PCU?8?S+|H%KByL%7_2^1$9zgg4o8bb@Lgrdc;+7>=eP6-Pv4H zAXEykqF0w>T3wb+-qgW}%fJ}udzZ&lb!w3A`+VcEF~Ui1D*A2xhqE}%$Bo)_=4DO) zh9fVb+9>g9(i8eTRVs(h`uR5s+dNr`6 zb{=2sq?fo^=;1%?W$G<&@=K;Hkr+KE$Bp+4ISDXvzbKC{cseCJ`4T#2KxcEm2M67>Wxx^%{RiQ;Ri?F~oHU{GP9@0mJT-QM~R%{v1;NXx-ch9LamFXUP(Jaz-Gxtnfl!)lpY2`Jic>K=i-%RTMTI zFs=*?QjyKT(=e*e`)c3H2vNbM|fj1Q~)dJD~3Ya;O`$h?`B zH0;lggqC#r@ux2YewG}xon(AZvjir>LJ?jWc=<{JcX65(msAEhuU&KNKFaBs?^T2r zfxJ2OPzDfQ7N;e=~yCGUa77%_1EFEX1sH;mxscw-w6(Ir2HRzT^g_Y@AN!SYHb}% zIQMCP@+eRlujmhWAe`nZsbLZ!$v0gXe=_^h*;PVxam%6Cp=Mw_w~!H%xxad^WM#$) z0upaU8rqhAXb9s(_otON`sL0&;k3G#uvKMia-!ybZ%5oZ|LH!D_f=T;EZxp7DBwYdz7HKjIgaw7dm`ln4Hp55^GVc8yxW0Q}cj~I8Fq3}_y z(RY*(wAl9A<^_|swRP)Z-`j`TTPlOS_^z2eO8%$<^hXY4RtO;!JtF2mNnlo2=7)x# z?g%My)gKDbGH)Jx90G@$fRDm$2EI=kTRdi-v$fZ*$9(kt?cmi!*@9xqeUn*7mEM#j zK$r-j+wW9l&uZt!kJtz>ds0-T5IEQyUppS+(Tyj<>_wS#^EN9y zN}AlQQA1+?(S6h{U&3*{Ln9>zX*5Pes%QKDQGziw6HAapzYi`HDx4%J58V$=kmXp* zTqhs?*5l4P{5QzeKq(@t624c%fSOp-C{N~1GsRXYvuv*=oBPcy`S(eGB{7gho+;kS z?sHr8+JnX!N7A6v29^C{-I%LV7)#@w(_Sqp(($}VXtA5D6O_tuT(j`B<|A$9< zo9de$RMAyM>&Widhp4Cx88!#iK+99{l2;cOD7q<5xr#bD6YDFnP~`_2ANEn6^d@ws zKdN173>6C$DF*Y52|MN(a|6QqKrQXZ%xb|hg~q4hx&zNIiI!8Jb1-;ikejcV)e(6A zqUQ3Ym9f9|L9Zd!b38|94S9?i7AblgeI5!}+J_828|*!#8*x`^+W^J-QXJfeosec^ zUk+GE@_EMXjAF2!vE@!FG~737G?ZI_ui`@qr>r@-!YATvJFs#27m5)4I)^siP!acr6v&Zw0hk!6{HJs|jk} z5E2nF`Ymy}cqf}yeNQmJpjGVDhQmY;tkk{UXsiLeHW7|A(E3cr>y43l_=dzA8eBaR z{rhEfHw>9K|2wHmt`iSa9l8k@36gmc8%k9IN6bucuqa#P=}d3Mqd^1jX# zv1OwUE{x_tVnvv2w^wW1c#t<5xdo=P6Sw?Uz}*OuS|%5*LTy z<;)B#c!)FB9NYl3goae!K{X0NaWxcgio;V4$(+hqqC5UD1}rTY?A`=9qh2wJS^tZhp?7%Lb^RS z6-oup=kmRAk9r_jAKD;(UNxAlLM6~CXaZCc^t=}<-}`!>o1ftUOU3{Fe0W>Kr!Qy zV!!ydAy~IoR~w9_7ZL9)m(?jB)I)tX0cq1!Fh*@VbcZ z%9_?{P35ynRC&3G&+?PaKXOzv{QUeOjw=}1L zikl+Z_({>OEj9KJVjsKcL>z6%OW0A}uGVB*VG(L$;ylY~QSa56wL90H*O(*Frtwkm zkK4u_3A--u-D zKUf!|*P&M^hiBc9<4F=FQ6?y-j4_#-nIH8xgy&JkEgt@1R(dkoUF?ow@d5Bg92{v2^U6$)=6y?V z^}y?nQ%?s4#c8lH#qTChPt#N|tg{`jTmoupw^kDvuZqUe?=GC4u}RFQ;jMkhi3Xhr z?>*xvA!Mp|wzZ2&5=b_DA!#ZMqs`wOO{#^iuRbLgcb$W-35i>c(6hRFrn?vU%@N}{ z9?jZP4c%iZTj1J#z-N{mM#Al-+&7nS^Y4$R!@V4&;0EKoKl;i&WRMWJb~|81^CWQq zifxThmJZ%#=Tbg0p}5b;PWZ3IL`-KOFm*}oeA`0z#Je|5IvL)y_4{Y$`A}St z4Rt_aX!GE!OQGFkxv2m=^!+u3RlXY1|H1}?INBDwoSo)j2O77^3?YM1xeM8KWpl%Z zwtzBuazIND zIvO4%9dVk$)(s`Rl2P3n&a_3$2P9yLX9pTbY0zYI)?!0_$~p`&?*k&Y&){YX@Z1F@L$4JS#FC)xly;`kolMKM zt19Nkn!!G6q6OHV&&Ex}{|6=Qf6IT=vZhN8QrkLRa}aGEDSNsz8FP;|2)v=P+y4#x zS%WG2m;K27lywqIyUAu~gSD%RvF&!aY+jM5<(SeL8hTC(Emp{!dM7b{dr^2-_i08w zx8fBzX5}|Qjmb`Hs<-KgRWf@gB&aSfssfrr`NV<3=iHtw2e+s~#;4;HDf1o(Ll#>s zjTWcXG)@;6@Ts?N3l>^L+s;Qu;`X2ykA<9 zrZU}0m3ZxTGJR&o7(R2rBp{DDAd6=xW@%Y8{I6SWZnq#gaz2Fck#Gg}IoGHOVyx1X zw*@KlPazncmd0&GLTKAGv=n%JvKx6^Mxw8y=aoBb*^7|gEXqeRSz^w3gQhmrT0$!4 zV1XV1LD{gstw(VlVwid{G|0{XI1Xn zapJKpi+0bJSOIsub#JHSacA+_G#>7U5g1^a6lIzj`kVL11caNGlo+kbKHPk>%R1mF&uLN_W{?jOW=e6OvdqVV8uxW{&4IdEw?@2!7~Tvhz#n=gPk;cmf_ptzs3C3p)|^xo^faHMYq%h zJ1@H@7hB#7wm-Uk*)p6kZkim6J^VhABsnE{Ht~pZ%VOUvk`WyZbyEt%gO zU5Din9nQ0#)biD(U56&Uc(0@9oCHg2LQijM59z$#w;7`PgvjIlM<9=+lfo$bPtc~B z1mD`T{N0zu2fvFsWX$Sa#NEjEd7m1dZehq*e!JkHY`ubsl%3EZUOc+MEfzOt&Rev! z)yzG^)yNWq6OO`fr+_%7uLXuw1lVc)0~kOoEF=GHXNLQhke(jA{r{vb6k#(ptf(J9 zGm7Y(9VkwIA`4!yXTZpHQ#nkfcR-^>cahx}lMN6pmg4`c7{jb`vR-K5FaTW&Ng0O6 z%Vum6oQMY(#SaS9#7R${GE7@^;@-)geB$~W00MkM-qv!@`VI4DwIq#v{P99?{X%(~ z(=Clnfnt~&d5#;393RwD{E&m9r0bY3+$bdPasz2=(wO&$aUz9xdtBjs^_K&7%A$r{x7Su7gD zEqskud}Pt}R6;}(Z9?}aJk>NQNHi{~F3)2##jAbh?*B0U`R>u@PlWP3xn;i;;GZnP zWAt;|F{003?+sZbg8-$XZVl~d92#=USR2@bGE>p-ZaO5Yk<{NQ&_r^Z>7ZsVWQ=PU z5@&Wz>-2reN@F|m=KJNvj*ascak)5JuztV+Uu*S*lcbr$96cQ2=o(~=dMfqK{-Ol? zhm?@S=bmCjRMo}izI^DS30g;?m|R~;jPvUnI&35MG&%qA2|-P3!8T}v9E5fk;1^<0 z)<0DDxXHQoi`jW(xH+iD!|q=d1c|ozX@8w9NZ;(YyEA^E3=?!OL1~ZQR+A;nd)GML zvw);oQ+sbTZY=Dm?l;`|vVr}gWpC;NYJ5u`cVeQQdJuDO8B_3BL%#8w%+p?l3av*S z7ekEJT&qUXnr5QIgI0PZlm67>HP*~+LB1jeDS<+tU4@xwBh%VP`Vt+`(BdACxEEbE z4r`v0#Q%qYaAs-dEz~xbjXh_}l8&dSiwo)}yBRN@ArB~(*|JZbVkuC!TIFV6?VCMN z595j~-<&(Y{YjgXW;vgE!)bLNb{c3-Tu8;mQNwU#zBgQZHY?i-W1W6)rKq1~fE3H` zK)%1poNto2a<;yd=|UY{+BT8Ve|;DpLSBU5UZ&Vxb}rrg-CqKPqI0#En$N8V*LH!l zz-Q3alnQ6jP}bN>2Oc&P{VI9k`Z3gu{&p9@AiI@R%gRneo8w7WZv4uWt!0_lyk zr_c);Y7n~J0&E-Z)!})!LfQy?|MAeNyYtegA-SbP1Qy#MA2>Zt;>N}1 LdCuG#t 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 dk.erst.oxalis oxalis - 1.1.1-cdcd75c57f1c18846d6950469e8f4fb53b3b8584 + 1.2.0-RC-482e9dc69c402ff235d9a34dba51310428093e10 dk.erst.oxalis oxalis-as4 - 1.1.1 + 1.2.0-RC jar Oxalis :: AS4 Extension adding Nemhandel e-Delivery AS4 support to Oxalis @@ -79,7 +79,7 @@ 2.23.4 4.7.7.Final - 5.11.1 + 5.13 3.1.8 @@ -421,10 +421,10 @@ 10.3 - + eu.europa.ec.joinup.sd-dss - dss-asic-xades + dss-xades ${dss.version} @@ -447,6 +447,12 @@ dss-crl-parser-stream ${dss.version} + + eu.europa.ec.joinup.sd-dss + dss-asic-xades + ${dss.version} + test + @@ -529,11 +535,11 @@ gson 2.10 - + - org.apache.maven.plugins - maven-javadoc-plugin - 3.5.0 + org.apache.maven + maven-artifact + 3.9.5 @@ -614,12 +620,6 @@ 2.9.0 test - - org.jetbrains - annotations - 23.0.0 - compile - @@ -637,19 +637,6 @@ jaxb2-maven-plugin 2.3 - - xjc - - xjc - - - - src/main/resources/META-INF/Schemas/transfer-delegation/transferDelegation.xsd - - dk.erst.oxalis.transferdelegation - true - - xjc-ubl @@ -659,7 +646,7 @@ src/main/resources/META-INF/Schemas/UBL_v2.1/maindoc/UBL-ApplicationResponse-2.1.xsd - false + true diff --git a/src/main/java/dk/erst/oxalis/as4/EDeliverySpecification.java b/src/main/java/dk/erst/oxalis/as4/EDeliverySpecification.java new file mode 100644 index 0000000..311a2b6 --- /dev/null +++ b/src/main/java/dk/erst/oxalis/as4/EDeliverySpecification.java @@ -0,0 +1,39 @@ +package dk.erst.oxalis.as4; + +/** + * Enumeration of the Nemhandel e-Delivery specification versions. + */ +public enum EDeliverySpecification { + /** + * Represents the Nemhandel e-Delivery specification version 1.2 + */ + NEMHANDEL_EDELIVERY_1_2("nemhandel-edelivery-1.2"); + + private final String value; + private EDeliverySpecification(String value) { + this.value = value; + } + + /** + * The value of the Nemhandel e-Delivery specification suitable for use in the Nemhandel e-Delivery document signature scope in the SBD. + * @return the value of the Nemhandel e-Delivery specification. + */ + public String value() { + return value; + } + + /** + * Finds the NemhandelEDeliverySpecification constant which matches the provided value. + * @param value The value to match to a NemhandelEDeliverySpecification. + * @return The NemhandelEDeliverySpecification constant which matches the value, or null if no matching constant is found + */ + public static EDeliverySpecification fromValue(String value) { + if(value == null) return null; + for(EDeliverySpecification spec : EDeliverySpecification.values()) { + if (spec.value().equals(value)) { + return spec; + } + } + return null; + } +} diff --git a/src/main/java/dk/erst/oxalis/as4/async/AsyncProcessingRunnable.java b/src/main/java/dk/erst/oxalis/as4/async/AsyncProcessingRunnable.java index 2a7b270..57cd71d 100644 --- a/src/main/java/dk/erst/oxalis/as4/async/AsyncProcessingRunnable.java +++ b/src/main/java/dk/erst/oxalis/as4/async/AsyncProcessingRunnable.java @@ -9,11 +9,9 @@ import dk.erst.oxalis.as4.async.response.MessageLevelResponseCreationException; import dk.erst.oxalis.as4.async.response.MessageLevelResponseFactory; import dk.erst.oxalis.as4.config.documenttype.*; import dk.erst.oxalis.as4.error.ErrorCodes; -import dk.erst.oxalis.as4.handlers.NemhandelPersister; import dk.erst.oxalis.as4.handlers.OutboundException; import dk.erst.oxalis.as4.handlers.SendSbdHandler; import dk.erst.oxalis.as4.handlers.dto.ValidationResult; -import dk.erst.oxalis.as4.mode.Mode; import dk.erst.oxalis.as4.persistence.model.*; import dk.erst.oxalis.as4.util.*; import dk.erst.oxalis.as4.validation.MessageValidator; @@ -21,7 +19,6 @@ import dk.erst.oxalis.as4.validation.ValidationException; import dk.erst.oxalis.as4.validation.ValidationType; import network.oxalis.api.model.Direction; import network.oxalis.vefa.peppol.common.lang.PeppolParsingException; -import network.oxalis.vefa.peppol.common.model.ParticipantIdentifier; import network.oxalis.vefa.peppol.sbdh.Ns; import org.apache.cxf.helpers.MapNamespaceContext; import org.slf4j.Logger; @@ -66,7 +63,7 @@ public class AsyncProcessingRunnable implements Runnable { /** * Performs the asynchronous processing of the message. - *
+ *
* Note: The whole asynchronous processing (including sending an MLR) runs in a single transaction. * If any exception occurs transaction will be rolled back and the message will be marked with an error status (in a separate transaction). */ @@ -98,9 +95,10 @@ public class AsyncProcessingRunnable implements Runnable { if(message != null) { logger.info("Marking message with status: {}", MessageStatus.ERROR); message.setStatus(MessageStatus.ERROR); - try(PrintWriter pw = new PrintWriter(new StringWriter())){ + StringWriter sw = new StringWriter(); + try(PrintWriter pw = new PrintWriter(sw)){ t.printStackTrace(pw); - message.setStacktrace(pw.toString()); + message.setStacktrace(sw.toString()); } em.merge(message); } else { @@ -154,23 +152,6 @@ public class AsyncProcessingRunnable implements Runnable { if (!result.hasErrors() && documentTypeConfig.isResponseType()) { handleMessageLevelResponse(em, message, document, documentTypeConfig); } - - Mode mode = injector.getInstance(Mode.class); - if (mode.isNemhandelEdeliveryMode() && !result.hasErrors()) { - DocumentSender documentSender = injector.getInstance(DocumentSender.class); - NemhandelPersister nemhandelPersister = injector.getInstance(NemhandelPersister.class); - - String receiverCVR = nemhandelPersister.getReceiverCvrNumber(ParticipantIdentifier.parse(message.getReceiver()).getIdentifier()); - - NemhandelLog.createMessageLog(em, message, ErrorCodes.C3_SIGNING_DOCUMENT_INIT); - NemhandelLog.createMessageLog(em, message, ErrorCodes.C3_CREATE_TRANSFER_DELEGATION_INIT); - byte[] sbd = message.getMessageContent().getData(); - byte[] signedSBD = documentSender.signMessage(sbd, sbd, receiverCVR, msgCtx); - message.getMessageContent().setData(signedSBD); - NemhandelLog.createMessageLog(em, message, ErrorCodes.C3_CREATE_TRANSFER_DELEGATION_DONE); - NemhandelLog.createMessageLog(em, message, ErrorCodes.C3_SIGNING_DOCUMENT_DONE); - - } } message = updateMessage(em, message, result); @@ -183,7 +164,7 @@ public class AsyncProcessingRunnable implements Runnable { /** * Creates and sends a Message Level Response (MLR) for the given message. Normally, a Peppol Message Level Response (MLR) is created. * In case the given message is OIOUBL 2.1, the Peppol MLR is converted to an OIOUBL 2.1 ApplicationResponse which is sent instead. - *

+ *

* Note: the actual sending uses the normal C2 sending process (including validation, signing, etc). Refer to {@link SendSbdHandler#handle(Account, String, String, SBDMessageContext)} for details. * * @param message The message for which to send a MLR. @@ -299,6 +280,7 @@ public class AsyncProcessingRunnable implements Runnable { if (originalMessage != null) { // If we know about the original document log into its document log, that we've received an MLR NemhandelLog.createMessageLog(em, originalMessage, ErrorCodes.C2_RECEIVES_MLR_OR_AR, ErrorCodes.C2_RECEIVES_MLR_OR_AR.formatErrorMessage(mlrMessage.getMessageUuid())); + mlrMessage.setOriginalMessage(originalMessage); String responseCode = XPathUtil.evaluateXPath(mlrDocument, xpath, mlrDocumentTypeConfig.getMessageLevelResponse().getResponseCodeLocation(), null); if(mlrDocumentTypeConfig.getMessageLevelResponse().getRejectionCodes().contains(responseCode)) { diff --git a/src/main/java/dk/erst/oxalis/as4/async/ExecutorServiceAsyncModule.java b/src/main/java/dk/erst/oxalis/as4/async/ExecutorServiceAsyncModule.java index 1dd4dc2..708134c 100644 --- a/src/main/java/dk/erst/oxalis/as4/async/ExecutorServiceAsyncModule.java +++ b/src/main/java/dk/erst/oxalis/as4/async/ExecutorServiceAsyncModule.java @@ -21,7 +21,7 @@ import java.util.concurrent.*; /** * Guice module which configures relevant bindings related to asynchronous processing of messages in C3 * using a {@link ExecutorService} thread-pool. - *
+ *
* Note: The module is a servlet module mainly to register the {@link ExecutorServiceLifecycleFilter} Servlet filter * for lifecycle management of the {@link ExecutorService}. */ diff --git a/src/main/java/dk/erst/oxalis/as4/async/ExecutorServiceConf.java b/src/main/java/dk/erst/oxalis/as4/async/ExecutorServiceConf.java index 0d77426..af0a3b6 100644 --- a/src/main/java/dk/erst/oxalis/as4/async/ExecutorServiceConf.java +++ b/src/main/java/dk/erst/oxalis/as4/async/ExecutorServiceConf.java @@ -6,7 +6,7 @@ import network.oxalis.api.settings.Title; /** * Enum constants for configuration properties relating to asynchronous processing. - * Intended for use with {@link network.oxalis.api.settings.Settings Settings}. + * Intended for use with {@link network.oxalis.api.settings.Settings Settings<ExecutorServiceConf>}. */ @Title("Executor Service Settings") public enum ExecutorServiceConf { diff --git a/src/main/java/dk/erst/oxalis/as4/async/ExecutorServiceLifecycleFilter.java b/src/main/java/dk/erst/oxalis/as4/async/ExecutorServiceLifecycleFilter.java index 8a08e30..3fb9e3e 100644 --- a/src/main/java/dk/erst/oxalis/as4/async/ExecutorServiceLifecycleFilter.java +++ b/src/main/java/dk/erst/oxalis/as4/async/ExecutorServiceLifecycleFilter.java @@ -61,7 +61,7 @@ public class ExecutorServiceLifecycleFilter implements Filter { * Destroys the filter and shuts down the {@link ExecutorService} managed by this filter. * The filter attempts instructs the ExecutorService to shut down and waits for the configured * time for a graceful shutdown (See {@link ExecutorServiceConf#GRACEFUL_SHUTDOWN_TIMEOUT}). - *
+ *
* If the graceful shutdown did not occur within the configured time the filter will try to force shutdown the ExecutorService. */ @Override diff --git a/src/main/java/dk/erst/oxalis/as4/async/response/MessageLevelResponseFactoryImpl.java b/src/main/java/dk/erst/oxalis/as4/async/response/MessageLevelResponseFactoryImpl.java index 9304263..044ad1a 100644 --- a/src/main/java/dk/erst/oxalis/as4/async/response/MessageLevelResponseFactoryImpl.java +++ b/src/main/java/dk/erst/oxalis/as4/async/response/MessageLevelResponseFactoryImpl.java @@ -260,7 +260,11 @@ public class MessageLevelResponseFactoryImpl implements MessageLevelResponseFact resp.setResponseCode(codeType); DescriptionType desc = new DescriptionType(); - desc.setValue(String.format("%s: %s", l.getCode(), l.getValidationLineReference())); + String msg = l.getDescription(); + if(!msg.startsWith(l.getCode() + ":")) { + msg = String.format("%s: %s", l.getCode(), l.getDescription()); + } + desc.setValue(msg); resp.getDescription().add(desc); StatusType status = new StatusType(); diff --git a/src/main/java/dk/erst/oxalis/as4/config/documenttype/DocumentTypeConfig.java b/src/main/java/dk/erst/oxalis/as4/config/documenttype/DocumentTypeConfig.java index c0ce152..456bc1d 100644 --- a/src/main/java/dk/erst/oxalis/as4/config/documenttype/DocumentTypeConfig.java +++ b/src/main/java/dk/erst/oxalis/as4/config/documenttype/DocumentTypeConfig.java @@ -24,6 +24,8 @@ public class DocumentTypeConfig { private MessageLevelResponseType messageLevelResponseType; @Optional + private boolean preloadSchema; + @Optional private boolean preloadSchematron; @Optional private boolean responseType; @@ -229,8 +231,8 @@ public class DocumentTypeConfig { } /** - * Sets whether this document type should be preloaded and cached during startup. - * @param preloadSchematron true if the document type should be preloaded, false otherwise + * Sets whether the schematron documents for this document type should be preloaded and cached during startup. + * @param preloadSchematron true if the schematron documents should be preloaded, false otherwise */ public void setPreloadSchematron(boolean preloadSchematron) { this.preloadSchematron = preloadSchematron; @@ -270,4 +272,23 @@ public class DocumentTypeConfig { public void setMessageLevelResponse(MessageLevelResponseConfig messageLevelResponse) { this.messageLevelResponse = messageLevelResponse; } + + /** + * Gets the boolean indicating if the XSD schema for this document type should be preloaded and cached during startup. + * + * Note: Even if the schema for a document type is not preloaded, it will still be loaded and cached on first use. + * + * @return true if the XSD schema should be preloaded, false otherwise + */ + public boolean isPreloadSchema() { + return preloadSchema; + } + + /** + * Sets whether the XSD schema for this document type should be preloaded and cached during startup. + * @param preloadSchema true if the XSD schema for this document type should be preloaded, false otherwise + */ + public void setPreloadSchema(boolean preloadSchema) { + this.preloadSchema = preloadSchema; + } } diff --git a/src/main/java/dk/erst/oxalis/as4/config/documenttype/DocumentTypeConfigResolverImpl.java b/src/main/java/dk/erst/oxalis/as4/config/documenttype/DocumentTypeConfigResolverImpl.java index b550a50..4f19741 100644 --- a/src/main/java/dk/erst/oxalis/as4/config/documenttype/DocumentTypeConfigResolverImpl.java +++ b/src/main/java/dk/erst/oxalis/as4/config/documenttype/DocumentTypeConfigResolverImpl.java @@ -18,7 +18,7 @@ import java.util.regex.Pattern; * Identifies the document type of a {@link Document} and resolves the configuration for that document type. * The known/supported document types is the set of {@link DocumentTypeConfig} provided in the constructor (and is normally resolved by Guice) * See {@link DocumentTypeModule} for the configuration of the various document types in Guice. - *
+ *
* Generally the document type is determined by the local name and namespace of the root element of the SBD payload as well as one of more * "identifier discriminators" which are a list of XPath expressions and a expected results (regex). Usually these match on the value in the CustomizationID element of the payload (but could be any value which can be extracted through XPath). */ diff --git a/src/main/java/dk/erst/oxalis/as4/error/ErrorCodes.java b/src/main/java/dk/erst/oxalis/as4/error/ErrorCodes.java index efaa9de..f8513d5 100644 --- a/src/main/java/dk/erst/oxalis/as4/error/ErrorCodes.java +++ b/src/main/java/dk/erst/oxalis/as4/error/ErrorCodes.java @@ -54,59 +54,15 @@ public enum ErrorCodes { * Error relating to SMP lookup when no endpoint is found for a given participant identifier and document type. */ OUTBOUND_SMP_NOT_FOUND_ERROR(Level.ERROR, Component.SENDER, Process.OUTBOUND, 5, "No endpoint found during SMP lookup."), - - /** - * Error relating to missing Nemhandel e-Delivery signature in the Standard Business Document header (Sender - C2). - */ - SENDER_EDELIVERY_SIGNATURE_MISSING(Level.ERROR, Component.SENDER, Process.VALIDATION, 1, "No Nemhandel e-Delivery document signature found at level %d"), - /** - * Error relating to invalid Nemhandel e-Delivery signature (Sender - C2). - */ - SENDER_EDELIVERY_SIGNATURE_INVALID(Level.ERROR, Component.SENDER, Process.VALIDATION, 2, "Invalid Nemhandel e-Delivery document signature at level %d: %s"), - /** - * Error relating to missing standard-business-document.xml file as part of the signed documents in the Nemhandel e-Delivery signature (Sender - C2). - */ - SENDER_EDELIVERY_MISSING_SBD_IN_ASIC(Level.ERROR, Component.SENDER, Process.VALIDATION, 3, "No standard-business-document.xml file found in Nemhandel e-Delivery document signature at level %d"), - /** - * Error relating to mismatch between the standard-business-document.xml file in the Nemhandel e-Delivery signature and the standard business document in which the signature is embedded (Sender - C2). - */ - SENDER_EDELIVERY_SBD_DOES_NOT_MATCH_ASIC(Level.ERROR, Component.SENDER, Process.VALIDATION, 4, "Standard Business Document does not match the standard-business-document.xml in the Nemhandel e-Delivery document signature at level %d. The document may have been altered by a third party without re-signing."), - /** - * Error relating to the number of Nemhandel e-Delivery signatures in a document (Sender - C2). - */ - SENDER_EDELIVERY_TOO_MANY_SIGNATURES(Level.ERROR, Component.SENDER, Process.VALIDATION, 5, "Found %d Nemhandel e-Delivery document signatures at level %d. Only one document signature is allowed per level."), - /** - * Error relating to missing transfer-delegation.xml file as part of the signed documents in the Nemhandel e-Delivery signature (Sender - C2). - */ - SENDER_EDELIVERY_MISSING_TRANSFER_DELEGATION(Level.ERROR, Component.SENDER, Process.VALIDATION, 6, "No Nemhandel e-Delivery transfer delegation found in document signature at level %d"), - /** - * Error relating to the contents of the original-standard-business-document.xml in the Nemhandel e-Delivery signature not being a valid standard business document (Sender - C2). - */ - SENDER_EDELIVERY_ORIGINAL_SBD_IN_ASIC_IS_NOT_SBD(Level.ERROR, Component.SENDER, Process.VALIDATION, 7, "original-standard-business-document.xml is not a StandardBusinessDocument in Nemhandel e-Delivery document signature at level %d"), - /** - * Error regarding an unexpected receiver CVR in the transfer delegation of the Nemhandel e-Delivery signature (Sender - C2). - */ - SENDER_EDELIVERY_TRANSFER_DELEGATION_INVALID_RECEIVER_CVR(Level.ERROR, Component.SENDER, Process.VALIDATION, 8, "The transfer delegation's Receiver CVR %s does not match the expected CVR %s at level %d"), - /** - * Error regarding an unexpected SchemeID for the receiver CVR of the transfer delegation of the Nemhandel e-Delivery signature (Sender - C2). - */ - SENDER_EDELIVERY_TRANSFER_DELEGATION_INVALID_RECEIVER_SCHEME(Level.ERROR, Component.SENDER, Process.VALIDATION, 9, "The transfer delegation's Receiver CVR SchemeID %s does not match the expected SchemeID %s at level %d"), - /** - * Error regarding an unexpected SchemeID for the sender CVR of the transfer delegation of the Nemhandel e-Delivery signature (Sender - C2). - */ - SENDER_EDELIVERY_TRANSFER_DELEGATION_INVALID_SENDER_SCHEME(Level.ERROR, Component.SENDER, Process.VALIDATION, 10, "The transfer delegation's Sender CVR SchemeID %s does not match the expected SchemeID %s at level %d"), /** - * Error regarding an unexpected sender CVR in the transfer delegation of the Nemhandel e-Delivery signature (Sender - C2). + * Error relating to receiver not registrered correctly in NHR. */ - SENDER_EDELIVERY_TRANSFER_DELEGATION_INVALID_SENDER_CVR(Level.ERROR, Component.SENDER, Process.VALIDATION, 11, "The transfer delegation's Sender CVR %s does not match the expected CVR %s at level %d"), + OUTBOUND_RECEIVER_DOES_NOT_EXIST_ERROR(Level.ERROR, Component.SENDER, Process.OUTBOUND, 6, "The receiver '%s' does not support document type '%s' according to the SMP lookup."), /** - * Error regarding a missing sender CVR in the transfer delegation of the Nemhandel e-Delivery signature (Sender - C2). + * Error relating to sender not registrered correctly in NHR. */ - SENDER_EDELIVERY_TRANSFER_DELEGATION_MISSING_SENDER_CVR(Level.ERROR, Component.SENDER, Process.VALIDATION, 12, "Missing Sender CVR in the transfer delegation at level %d"), - /** - * Error regarding a missing receiver CVR in the transfer delegation of the Nemhandel e-Delivery signature (Sender - C2). - */ - SENDER_EDELIVERY_TRANSFER_DELEGATION_MISSING_RECEIVER_CVR(Level.ERROR, Component.SENDER, Process.VALIDATION, 13, "Missing Receiver CVR in the transfer delegation at level %d"), + OUTBOUND_SENDER_DOES_NOT_EXIST_ERROR(Level.ERROR, Component.SENDER, Process.OUTBOUND, 7, "The sender '%s' does not support process '%s' document type '%s' according to the SMP lookup."), + /** * Schematron validation error (Sender - C2). */ @@ -129,59 +85,6 @@ public enum ErrorCodes { SENDER_MISSING_COUNTRY_C1_SCOPE_C2(Level.ERROR, Component.SENDER, Process.VALIDATION, 19, "Missing or invalid COUNTRY_C1 business scope"), SENDER_MISSING_COUNTRY_C1_SCOPE_C3(Level.ERROR, Component.RECEIVER, Process.VALIDATION, 19, "Missing or invalid COUNTRY_C1 business scope"), - - /** - * Error relating to missing Nemhandel e-Delivery signature in the Standard Business Document header (Receiver - C3). - */ - RECEIVER_EDELIVERY_SIGNATURE_MISSING(Level.ERROR, Component.RECEIVER, Process.VALIDATION, 1, "No Nemhandel e-Delivery document signature found at level %d"), - /** - * Error relating to invalid Nemhandel e-Delivery signature (Receiver - C3). - */ - RECEIVER_EDELIVERY_SIGNATURE_INVALID(Level.ERROR, Component.RECEIVER, Process.VALIDATION, 2, "Invalid Nemhandel e-Delivery document signature at level %d: %s"), - /** - * Error relating to missing standard-business-document.xml file as part of the signed documents in the Nemhandel e-Delivery signature (Receiver - C3). - */ - RECEIVER_EDELIVERY_MISSING_SBD_IN_ASIC(Level.ERROR, Component.RECEIVER, Process.VALIDATION, 3, "No standard-business-document.xml file found in Nemhandel e-Delivery document signature at level %d"), - /** - * Error relating to mismatch between the standard-business-document.xml file in the Nemhandel e-Delivery signature and the standard business document in which the signature is embedded (Receiver - C3). - */ - RECEIVER_EDELIVERY_SBD_DOES_NOT_MATCH_ASIC(Level.ERROR, Component.RECEIVER, Process.VALIDATION, 4, "Standard Business Document does not match the standard-business-document.xml in the Nemhandel e-Delivery document signature at level %d. The document may have been altered by a third party without re-signing."), - /** - * Error relating to the number of Nemhandel e-Delivery signatures in a document (Receiver - C3). - */ - RECEIVER_EDELIVERY_TOO_MANY_SIGNATURES(Level.ERROR, Component.RECEIVER, Process.VALIDATION, 5, "Found %d Nemhandel e-Delivery document signatures at level %d. Only one document signature is allowed per level."), - /** - * Error relating to missing transfer-delegation.xml file as part of the signed documents in the Nemhandel e-Delivery signature (Receiver - C3). - */ - RECEIVER_EDELIVERY_MISSING_TRANSFER_DELEGATION(Level.ERROR, Component.RECEIVER, Process.VALIDATION, 6, "No Nemhandel e-Delivery transfer delegation found in document signature at level %d"), - /** - * Error relating to the contents of the original-standard-business-document.xml in the Nemhandel e-Delivery signature not being a valid standard business document (Receiver - C3). - */ - RECEIVER_EDELIVERY_ORIGINAL_SBD_IN_ASIC_IS_NOT_SBD(Level.ERROR, Component.RECEIVER, Process.VALIDATION, 7, "original-standard-business-document.xml is not a StandardBusinessDocument in Nemhandel e-Delivery document signature at level %d"), - /** - * Error regarding an unexpected receiver CVR in the transfer delegation of the Nemhandel e-Delivery signature (Receiver - C3). - */ - RECEIVER_EDELIVERY_TRANSFER_DELEGATION_INVALID_RECEIVER_CVR(Level.ERROR, Component.RECEIVER, Process.VALIDATION, 8, "The transfer delegation's Receiver CVR %s does not match the expected CVR %s at level %d"), - /** - * Error regarding an unexpected SchemeID for the receiver CVR of the transfer delegation of the Nemhandel e-Delivery signature (Receiver - C3). - */ - RECEIVER_EDELIVERY_TRANSFER_DELEGATION_INVALID_RECEIVER_SCHEME(Level.ERROR, Component.RECEIVER, Process.VALIDATION, 9, "The transfer delegation's Receiver CVR SchemeID %s does not match the expected SchemeID %s at level %d"), - /** - * Error regarding an unexpected SchemeID for the sender CVR of the transfer delegation of the Nemhandel e-Delivery signature (Receiver - C3). - */ - RECEIVER_EDELIVERY_TRANSFER_DELEGATION_INVALID_SENDER_SCHEME(Level.ERROR, Component.RECEIVER, Process.VALIDATION, 10, "The transfer delegation's Sender CVR SchemeID %s does not match the expected SchemeID %s at level %d"), - /** - * Error regarding an unexpected sender CVR in the transfer delegation of the Nemhandel e-Delivery signature (Receiver - C3). - */ - RECEIVER_EDELIVERY_TRANSFER_DELEGATION_INVALID_SENDER_CVR(Level.ERROR, Component.RECEIVER, Process.VALIDATION, 11, "The transfer delegation's Sender CVR %s does not match the expected CVR %s at level %d"), - /** - * Error regarding a missing sender CVR in the transfer delegation of the Nemhandel e-Delivery signature (Receiver - C3). - */ - RECEIVER_EDELIVERY_TRANSFER_DELEGATION_MISSING_SENDER_CVR(Level.ERROR, Component.RECEIVER, Process.VALIDATION, 12, "Missing Sender CVR in the transfer delegation at level %d"), - /** - * Error regarding a missing receiver CVR in the transfer delegation of the Nemhandel e-Delivery signature (Receiver - C3). - */ - RECEIVER_EDELIVERY_TRANSFER_DELEGATION_MISSING_RECEIVER_CVR(Level.ERROR, Component.RECEIVER, Process.VALIDATION, 13, "Missing Receiver CVR in the transfer delegation at level %d"), /** * Schematron validation error (Receiver - C3). */ @@ -203,31 +106,6 @@ public enum ErrorCodes { * Error regarding null or empty standard business document to sign with a Nemhandel e-Delivery signature. */ SIGNING_EDELIVERY_SBD_EMPTY_OR_NULL(Level.ERROR, Component.REF, Process.SIGNING, 1,"Standard Business Document is null or empty"), - /** - * Error regarding null or empty transfer delegation when signing a document with a Nemhandel e-Delivery signature. - */ - SIGNING_EDELIVERY_TRANSFER_DELEGATION_EMPTY_OR_NULL(Level.ERROR, Component.REF, Process.SIGNING, 2,"Transfer delegation is null or empty"), - /** - * Error relating to missing sender CVR when creating a transfer delegation. - */ - SIGNING_EDELIVERY_TRANSFER_DELEGATION_EMPTY_OR_NULL_SENDER_CVR(Level.ERROR, Component.REF, Process.SIGNING, 3, "Missing Sender CVR in the transfer delegation."), - /** - * Error relating to missing receiver CVR when creating a transfer delegation. - */ - SIGNING_EDELIVERY_TRANSFER_DELEGATION_EMPTY_OR_NULL_RECEIVER_CVR(Level.ERROR, Component.REF, Process.SIGNING, 4, "Missing Receiver CVR in the transfer delegation."), - /** - * Error relating to marshalling of transfer delegation to XML. - */ - SIGNING_EDELIVERY_TRANSFER_DELEGATION_MARSHALLING_ERROR(Level.ERROR, Component.REF, Process.SIGNING, 5, "Could not marshal transfer delegation."), - /** - * Error relating to serializing an ASiC container as a byte array. - */ - SIGNING_EDELIVERY_ASIC_CONTAINER_TRANSFORMING_ERROR(Level.ERROR, Component.REF, Process.SIGNING, 6, "Could not convert ASiC container to Byte[]."), - /** - * Error regarding null or empty original standard business document to sign with a Nemhandel e-Delivery signature. - */ - SIGNING_EDELIVERY_ORIGINAL_SBD_EMPTY_OR_NULL(Level.ERROR, Component.REF, Process.SIGNING, 7,"Original Standard Business Document is null or empty."), - /** * Error relating to missing SchemeID in a participant identifier when creating a Message Level Response. @@ -279,22 +157,15 @@ public enum ErrorCodes { * Information message regarding the end of Nemhandel e-Delivery signature validation in C2. */ C2_SIGNATURE_VALIDATION_DONE(Level.INFORMATION, Component.SENDER, Process.OUTBOUND, 121, "C2 is done with signature validation on document"), - /** - * Information message regarding the start of creating a transfer delegation in C2. - */ - C2_CREATE_TRANSFER_DELEGATION_INIT(Level.INFORMATION, Component.SENDER, Process.OUTBOUND, 200, "C2 is starting creation of OVERDRAGELSEFORHOLD for document"), - /** - * Information message regarding the end of creating a transfer delegation in C2. - */ - C2_CREATE_TRANSFER_DELEGATION_DONE(Level.INFORMATION, Component.SENDER, Process.OUTBOUND, 201, "C2 is done with the creation of OVERDRAGELSEFORHOLD for document"), + /** * Information message regarding the start of signing a document in C2. */ - C2_SINGNING_DOCUMENT_INIT(Level.INFORMATION, Component.SENDER, Process.OUTBOUND, 210, "C2 is starting signing the document"), + C2_SIGNING_DOCUMENT_INIT(Level.INFORMATION, Component.SENDER, Process.OUTBOUND, 210, "C2 is starting signing the document"), /** * Information message regarding the end of signing a document in C2. */ - C2_SINGNING_DOCUMENT_DONE(Level.INFORMATION, Component.SENDER, Process.OUTBOUND, 211, "C2 is done with signing the document"), + C2_SIGNING_DOCUMENT_DONE(Level.INFORMATION, Component.SENDER, Process.OUTBOUND, 211, "C2 is done with signing the document"), /** * Information message regarding the start of a SMP lookup in C2. */ @@ -351,22 +222,7 @@ public enum ErrorCodes { * Information message regarding the end of Nemhandel e-Delivery signature validation in C3. */ C3_SIGNATURE_VALIDATION_DONE(Level.INFORMATION, Component.RECEIVER, Process.INBOUND, 121, "C3 is done with the signature validation on the Document"), - /** - * Information message regarding the start of creating a transfer delegation in C3. - */ - C3_CREATE_TRANSFER_DELEGATION_INIT(Level.INFORMATION, Component.RECEIVER, Process.INBOUND, 200, "C3 is starting creation of OVERDRAGELSEFORHOLD for document"), - /** - * Information message regarding the end of creating a transfer delegation in C3. - */ - C3_CREATE_TRANSFER_DELEGATION_DONE(Level.INFORMATION, Component.RECEIVER, Process.INBOUND, 201, "C3 is done with the creation of OVERDRAGELSEFORHOLD for document"), - /** - * Information message regarding the start of signing a document in C3. - */ - C3_SIGNING_DOCUMENT_INIT(Level.INFORMATION, Component.RECEIVER, Process.INBOUND, 210,"C3 has started signing the document"), - /** - * Information message regarding the end of signing a document in C3. - */ - C3_SIGNING_DOCUMENT_DONE(Level.INFORMATION, Component.RECEIVER, Process.INBOUND, 211, "C3 is done with signing the document"), + /** * Information message regarding the start of fetching message content of document for C4 through inbox the API. */ @@ -400,6 +256,39 @@ public enum ErrorCodes { */ C3_RECEIVES_DOCUMENT_WITH_RECEIVER_NOT_KNOWN(Level.ERROR, Component.RECEIVER, Process.OUTBOUND, 123, "Receiver %s is not recognized by the receiving endpoint. The document was rejected by the receiving endpoint"), + /** + * Error relating to missing Nemhandel e-Delivery signature in the Standard Business Document header. + */ + SENDER_EDELIVERY_SIGNATURE_MISSING(Level.ERROR, Component.SENDER, Process.VALIDATION, 20, "No Nemhandel e-Delivery document signature found"), + /** + * Error relating to invalid Nemhandel e-Delivery signature. + */ + SENDER_EDELIVERY_SIGNATURE_INVALID(Level.ERROR, Component.SENDER, Process.VALIDATION, 21, "Invalid Nemhandel e-Delivery document signature: %s"), + /** + * Error relating to number of documents signatures found. + */ + SENDER_EDELIVERY_TOO_MANY_SIGNATURES(Level.ERROR, Component.SENDER, Process.VALIDATION, 22, "Found %d Nemhandel e-Delivery document signatures. Only one document signature is allowed."), + /** + * Error relating to missing or invalid document signature version. + */ + SENDER_EDELIVERY_SIGNATURE_INVALID_SPECIFICATION_VERSION(Level.ERROR, Component.SENDER, Process.VALIDATION, 23, "Missing or invalid Nemhandel e-Delivery specification version in Nemhandel e-Delivery document signature scope. Accepted values are %s"), + + /** + * Error relating to missing Nemhandel e-Delivery signature in the Standard Business Document header. + */ + RECEIVER_EDELIVERY_SIGNATURE_MISSING(Level.ERROR, Component.RECEIVER, Process.VALIDATION, 20, "No Nemhandel e-Delivery document signature found"), + /** + * Error relating to invalid Nemhandel e-Delivery signature. + */ + RECEIVER_EDELIVERY_SIGNATURE_INVALID(Level.ERROR, Component.RECEIVER, Process.VALIDATION, 21, "Invalid Nemhandel e-Delivery document signature: %s"), + /** + * Error relating to number of documents signatures found. + */ + RECEIVER_EDELIVERY_TOO_MANY_SIGNATURES(Level.ERROR, Component.RECEIVER, Process.VALIDATION, 22, "Found %d Nemhandel e-Delivery document signatures. Only one document signature is allowed."), + /** + * Error relating to missing or invalid document signature version. + */ + RECEIVER_EDELIVERY_SIGNATURE_INVALID_SPECIFICATION_VERSION(Level.ERROR, Component.RECEIVER, Process.VALIDATION, 23, "Missing or invalid Nemhandel e-Delivery specification version in Nemhandel e-Delivery document signature scope. Accepted values are %s"), ; private final Level level; diff --git a/src/main/java/dk/erst/oxalis/as4/error/Process.java b/src/main/java/dk/erst/oxalis/as4/error/Process.java index f098c5c..fe54e2f 100644 --- a/src/main/java/dk/erst/oxalis/as4/error/Process.java +++ b/src/main/java/dk/erst/oxalis/as4/error/Process.java @@ -20,7 +20,7 @@ public enum Process { */ FOLDER_MONITOR_SENDER(22), /** - * The signing process including creation of transfer delegations and creation of Nemhandel e-Delivery document signatures. + * The signing process and creation of Nemhandel e-Delivery document signatures. */ SIGNING(23), /** diff --git a/src/main/java/dk/erst/oxalis/as4/handlers/GetInboxMessagesHandler.java b/src/main/java/dk/erst/oxalis/as4/handlers/GetInboxMessagesHandler.java index d3e9f44..31783ca 100644 --- a/src/main/java/dk/erst/oxalis/as4/handlers/GetInboxMessagesHandler.java +++ b/src/main/java/dk/erst/oxalis/as4/handlers/GetInboxMessagesHandler.java @@ -89,7 +89,7 @@ public class GetInboxMessagesHandler { * } * * @param uuid the uuid of the message - * @param request the status to update to (either RECEIVED or CREATED) + * @param request the status to update to (either {@code RECEIVED} or {@code CREATED}) * @param account the account of the requester */ public void updateStatus(String uuid, UpdateStatusRequest request, Account account) { diff --git a/src/main/java/dk/erst/oxalis/as4/handlers/GetMessageLogHandler.java b/src/main/java/dk/erst/oxalis/as4/handlers/GetMessageLogHandler.java index e907a46..bc51118 100644 --- a/src/main/java/dk/erst/oxalis/as4/handlers/GetMessageLogHandler.java +++ b/src/main/java/dk/erst/oxalis/as4/handlers/GetMessageLogHandler.java @@ -14,7 +14,7 @@ import java.util.stream.Collectors; /** * A handler class for fetching MessageLog records associated with a given message. - *
+ *
* The handler fetches MessageLog entities for a specific message identified by a UUID, account and direction. * It throws an {@link OutboundException} if no corresponding message logs are found. */ diff --git a/src/main/java/dk/erst/oxalis/as4/handlers/GetOutboxMessagesHandler.java b/src/main/java/dk/erst/oxalis/as4/handlers/GetOutboxMessagesHandler.java index 83d1ade..8433c8a 100644 --- a/src/main/java/dk/erst/oxalis/as4/handlers/GetOutboxMessagesHandler.java +++ b/src/main/java/dk/erst/oxalis/as4/handlers/GetOutboxMessagesHandler.java @@ -2,8 +2,11 @@ package dk.erst.oxalis.as4.handlers; import com.google.inject.Inject; import com.google.inject.Provider; +import dk.erst.oxalis.as4.error.ErrorCodes; import dk.erst.oxalis.as4.handlers.dto.ListMessageResponse; +import dk.erst.oxalis.as4.handlers.dto.MessageAndResponses; import dk.erst.oxalis.as4.handlers.dto.MessageModel; +import dk.erst.oxalis.as4.persistence.model.AbstractEntity; import dk.erst.oxalis.as4.persistence.model.Account; import dk.erst.oxalis.as4.persistence.model.Message; import dk.erst.oxalis.as4.persistence.model.MessageDirection; @@ -15,12 +18,14 @@ import org.slf4j.LoggerFactory; import javax.persistence.EntityManager; import javax.persistence.NoResultException; import javax.ws.rs.NotFoundException; +import java.io.ByteArrayInputStream; +import java.io.InputStream; import java.util.List; import java.util.stream.Collectors; /** * A handler class for fetching outbox messages associated with a given account. - *
+ *
* This handler fetches 'Message' entities from the database for a specific account and message direction (outbound). * It supports pagination with a fixed page size of 20. */ @@ -94,6 +99,39 @@ public class GetOutboxMessagesHandler { return model; } + /** + * Returns the message with the given UUID from the outbox along with any received responses (from the inbox) relating to the message. + * @param account The account of the requester + * @param uuid The UUID of the message + * @return Information regarding the message and any received responses. + */ + public MessageAndResponses getMessageAndAllResponses(Account account, String uuid) { + Message m; + try { + m = em.get().createQuery("FROM Message m WHERE m.direction = :direction AND m.messageUuid = :uuid AND m.account = :account", Message.class) + .setParameter("direction", MessageDirection.OUT) + .setParameter("account", account) + .setParameter("uuid", uuid) + .getSingleResult(); + } catch (NoResultException e) { + throw new NotFoundException(); + } + + MessageAndResponses result = new MessageAndResponses(); + result.setOriginalDocument(MessageModel.fromMessage(m)); + + List responses = em.get().createQuery("FROM Message r WHERE r.originalMessage = :original AND r.account = :account order by r.received, r.id", Message.class) + .setParameter("account", account) + .setParameter("original", m) + .getResultStream() + .map(MessageModel::fromMessage) + .collect(Collectors.toList()); + + result.setResponses(responses); + + return result; + } + /** * Gets the status of a message. * @@ -129,4 +167,24 @@ public class GetOutboxMessagesHandler { NemhandelLog.WithMDC.setMessageUuid(m.getMessageUuid()); return m; } + + /** + * Gets the content of a message. Will return a stream of the data + * + * @param uuid the uuid of the message + * @param account the account of the requester + * @return inputstream of data for the message + */ + public InputStream getMessageContent(String uuid, Account account) { + return TxUtil.doInTxResult(em, entityManager -> { + try { + if (isNotAuthorized(account, uuid, entityManager)) throw new NotFoundException(); + Message m = getMessage(uuid, entityManager); + return new ByteArrayInputStream(m.getMessageContent().getData()); + } catch (NoResultException e) { + logger.debug("No message with id: {} found in outbox", uuid); + throw new NotFoundException(); + } + }); + } } diff --git a/src/main/java/dk/erst/oxalis/as4/handlers/NemhandelPersister.java b/src/main/java/dk/erst/oxalis/as4/handlers/NemhandelPersister.java index 2a046c1..2ae06b6 100644 --- a/src/main/java/dk/erst/oxalis/as4/handlers/NemhandelPersister.java +++ b/src/main/java/dk/erst/oxalis/as4/handlers/NemhandelPersister.java @@ -25,8 +25,10 @@ import network.oxalis.api.model.Direction; import network.oxalis.api.model.TransmissionIdentifier; import network.oxalis.api.util.Type; import network.oxalis.commons.persist.TempPersister; +import network.oxalis.vefa.peppol.common.model.DocumentTypeIdentifier; import network.oxalis.vefa.peppol.common.model.Header; import network.oxalis.vefa.peppol.common.model.ParticipantIdentifier; +import network.oxalis.vefa.peppol.common.model.ProcessIdentifier; import network.oxalis.vefa.peppol.common.model.Scheme; import org.apache.commons.lang3.StringUtils; import org.apache.cxf.helpers.MapNamespaceContext; @@ -53,7 +55,7 @@ import java.util.concurrent.ExecutorService; /** * Provides a way to (in C3) save received documents from C2, such that the documents will be available in C4. - *
+ *
* NOTE: The document will only be validated using the synchronous validation (e.g. schema validation). If everything is ok * the message will be queued for asynchronous processing which will perform asynchronous validation (schematron and document signature validation) * as well as generate and send an appropriate Message Level Response if necessary. @@ -195,10 +197,30 @@ public class NemhandelPersister extends TempPersister { throw new IOException("Xml is not a StandardBusinessDocument, or a with DOCUMENTID is missing"); } + String documentIdentifier = evaluateXPath(document, xpath, + "/sbd:StandardBusinessDocument/sbd:StandardBusinessDocumentHeader/sbd:BusinessScope/sbd:Scope[sbd:Type='DOCUMENTID']/sbd:Identifier", + null); + if(documentIdentifier != null && !documentIdentifier.isEmpty()) { + documentTypeId = DocumentTypeIdentifier.of(documentTypeId, Scheme.of(documentIdentifier)).toString(); + } else { + documentTypeId = DocumentTypeIdentifier.of(documentTypeId).toString(); // use default scheme! + } + String processId = evaluateXPath(document, xpath, "/sbd:StandardBusinessDocument/sbd:StandardBusinessDocumentHeader/sbd:BusinessScope/sbd:Scope[sbd:Type='PROCESSID']/sbd:InstanceIdentifier", null); + String processIdentifier = evaluateXPath(document, xpath, + "/sbd:StandardBusinessDocument/sbd:StandardBusinessDocumentHeader/sbd:BusinessScope/sbd:Scope[sbd:Type='PROCESSID']/sbd:Identifier", + null); + if(processId != null) { + if (processIdentifier != null && !processIdentifier.isEmpty()) { + processId = ProcessIdentifier.of(processId, Scheme.of(processIdentifier)).toString(); + } else { + processId = ProcessIdentifier.of(processId).toString(); // use default scheme + } + } + messageUuid = evaluateXPath(document, xpath, "/sbd:StandardBusinessDocument/sbd:StandardBusinessDocumentHeader/sbd:DocumentIdentification/sbd:InstanceIdentifier", UUID.randomUUID().toString()); @@ -218,6 +240,7 @@ public class NemhandelPersister extends TempPersister { documentTypeId, content ); + message.setTransmissionId(transmissionIdentifier.getIdentifier()); if (documentTypeConfig.isResponseType()) { messageLogList.add(NemhandelLog.createMessageLog(message, ErrorCodes.C2_RECEIVING_OF_MLR_INT)); @@ -334,18 +357,6 @@ public class NemhandelPersister extends TempPersister { .evaluate(document, XPathConstants.NODE); } - /** - * Extracts the CVR number from the database, for the receiver. - * - * @param receiver the receiver to find the CVR number from - * @return the receiver cvr number - * @throws NullPointerException if there is no receiver - */ - public String getReceiverCvrNumber(String receiver) { - AccountReceiver accountReceiver = getAccountReceiverTiedToReceiver(receiver, em.get()); - return accountReceiver.getCvrNumber(); - } - /** * Queries the database for an {@link AccountReceiver} based on the participant id. * diff --git a/src/main/java/dk/erst/oxalis/as4/handlers/SendSbdHandler.java b/src/main/java/dk/erst/oxalis/as4/handlers/SendSbdHandler.java index 8848f0d..d5bbd85 100644 --- a/src/main/java/dk/erst/oxalis/as4/handlers/SendSbdHandler.java +++ b/src/main/java/dk/erst/oxalis/as4/handlers/SendSbdHandler.java @@ -36,10 +36,8 @@ import java.io.StringWriter; import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; import java.time.ZoneId; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.UUID; +import java.util.*; +import java.util.stream.Collectors; import static dk.erst.oxalis.as4.util.ExceptionUtil.findIfCauseIsMessageUUIDConstraint; @@ -98,33 +96,33 @@ public class SendSbdHandler { } } catch(OutboundValidationException e) { Message finalMessage = message; - List messageLogList; - - if (e.getMessageLogList() == null) { - messageLogList = new ArrayList<>(); - if (e.getValidationResult() != null && e.getValidationResult().hasErrors()) { - message.setStatus(MessageStatus.ERROR); - - e.getValidationResult().getErrors().stream() - .map(v -> NemhandelLog.createMessageLog(finalMessage, v)) - .forEach(messageLogList::add); - logger.error("Validation errors: {}", e.getValidationResult()); - } + List messageLogList = e.getMessageLogList() != null ? e.getMessageLogList() : new ArrayList<>(); + final Set codes = messageLogList.stream() + .map(MessageLog::getCode) + .collect(Collectors.toSet()); + + finalMessage.setStatus(MessageStatus.ERROR); + + if (e.getValidationResult() != null && e.getValidationResult().hasErrors()) { + e.getValidationResult().getErrors().stream() + .filter(v -> !codes.contains(v.getCode())) + .map(v -> NemhandelLog.createMessageLog(finalMessage, v)) + .forEach(messageLogList::add); + logger.error("Validation errors: {}", e.getValidationResult()); + } - if (e.getValidationResult() != null && e.getValidationResult().hasWarnings()) { - e.getValidationResult().getWarnings().stream() - .map(v -> NemhandelLog.createMessageLog(finalMessage, v)) - .forEach(messageLogList::add); - if (!e.getValidationResult().hasErrors()) { - logger.warn("Validation warnings: {}", e.getValidationResult()); - } + if (e.getValidationResult() != null && e.getValidationResult().hasWarnings()) { + e.getValidationResult().getWarnings().stream() + .filter(v -> !codes.contains(v.getCode())) + .map(v -> NemhandelLog.createMessageLog(finalMessage, v)) + .forEach(messageLogList::add); + if (!e.getValidationResult().hasErrors()) { + logger.warn("Validation warnings: {}", e.getValidationResult()); } - } else { - messageLogList = e.getMessageLogList(); } if(messageContext != null && messageContext.isInternalMessageLevelResponse()) { - em.get().persist(finalMessage); + em.get().merge(finalMessage); messageLogList.forEach(vrl -> vrl.persist(em.get())); // in this case an active transaction already exists (from the async processing) } else { TxUtil.doInTx(em, entityManager -> { @@ -174,7 +172,7 @@ public class SendSbdHandler { } if(messageContext != null && messageContext.isInternalMessageLevelResponse()) { - em.get().persist(message); // in this case an active transaction already exists (from the async processing) + em.get().merge(message); // in this case an active transaction already exists (from the async processing) } else { TxUtil.doInTx(em, entityManager -> { entityManager.merge(message); diff --git a/src/main/java/dk/erst/oxalis/as4/handlers/dto/ErrorMessage.java b/src/main/java/dk/erst/oxalis/as4/handlers/dto/ErrorMessage.java index 982c29e..d6cd2ed 100644 --- a/src/main/java/dk/erst/oxalis/as4/handlers/dto/ErrorMessage.java +++ b/src/main/java/dk/erst/oxalis/as4/handlers/dto/ErrorMessage.java @@ -1,5 +1,7 @@ package dk.erst.oxalis.as4.handlers.dto; +import io.swagger.v3.oas.annotations.media.Schema; + import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlType; @@ -10,7 +12,9 @@ import javax.xml.bind.annotation.XmlType; @XmlType @XmlAccessorType(XmlAccessType.FIELD) public class ErrorMessage { + @Schema(description = "Error code") private String errorCode; + @Schema(description = "Error message") private String errorMessage; /** diff --git a/src/main/java/dk/erst/oxalis/as4/handlers/dto/ErrorResponse.java b/src/main/java/dk/erst/oxalis/as4/handlers/dto/ErrorResponse.java index e84ee05..c51be75 100644 --- a/src/main/java/dk/erst/oxalis/as4/handlers/dto/ErrorResponse.java +++ b/src/main/java/dk/erst/oxalis/as4/handlers/dto/ErrorResponse.java @@ -1,6 +1,7 @@ package dk.erst.oxalis.as4.handlers.dto; import dk.erst.oxalis.as4.handlers.OutboundException; +import io.swagger.v3.oas.annotations.media.Schema; import javax.xml.bind.annotation.*; import java.util.ArrayList; @@ -9,14 +10,16 @@ import java.util.List; /** * Represents an error response with a message UUID and a list of {@link ErrorMessage}. */ -@XmlRootElement +@XmlRootElement(namespace = "http://www.erst.dk/oxalis/api", name ="errorResponse") @XmlAccessorType(XmlAccessType.FIELD) @XmlType public class ErrorResponse { @XmlElement + @Schema(description = "MessageUUID this error response belongs to") private String messageUuid; @XmlElement + @Schema(description = "List of error messages") private List errors; /** diff --git a/src/main/java/dk/erst/oxalis/as4/handlers/dto/ListMessageResponse.java b/src/main/java/dk/erst/oxalis/as4/handlers/dto/ListMessageResponse.java index 0f66c72..9056566 100644 --- a/src/main/java/dk/erst/oxalis/as4/handlers/dto/ListMessageResponse.java +++ b/src/main/java/dk/erst/oxalis/as4/handlers/dto/ListMessageResponse.java @@ -12,11 +12,11 @@ import java.util.List; /** * Represents a response containing a list of {@link MessageModel} messages. */ -@XmlRootElement +@XmlRootElement(namespace = "http://www.erst.dk/oxalis/api", name ="listMessageResponse") @XmlAccessorType(XmlAccessType.FIELD) public class ListMessageResponse { @XmlElement - @Schema(description = "a list of messages") + @Schema(description = "a list of messages...") private List messages = new ArrayList<>(); @XmlElement @Schema(description = "The page of messages returned") diff --git a/src/main/java/dk/erst/oxalis/as4/handlers/dto/MessageAndResponses.java b/src/main/java/dk/erst/oxalis/as4/handlers/dto/MessageAndResponses.java new file mode 100644 index 0000000..59a6650 --- /dev/null +++ b/src/main/java/dk/erst/oxalis/as4/handlers/dto/MessageAndResponses.java @@ -0,0 +1,65 @@ +package dk.erst.oxalis.as4.handlers.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.List; + +/** + * Response class for the REST api returning a message from the outbox + * as well as any received response messages. + */ +@XmlRootElement(namespace = "http://www.erst.dk/oxalis/api", name ="messageAndResponses") +@XmlAccessorType(XmlAccessType.FIELD) +public class MessageAndResponses { + + @XmlElement + @Schema(implementation = MessageModel.class, description = "The original document") + private MessageModel originalDocument; + @XmlElement + @Schema(implementation = MessageModel.class, description = "The list of all responses for the original document") + private List responses; + + /** + * Constructs a new instance of the class + */ + public MessageAndResponses() { + } + + /** + * Gets the original document - i.e. the document from the outbox. + * @return The Message as a {@link MessageModel} + */ + public MessageModel getOriginalDocument() { + return originalDocument; + } + + /** + * Sets the original document - i.e. the document from the outbox. + * @param originalDocument The original document + */ + public void setOriginalDocument(MessageModel originalDocument) { + this.originalDocument = originalDocument; + } + + /** + * Gets the list of associated responses received for the original document - i.e. the responses from the inbox + * which reference the original document. + * @return The list of responses received for the original document. + */ + public List getResponses() { + return responses; + } + + /** + * Sets the list of associated responses received for the original document - i.e. the responses from the inbox + * which reference the original document. + * @param responses The list of responses associated with the original document + */ + public void setResponses(List responses) { + this.responses = responses; + } +} diff --git a/src/main/java/dk/erst/oxalis/as4/handlers/dto/MessageLogResponse.java b/src/main/java/dk/erst/oxalis/as4/handlers/dto/MessageLogResponse.java index 2013da2..fb8e5fe 100644 --- a/src/main/java/dk/erst/oxalis/as4/handlers/dto/MessageLogResponse.java +++ b/src/main/java/dk/erst/oxalis/as4/handlers/dto/MessageLogResponse.java @@ -12,7 +12,7 @@ import java.util.List; /** * Represents a message log response with a unique message UUID, an optional Message level response UUID and a {@link MessageLogModel} list of validations. */ -@XmlRootElement +@XmlRootElement(namespace = "http://www.erst.dk/oxalis/api", name ="messageLogResponse") @XmlAccessorType(XmlAccessType.FIELD) public class MessageLogResponse { diff --git a/src/main/java/dk/erst/oxalis/as4/handlers/dto/MessageModel.java b/src/main/java/dk/erst/oxalis/as4/handlers/dto/MessageModel.java index da4a526..d65fdb3 100644 --- a/src/main/java/dk/erst/oxalis/as4/handlers/dto/MessageModel.java +++ b/src/main/java/dk/erst/oxalis/as4/handlers/dto/MessageModel.java @@ -27,7 +27,8 @@ import java.time.LocalDateTime; "transmissionId", "documentTypeId", "peppolProcessId", - "remoteHost" + "remoteHost", + "messageLevelResponseUuid" }) public class MessageModel { @XmlElement @@ -75,6 +76,10 @@ public class MessageModel { example = "http://localhost:8080/oxalis/as4") private String remoteHost; + @XmlElement + @Schema(description = "The UUID of the Message Level Response or OIOUBL Application Response which has been sent for this message in the event of validation errors") + private String messageLevelResponseUuid; + /** * Returns the status of the document. * @@ -311,6 +316,25 @@ public class MessageModel { model.setDocumentTypeId(m.getDocumentTypeId()); model.setPeppolProcessId(m.getPeppolProcessId()); model.setTransmissionId(m.getTransmissionId()); + model.setMessageLevelResponseUuid(m.getMessageLevelResponseUuid()); return model; } + + /** + * Gets the UUID of the Message Level Response or OIOUBL Application Response which + * has been sent regarding this message in the event of validation errors. + * @return The UUID of the response. + */ + public String getMessageLevelResponseUuid() { + return messageLevelResponseUuid; + } + + /** + * Sets the UUID of the Message Level Response or OIOUBL Application Response which + * has been sent regarding this message in the event of validation errors. + * @param messageLevelResponseUuid The UUID of the response. + */ + public void setMessageLevelResponseUuid(String messageLevelResponseUuid) { + this.messageLevelResponseUuid = messageLevelResponseUuid; + } } diff --git a/src/main/java/dk/erst/oxalis/as4/handlers/dto/MessageModelResponse.java b/src/main/java/dk/erst/oxalis/as4/handlers/dto/MessageModelResponse.java index a3cd5f2..8222ed4 100644 --- a/src/main/java/dk/erst/oxalis/as4/handlers/dto/MessageModelResponse.java +++ b/src/main/java/dk/erst/oxalis/as4/handlers/dto/MessageModelResponse.java @@ -12,7 +12,7 @@ import java.util.List; /** * Represents a response containing a {@link MessageModel}. */ -@XmlRootElement +@XmlRootElement(namespace = "http://www.erst.dk/oxalis/api", name ="messageModelResponse") @XmlAccessorType(XmlAccessType.FIELD) public class MessageModelResponse { @XmlElement diff --git a/src/main/java/dk/erst/oxalis/as4/handlers/dto/MessageResponse.java b/src/main/java/dk/erst/oxalis/as4/handlers/dto/MessageResponse.java index 42ae519..0a22d84 100644 --- a/src/main/java/dk/erst/oxalis/as4/handlers/dto/MessageResponse.java +++ b/src/main/java/dk/erst/oxalis/as4/handlers/dto/MessageResponse.java @@ -10,7 +10,7 @@ import javax.xml.bind.annotation.XmlRootElement; /** * Represents a message response with a unique message UUID. */ -@XmlRootElement +@XmlRootElement(namespace = "http://www.erst.dk/oxalis/api", name ="messageResponse") @XmlAccessorType(XmlAccessType.FIELD) public class MessageResponse { diff --git a/src/main/java/dk/erst/oxalis/as4/handlers/dto/UpdateStatusRequest.java b/src/main/java/dk/erst/oxalis/as4/handlers/dto/UpdateStatusRequest.java index 7e34160..273f991 100644 --- a/src/main/java/dk/erst/oxalis/as4/handlers/dto/UpdateStatusRequest.java +++ b/src/main/java/dk/erst/oxalis/as4/handlers/dto/UpdateStatusRequest.java @@ -11,10 +11,10 @@ import javax.xml.bind.annotation.XmlRootElement; /** * Represents a request to update the status of a message. */ -@XmlRootElement(name = "UpdateStatusRequest") +@XmlRootElement(namespace = "http://www.erst.dk/oxalis/api", name = "updateStatusRequest") @XmlAccessorType(XmlAccessType.FIELD) public class UpdateStatusRequest { - @XmlElement(name = "MessageStatus") + @XmlElement @Schema(description = "Status to set on the message. Must be either RECEIVED or CREATED.", example = "RECEIVED") private MessageStatus messageStatus; diff --git a/src/main/java/dk/erst/oxalis/as4/handlers/dto/ValidationResult.java b/src/main/java/dk/erst/oxalis/as4/handlers/dto/ValidationResult.java index 45c2af6..adc7549 100644 --- a/src/main/java/dk/erst/oxalis/as4/handlers/dto/ValidationResult.java +++ b/src/main/java/dk/erst/oxalis/as4/handlers/dto/ValidationResult.java @@ -14,7 +14,7 @@ import java.util.stream.Collectors; /** * Validation result containing any errors or warnings. */ -@XmlRootElement +@XmlRootElement(namespace = "http://www.erst.dk/oxalis/api", name ="validationResult") @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "ValidationResult", propOrder = { "validationType", @@ -253,23 +253,26 @@ public class ValidationResult { if(validationType != null) { sb.append(" (validation type: ").append(validationType).append(")"); } - sb.append(": \n"); + sb.append(": "); if(hasErrors()) { - sb.append(getErrors().size() + " errors: \n") - .append(getErrors().stream().map(e -> e.toString()).collect(Collectors.joining("\n"))) - .append("\n"); + sb.append(getErrors().size()) + .append(" errors: [") + .append(getErrors().stream().map(e -> e.toString()).collect(Collectors.joining(", "))) + .append("]"); } else { - sb.append("0 errors \n"); + sb.append("0 errors"); } + sb.append(", "); if(hasWarnings()) { - sb.append(getWarnings().size() + " warnings: \n") - .append(getWarnings().stream().map(e -> e.toString()).collect(Collectors.joining("\n"))) - .append("\n"); + sb.append(getWarnings().size()) + .append(" warnings: [") + .append(getWarnings().stream().map(e -> e.toString()).collect(Collectors.joining(", "))) + .append("]"); } else { - sb.append("0 Warnings \n"); + sb.append("0 warnings"); } return sb.toString(); diff --git a/src/main/java/dk/erst/oxalis/as4/jdbc/JdbcConf.java b/src/main/java/dk/erst/oxalis/as4/jdbc/JdbcConf.java index 64dc849..733309b 100644 --- a/src/main/java/dk/erst/oxalis/as4/jdbc/JdbcConf.java +++ b/src/main/java/dk/erst/oxalis/as4/jdbc/JdbcConf.java @@ -115,10 +115,18 @@ public enum JdbcConf { @DefaultValue("none") JDBC_LIQUIBASE_CONTEXTS, /** - * If set to true, run the liquibase located in /src/main/resources/db/oxalis-as4-db-changelog.xml + * If set to true, run the configured {@link #JDBC_LIQUIBASE_CHANGELOG} file. * Make sure to take a look at the {@link JdbcConf#JDBC_LIQUIBASE_CONTEXTS} before setting this to true */ @Path("jdbc.liquibase.run") @DefaultValue("true") - JDBC_LIQUIBASE_RUN; + JDBC_LIQUIBASE_RUN, + + /** + * The changelog file is to be run by liquibase during startup (provided liquibase is enabled via {@link #JDBC_LIQUIBASE_RUN}). + * Default is to use the changelog file located in /src/main/resources/db/oxalis-as4-db-changelog.xml + */ + @Path("jdbc.liquibase.changelog") + @DefaultValue("db/oxalis-as4-db-changelog.xml") + JDBC_LIQUIBASE_CHANGELOG; } diff --git a/src/main/java/dk/erst/oxalis/as4/jdbc/JdbcFilter.java b/src/main/java/dk/erst/oxalis/as4/jdbc/JdbcFilter.java index e5e6649..58878d7 100644 --- a/src/main/java/dk/erst/oxalis/as4/jdbc/JdbcFilter.java +++ b/src/main/java/dk/erst/oxalis/as4/jdbc/JdbcFilter.java @@ -14,7 +14,7 @@ import java.util.Enumeration; /** * A singleton filter class that applies JDBC operations. - *
+ *
* This filter class is responsible for managing JDBC driver deregistration, as well as * cleaning up any abandoned connections. It doesn't apply any filtering operations. */ diff --git a/src/main/java/dk/erst/oxalis/as4/jdbc/JdbcModule.java b/src/main/java/dk/erst/oxalis/as4/jdbc/JdbcModule.java index 15487df..cc7ea94 100644 --- a/src/main/java/dk/erst/oxalis/as4/jdbc/JdbcModule.java +++ b/src/main/java/dk/erst/oxalis/as4/jdbc/JdbcModule.java @@ -21,7 +21,7 @@ import java.util.Map; /** * This module handles JPA/JDBC configuration by reading the configuration specified from {@link JdbcConf}. - *
+ *
* eg.; *
{@code
  * jpaProperties.put("hibernate.connection.driver_class", settings.getString(JdbcConf.JDBC_DRIVER_CLASS))
@@ -65,7 +65,7 @@ public class JdbcModule extends ServletModule {
     /**
      *
      * This method is responsible for initializing the Database. All the configurations that are passed into the JPA properties module, is taken from the Oxalis.conf file (or default values).
-     * 
+ *
* Note: if a H2 database is used (based on the configured dialect) a default connection pool is configured (with a maximum size), otherwise a C3P0 connection pool is configured with a min size and a max size. * * @param settings {@link JdbcConf} - the configured settings injected into the method @@ -96,14 +96,14 @@ public class JdbcModule extends ServletModule { // we have to do liquibase update here (as opposed to configureServlets()), since it requires // the above values to be present if(settings.getString(JdbcConf.JDBC_LIQUIBASE_RUN).contentEquals("true")) { - runLiquibaseMigration(settings.getString(JdbcConf.JDBC_LIQUIBASE_CONTEXTS)); + runLiquibaseMigration(settings.getString(JdbcConf.JDBC_LIQUIBASE_CHANGELOG), settings.getString(JdbcConf.JDBC_LIQUIBASE_CONTEXTS)); } else { logger.info("Skipping liquibase run."); } } - private void runLiquibaseMigration(String liquibaseContexts) { + private void runLiquibaseMigration(String changelogFile, String liquibaseContexts) { logger.info("Running liquibase update with contexts: {}", liquibaseContexts); String url = jpaProperties.get("hibernate.connection.url"); @@ -122,7 +122,7 @@ public class JdbcModule extends ServletModule { // see http://www.h2database.com/html/features.html#in_memory_databases try (Connection connection = DriverManager.getConnection(url, user, password)) { JdbcConnection liquibaseConnection = new JdbcConnection(connection); - Liquibase liquibase = new Liquibase("db/oxalis-as4-db-changelog.xml", new ClassLoaderResourceAccessor(), liquibaseConnection); + Liquibase liquibase = new Liquibase(changelogFile, new ClassLoaderResourceAccessor(), liquibaseConnection); liquibase.update(liquibaseContexts); } catch (SQLException | LiquibaseException e) { throw new RuntimeException(e); diff --git a/src/main/java/dk/erst/oxalis/as4/persistence/model/AccountReceiver.java b/src/main/java/dk/erst/oxalis/as4/persistence/model/AccountReceiver.java index 7de08e3..194ea19 100644 --- a/src/main/java/dk/erst/oxalis/as4/persistence/model/AccountReceiver.java +++ b/src/main/java/dk/erst/oxalis/as4/persistence/model/AccountReceiver.java @@ -17,10 +17,6 @@ public class AccountReceiver extends AbstractEntity { @Column(name = "participant_id", nullable = false, unique = true) private String participantId; - // currently, transferDelegation identifies parties by cvr number, thus it is required - @Column(name = "cvr_number", nullable = false) - private String cvrNumber; - /** * Instantiates a new Account receiver. */ @@ -32,12 +28,10 @@ public class AccountReceiver extends AbstractEntity { * * @param account the account * @param participantId the participant id - * @param cvrNumber the cvr number */ - public AccountReceiver(Account account, String participantId, String cvrNumber){ + public AccountReceiver(Account account, String participantId){ this.account = account; this.participantId = participantId; - this.cvrNumber = cvrNumber; } /** @@ -76,21 +70,4 @@ public class AccountReceiver extends AbstractEntity { this.participantId = participantId; } - /** - * Gets cvr number. - * - * @return the cvr number - */ - public String getCvrNumber() { - return cvrNumber; - } - - /** - * Sets cvr number. - * - * @param cvrNumber the cvr number - */ - public void setCvrNumber(String cvrNumber) { - this.cvrNumber = cvrNumber; - } } diff --git a/src/main/java/dk/erst/oxalis/as4/persistence/model/Message.java b/src/main/java/dk/erst/oxalis/as4/persistence/model/Message.java index d0a1c88..8c0bd0a 100644 --- a/src/main/java/dk/erst/oxalis/as4/persistence/model/Message.java +++ b/src/main/java/dk/erst/oxalis/as4/persistence/model/Message.java @@ -18,6 +18,13 @@ public class Message extends AbstractEntity { @JoinColumns({ @JoinColumn(name = "account_id", nullable = false) }) private Account account; + @ManyToOne(fetch = FetchType.LAZY, optional = true) + @JoinColumn(name = "original_message", nullable = true) + private Message originalMessage; + + @OneToMany(fetch = FetchType.LAZY, mappedBy = "originalMessage") + private Set responses; + @Enumerated(EnumType.STRING) @Column(name = "status", nullable = false) private MessageStatus status = MessageStatus.CREATED; @@ -468,4 +475,40 @@ public class Message extends AbstractEntity { public void setMessageLevelResponseUuid(String responseMessageUuid) { this.messageLevelResponseUuid = responseMessageUuid; } + + /** + * If message is a representation of a response, then this will have a relation to the original message. + * + * @return the original message + */ + public Message getOriginalMessage() { + return originalMessage; + } + + /** + * Sets the original message that this Message is a response to + * + * @param originalMessage the original message + */ + public void setOriginalMessage(Message originalMessage) { + this.originalMessage = originalMessage; + } + + /** + * Gets all responses for the Message + * + * @return the responses + */ + public Set getResponses() { + return responses; + } + + /** + * Sets all responses to the Message. + * + * @param responses the responses + */ + public void setResponses(Set responses) { + this.responses = responses; + } } diff --git a/src/main/java/dk/erst/oxalis/as4/persistence/model/MessageLog.java b/src/main/java/dk/erst/oxalis/as4/persistence/model/MessageLog.java index a656612..a9db8e0 100644 --- a/src/main/java/dk/erst/oxalis/as4/persistence/model/MessageLog.java +++ b/src/main/java/dk/erst/oxalis/as4/persistence/model/MessageLog.java @@ -54,7 +54,7 @@ public class MessageLog { /** *
-        Creates a MessageLog and accompanying application log.
+        Persists a MessageLog and accompanying application log.
         Use this when you have created a Message Log object somewhere else,
         but now wish to save it.
      * 
@@ -120,6 +120,8 @@ public class MessageLog { msgLog.setType(type); msgLog.setValidationLineReference(validationLineReference); + logger.debug(NemhandelLog.EVENT_TEXT, msgLog.getDescription()); + return msgLog; } diff --git a/src/main/java/dk/erst/oxalis/as4/persistence/model/MessageStatus.java b/src/main/java/dk/erst/oxalis/as4/persistence/model/MessageStatus.java index 3e02ee4..6998177 100644 --- a/src/main/java/dk/erst/oxalis/as4/persistence/model/MessageStatus.java +++ b/src/main/java/dk/erst/oxalis/as4/persistence/model/MessageStatus.java @@ -1,6 +1,5 @@ package dk.erst.oxalis.as4.persistence.model; -import org.jetbrains.annotations.NotNull; /** * Used to describe the status of the Message passing through the flow @@ -37,7 +36,7 @@ public enum MessageStatus { * @return the MessageStatus * @throws IllegalArgumentException if no MessageStatus if found with the given name. */ - public static @NotNull MessageStatus getByName(String n) { + public static MessageStatus getByName(String n) { for (MessageStatus v : MessageStatus.values()) { if (v.name().equalsIgnoreCase(n)) return v; diff --git a/src/main/java/dk/erst/oxalis/as4/persistence/model/NemhandelLog.java b/src/main/java/dk/erst/oxalis/as4/persistence/model/NemhandelLog.java index 5fbde49..8ff65a4 100644 --- a/src/main/java/dk/erst/oxalis/as4/persistence/model/NemhandelLog.java +++ b/src/main/java/dk/erst/oxalis/as4/persistence/model/NemhandelLog.java @@ -37,6 +37,7 @@ import java.util.Set; */ public class NemhandelLog { + static final String EVENT_TEXT = "DocumentLog event recorded with text: \"{}\""; static final String ENTRY_TEXT = "Entry inserted into DocumentLog with id = [{}], and text: \"{}\""; static final String MERGE_TEXT = "Entry updated in DocumentLog with id = [{}], and text \"{}\""; @@ -46,8 +47,8 @@ public class NemhandelLog { /** * Creates a MessageLog and accompanying application log - * @See {@link MessageLog#persist(EntityManager)} - * @See {@link MessageLog#merge(EntityManager)} + * @see MessageLog#persist(EntityManager) + * @see MessageLog#merge(EntityManager) * @param em entity manager * @param message {@link Message} * @param errorCode error codes as string @@ -62,8 +63,8 @@ public class NemhandelLog { } /** * Creates a MessageLog and accompanying application log - * @See {@link MessageLog#persist(EntityManager)} - * @See {@link MessageLog#merge(EntityManager)} + * @see MessageLog#persist(EntityManager) + * @see MessageLog#merge(EntityManager) * @param em entity manager * @param message {@link Message} * @param errorCode error codes object @@ -76,8 +77,8 @@ public class NemhandelLog { } /** * Creates a MessageLog and accompanying application log - * @See {@link MessageLog#persist(EntityManager)} - * @See {@link MessageLog#merge(EntityManager)} + * @see MessageLog#persist(EntityManager) + * @see MessageLog#merge(EntityManager) * @param em entity manager * @param message {@link Message} * @param errorCode error codes object @@ -88,8 +89,8 @@ public class NemhandelLog { } /** * Creates a MessageLog and accompanying application log - * @See {@link MessageLog#persist(EntityManager)} - * @See {@link MessageLog#merge(EntityManager)} + * @see MessageLog#persist(EntityManager) + * @see MessageLog#merge(EntityManager) * @param em entity manager * @param message {@link Message} * @param errorCode {@link ErrorCodes} @@ -114,8 +115,8 @@ public class NemhandelLog { } /** * Creates a MessageLog and accompanying application log - * @See {@link MessageLog#persist(EntityManager)} - * @See {@link MessageLog#merge(EntityManager)} + * @see MessageLog#persist(EntityManager) + * @see MessageLog#merge(EntityManager) * @param em entity manager * @param message {@link Message} * @param errorCode error codes object @@ -128,8 +129,8 @@ public class NemhandelLog { } /** * Creates a MessageLog and accompanying application log - * @See {@link MessageLog#persist(EntityManager)} - * @See {@link MessageLog#merge(EntityManager)} + * @see MessageLog#persist(EntityManager) + * @see MessageLog#merge(EntityManager) * @param em entity manager * @param message {@link Message} * @param validationMessage {@link ValidationMessage} @@ -142,10 +143,10 @@ public class NemhandelLog { /** * OBS: * Should use {@link MessageLog#persist(EntityManager)} or {@link MessageLog#merge(EntityManager)} - * when persisting or merging.


+ * when persisting or merging.


* Should only be used in async env. to avoid breaking the transactions, instead use {@link NemhandelLog#createMessageLog(EntityManager, Message, ValidationMessage)} instead - * @See {@link MessageLog#persist(EntityManager)} - * @See {@link MessageLog#merge(EntityManager)} + * @see MessageLog#persist(EntityManager) + * @see MessageLog#merge(EntityManager) * @param msg {@link Message} * @param validationMessage {@link ValidationMessage} * @return {@link MessageLog} (not persisted) @@ -172,7 +173,7 @@ public class NemhandelLog { /** * Helper class for adding parameters to the MDC for logging purposes. - * MDC => the stored variables that Logback utilizes for parametrization of a loggable line + * MDC => the stored variables that Logback utilizes for parametrization of a loggable line */ public static class WithMDC { private static final String UUID_KEY = "messageUuid"; diff --git a/src/main/java/dk/erst/oxalis/as4/providers/NemhandelProvider.java b/src/main/java/dk/erst/oxalis/as4/providers/NemhandelProvider.java index 5b1eb67..2fe5500 100644 --- a/src/main/java/dk/erst/oxalis/as4/providers/NemhandelProvider.java +++ b/src/main/java/dk/erst/oxalis/as4/providers/NemhandelProvider.java @@ -4,7 +4,6 @@ import network.oxalis.vefa.peppol.common.model.DocumentTypeIdentifier; import network.oxalis.vefa.peppol.common.model.ParticipantIdentifier; import network.oxalis.vefa.peppol.common.util.ModelUtils; import network.oxalis.vefa.peppol.lookup.api.MetadataProvider; -import org.jetbrains.annotations.NotNull; import java.net.URI; import java.net.URISyntaxException; @@ -78,7 +77,6 @@ public class NemhandelProvider implements MetadataProvider { return resolvedServiceMetaDataURIList; } - @NotNull private static URI getUri(URI location) { if (!location.toString().endsWith("/")) { try { diff --git a/src/main/java/dk/erst/oxalis/as4/rest/ForwardedHeaderFilter.java b/src/main/java/dk/erst/oxalis/as4/rest/ForwardedHeaderFilter.java index ed4e689..f6a5ad7 100644 --- a/src/main/java/dk/erst/oxalis/as4/rest/ForwardedHeaderFilter.java +++ b/src/main/java/dk/erst/oxalis/as4/rest/ForwardedHeaderFilter.java @@ -79,7 +79,7 @@ public class ForwardedHeaderFilter implements javax.servlet.Filter { if (request.getHeader("X-Forwarded-Port") != null) { forwardedPortHeader = request.getHeader("X-Forwarded-Port"); } else { - if (this.host.equalsIgnoreCase("localhost")) { + if (this.host.equalsIgnoreCase("localhost") || this.host.equals("127.0.0.1")) { forwardedPortHeader = String.valueOf(request.getServerPort()); } else { forwardedPortHeader = String.valueOf(-1); diff --git a/src/main/java/dk/erst/oxalis/as4/rest/openapi/CustomOpenApiServlet.java b/src/main/java/dk/erst/oxalis/as4/rest/openapi/CustomOpenApiServlet.java index f5d7d78..6a455f9 100644 --- a/src/main/java/dk/erst/oxalis/as4/rest/openapi/CustomOpenApiServlet.java +++ b/src/main/java/dk/erst/oxalis/as4/rest/openapi/CustomOpenApiServlet.java @@ -3,13 +3,13 @@ package dk.erst.oxalis.as4.rest.openapi; import com.google.inject.Inject; import dk.erst.oxalis.as4.rest.resources.OutboxResource; import dk.erst.oxalis.as4.util.BaseConfig; +import dk.erst.oxalis.as4.validation.version.OxalisAS4Version; import io.swagger.v3.jaxrs2.integration.JaxrsOpenApiContextBuilder; import io.swagger.v3.jaxrs2.integration.ServletConfigContextUtils; import io.swagger.v3.oas.integration.OpenApiConfigurationException; import io.swagger.v3.oas.integration.SwaggerConfiguration; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.info.Contact; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.info.License; import io.swagger.v3.oas.models.security.SecurityRequirement; @@ -58,6 +58,7 @@ public class CustomOpenApiServlet extends io.swagger.v3.jaxrs2.integration.OpenA OpenAPI oas = new OpenAPI(); Info info = new Info() .title("NemHandel eDelivery Reference Access Point API") + .version(OxalisAS4Version.getVersion()) .description("This is API allows a client to interact with the access point to both send and receive " + "documents through the NemHandel eDelivery network. It uses simple HTTP basic authentication " + "for security.\n\n" + diff --git a/src/main/java/dk/erst/oxalis/as4/rest/resources/InboxResource.java b/src/main/java/dk/erst/oxalis/as4/rest/resources/InboxResource.java index f95d3b5..f97ced4 100644 --- a/src/main/java/dk/erst/oxalis/as4/rest/resources/InboxResource.java +++ b/src/main/java/dk/erst/oxalis/as4/rest/resources/InboxResource.java @@ -41,7 +41,8 @@ public class InboxResource { description = "This is a paged API over inbox messages (i.e. messages received by this AP).", tags = "Inbox", responses = { - @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = ListMessageResponse.class))), + @ApiResponse(responseCode = "200", description = "The list of inbox messages", + content = @Content(schema = @Schema(implementation = ListMessageResponse.class))), @ApiResponse(responseCode = "401", description = "Unauthorized") }) public Response getList( @@ -86,7 +87,8 @@ public class InboxResource { description = "Returns the status of the specified message", tags = "Inbox", responses = { - @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = MessageLogResponse.class))), + @ApiResponse(responseCode = "200", description = "The status of the inbox message", + content = @Content(schema = @Schema(implementation = MessageModelResponse.class))), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "404", description = "Message with the specified UUID does not exist " + "(for security purposes this is also the result if specifying a message uuid tied to " + @@ -107,7 +109,8 @@ public class InboxResource { description = "Returns errors, warnings and general info, if any, related to the specified message", tags = "Inbox", responses = { - @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = MessageLogResponse.class))), + @ApiResponse(responseCode = "200", description = "The document log related to the inbox message", + content = @Content(schema = @Schema(implementation = MessageLogResponse.class))), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "404", description = "Message with the specified UUID does not exist " + "(for security purposes this is also the result if specifying a message uuid tied to " + diff --git a/src/main/java/dk/erst/oxalis/as4/rest/resources/OutboxResource.java b/src/main/java/dk/erst/oxalis/as4/rest/resources/OutboxResource.java index 72c36f4..08d9a9d 100644 --- a/src/main/java/dk/erst/oxalis/as4/rest/resources/OutboxResource.java +++ b/src/main/java/dk/erst/oxalis/as4/rest/resources/OutboxResource.java @@ -5,14 +5,15 @@ import dk.erst.oxalis.as4.handlers.GetOutboxMessagesHandler; import dk.erst.oxalis.as4.handlers.GetMessageLogHandler; import dk.erst.oxalis.as4.handlers.OutboundException; import dk.erst.oxalis.as4.handlers.SendSbdHandler; +import dk.erst.oxalis.as4.handlers.dto.*; +import dk.erst.oxalis.as4.persistence.model.Account; +import dk.erst.oxalis.as4.persistence.model.MessageDirection; import dk.erst.oxalis.as4.handlers.dto.MessageModel; import dk.erst.oxalis.as4.handlers.dto.MessageModelResponse; import dk.erst.oxalis.as4.handlers.dto.MessageResponse; import dk.erst.oxalis.as4.handlers.dto.MessageLogResponse; import dk.erst.oxalis.as4.handlers.dto.ListMessageResponse; import dk.erst.oxalis.as4.handlers.dto.ValidationResult; -import dk.erst.oxalis.as4.persistence.model.Account; -import dk.erst.oxalis.as4.persistence.model.MessageDirection; import dk.erst.oxalis.as4.persistence.model.NemhandelLog; import dk.erst.oxalis.as4.rest.security.SecurityContextHolder; import dk.erst.oxalis.as4.util.SBDMessageContext; @@ -58,7 +59,7 @@ public class OutboxResource { summary = "Sends the specified document through the AP. ", description = "If the AP is running in NemHandel eDelivery mode, then the document can be signed " + - "via an ASiC-E container in the header including a transfer-delegation.\n\n" + + "via a Nemhandel e-Delivery document signature in the header.\n\n" + "Example of NemHandel eDelivery compliant document:\n" + "```" + "\n" + @@ -87,44 +88,53 @@ public class OutboxResource { " PROCESSID\n" + " urn:www.cenbii.eu:profile:bii04:ver2.0\n" + " \n" + + " \n" + " \n" + " NEMHANDEL_EDELIVERY_SIGNATURE\n" + - " \n" + + " \n" + + " nemhandel-edelivery-1.2\n" + " \n" + " \n" + " \n" + " ...\n" + "\n" + "```\n\n" + - "The contents of the ASiC-E container is expected to be the following:\n" + - "```\n" + - "asic-container.asice:\n" + - " |\n" + - " +-- mimetype\n" + - " |\n" + - " +-- standard-business-document.xml (2)\n" + - " |\n" + - " +-- original-standard-business-document.xml (3)\n" + - " |\n" + - " +-- transfer-delegation.xml (4)\n" + - " |\n" + - " +-- META-INF/\n" + - " |\n" + - " +-- manifest.xml\n" + - " |\n" + - " +-- signatures001.xml (1)\n" + - "```\n" + - "The following files are of specific interest:\n" + - "1. The actual signature of the ASiC-E container is contained in this file. An ASiC-E container may contain multiple signatures though we only require one, which must include the following 3 files (1 optional).\n" + - "2. The Standard Business Document (SBD) to be signed (thus this is excluding the ASiC-E container). This is the file that Corner X will send to Corner X+1.\n" + - "3. The full \"original\" Standard Business Document including the ASiC-E container which has been received from Corner X-1. Note that for Corner 1 this file is optional in case this corner is responsible for generating the original SBD.\n" + - "4. A transfer delegation documents the intention of Corner X to transfer the SBD to Corner X+1. \n\n" + + "The Nemhandel e-Delivery document signature is an detached XAdES signature. Please refer to the specification and the developer documentation for further information.\n\n" + "*NOTE:* Parts of the validation of the message on the receiver end runs asynchronously and returns a MessageLevelResponse in the event of errors. More documentation can be found in `/docs/nemhandel-edelivery.md` in the git repository.", tags = "Outbox", responses = { - @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = MessageResponse.class))), - @ApiResponse(responseCode = "400", content = @Content(schema = @Schema(implementation = ValidationResult.class))), - @ApiResponse(responseCode = "401", description = "Unautorized") + @ApiResponse(responseCode = "200", description = "Message response with a unique message UUID", + content = @Content(mediaType = MediaType.APPLICATION_XML, schema = @Schema(implementation = MessageResponse.class))), + @ApiResponse(responseCode = "400", description = "Indicates that an error occurred", + content = @Content(mediaType = MediaType.APPLICATION_XML, schema = @Schema(anyOf = {ErrorResponse.class, ValidationResult.class}, implementation = ErrorResponse.class), + examples = {@ExampleObject(name = "ErrorResponse", description = "errorResponse", + value = "\n" + + "\n" + + "d4a28d16-bf5e-4332-aca7-eb10b09ddf48\n" + + "\n" + + " E-APS24003\n" + + " Error while transmitting request\n" + + "\n" + + ""), + @ExampleObject(name = "ValidationResult", + value = "\n" + + "\n" + + " ALL\n" + + " \n" + + " ERROR\n" + + " SCHMTRN-1\n" + + " [BR-CO-16]-Amount due for payment (BT-115) = Invoice total amount with VAT (BT-112) -Paid amount (BT-113) +Rounding amount (BT-114).\n" + + " /*:CreditNote[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2'][1]/*:LegalMonetaryTotal[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2'][1]\n" + + " \n" + + " \n" + + " ERROR\n" + + " SCHMTRN-1\n" + + " [BR-CO-16]-Amount due for payment (BT-115) = Invoice total amount with VAT (BT-112) -Paid amount (BT-113) +Rounding amount (BT-114).\n" + + " /*:CreditNote[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2'][1]/*:LegalMonetaryTotal[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2'][1]\n" + + " \n" + + " cfa510ec-2465-4a41-9289-db1290936785\n" + + "")})), + @ApiResponse(responseCode = "401", description = "Unauthorized") } ) public Response sendStandardBusinessDocument( @@ -135,8 +145,8 @@ public class OutboxResource { mediaType = MediaType.APPLICATION_XML, examples = @ExampleObject(name ="NemHandel eDelivery-compliant document", summary = "NemHandel eDelivery-compliant document", - description = "This is the structure of NemHandel eDelivery-compliant document with an " + - "ASiC-E container in the StandardBusinessDocumentHeader", + description = "This is the structure of NemHandel eDelivery-compliant document with a Nemhandel e-Delivery document signature " + + "in the StandardBusinessDocumentHeader", value = "\n" + " \n" + " 1.0\n" + @@ -164,7 +174,8 @@ public class OutboxResource { " \n" + " \n" + " NEMHANDEL_EDELIVERY_SIGNATURE\n" + - " \n" + + " \n" + + " nemhandel-edelivery-1.2\n" + " \n" + " \n" + " \n" + @@ -186,7 +197,7 @@ public class OutboxResource { description = "This is a paged API over outbox messages (i.e. messages sent through this AP).", tags = "Outbox", responses = { - @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = ListMessageResponse.class))), + @ApiResponse(responseCode = "200", description = "A list of outbox messages", content = @Content(schema = @Schema(implementation = ListMessageResponse.class))), @ApiResponse(responseCode = "401", description = "Unauthorized") }) public Response listOutboxMessages( @@ -197,6 +208,28 @@ public class OutboxResource { return Response.ok(dataResponse, MediaType.APPLICATION_XML_TYPE).build(); } + @GET + @Path("/{uuid}") + @Produces(MediaType.APPLICATION_OCTET_STREAM) + @Operation(summary = "Fetches a specific message from the outbox.", + description = "Returns the actual message as binary. Note that the message has been signed by C2 (assuming no validation errors) and thus may not match the message which was sent from C1 exactly", + tags = "Outbox", + responses = { + @ApiResponse(responseCode = "200", description = "The actual message as a binary octet stream"), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "404", description = "Message with the specified UUID does not exist " + + "(for security purposes this is also the result if specifying a message uuid tied to " + + "another account)") + }) + public Response getMessageContent( + @Parameter(description = "UUID of the message to fetch") + @PathParam("uuid") String uuid) { + logger.debug("Trying to get specific doc with uuid {}", uuid); + Account account = SecurityContextHolder.getSecurityContext().getAccount(); + NemhandelLog.WithMDC.cleanMDC(); + return Response.status(Response.Status.OK).entity(getOutboxMessagesHandler.getMessageContent(uuid, account)).build(); + } + @GET @Produces(MediaType.APPLICATION_XML) @Path("/{messageUUID}/document-log") @@ -204,7 +237,8 @@ public class OutboxResource { description = "Returns errors, warnings and general info, if any, related to the specified message", tags = "Outbox", responses = { - @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = MessageLogResponse.class))), + @ApiResponse(responseCode = "200", description = "The document log related to the outbox message", + content = @Content(schema = @Schema(implementation = MessageLogResponse.class))), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "404", description = "Message with the specified UUID does not exist " + "(for security purposes this is also the result if specifying a message uuid tied to " + @@ -217,6 +251,25 @@ public class OutboxResource { } + @GET + @Produces(MediaType.APPLICATION_XML) + @Path("/{messageUUID}/with-responses") + @Operation(summary = "Fetches the original document and a list of all responses tied to document", + tags = "Outbox", + responses = { + @ApiResponse(responseCode = "200", description = "The original document and a list of all responses tied to the document", + content = @Content(schema = @Schema(implementation = MessageAndResponses.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "404", description = "Message with the specified UUID does not exist " + + "(for security purposes this is also the result if specifying a message uuid tied to " + + "another account)") + }) + public Response getMessageAndResponses(@PathParam("messageUUID") String uuid) { + Account account = SecurityContextHolder.getSecurityContext().getAccount(); + MessageAndResponses dataResponse = getOutboxMessagesHandler.getMessageAndAllResponses(account, uuid); + return Response.ok(dataResponse, MediaType.APPLICATION_XML_TYPE).build(); + } + @GET @Path("/{messageUUID}/status") @Produces(MediaType.APPLICATION_XML) @@ -224,7 +277,8 @@ public class OutboxResource { description = "Returns the status of the specified message", tags = "Outbox", responses = { - @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = MessageLogResponse.class))), + @ApiResponse(responseCode = "200", description = "The status of the outbox message", + content = @Content(schema = @Schema(implementation = MessageModelResponse.class))), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "404", description = "Message with the specified UUID does not exist " + "(for security purposes this is also the result if specifying a message uuid tied to " + diff --git a/src/main/java/dk/erst/oxalis/as4/signature/SignatureException.java b/src/main/java/dk/erst/oxalis/as4/signature/SignatureException.java new file mode 100644 index 0000000..1dc54ec --- /dev/null +++ b/src/main/java/dk/erst/oxalis/as4/signature/SignatureException.java @@ -0,0 +1,54 @@ +package dk.erst.oxalis.as4.signature; + +/** + * Exception thrown when an unexpected error occurs during signature. + */ +public class SignatureException extends Exception { + + /** + * Constructs a new empty signature exception. + */ + public SignatureException() { + } + + /** + * Constructs a new signature exception with the specified detail message. + * + * @param message the detail message. + */ + public SignatureException(String message) { + super(message); + } + + /** + * Constructs a new signature exception with the specified detail message and cause. + * + * @param message the detail message. + * @param cause the cause of the exception. + */ + public SignatureException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new signature exception with the specified {@link Throwable} cause. + * + * @param cause the cause of the exception. + */ + public SignatureException(Throwable cause) { + super(cause); + } + + /** + * Constructs a new signature exception with the specified detail message, cause, + * suppression enabled or disabled, and writable stack trace enabled or disabled. + * + * @param message the detail message. + * @param cause the cause of the exception. + * @param enableSuppression whether suppression is enabled or disabled. + * @param writableStackTrace whether the stack trace should be writable. + */ + public SignatureException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/src/main/java/dk/erst/oxalis/as4/signature/SignatureFactory.java b/src/main/java/dk/erst/oxalis/as4/signature/SignatureFactory.java index 8027ee8..ee04454 100644 --- a/src/main/java/dk/erst/oxalis/as4/signature/SignatureFactory.java +++ b/src/main/java/dk/erst/oxalis/as4/signature/SignatureFactory.java @@ -5,6 +5,7 @@ import dk.erst.oxalis.as4.util.SBDMessageContext; import org.apache.xml.security.exceptions.XMLSecurityException; import org.xml.sax.SAXException; +import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.xpath.XPathExpressionException; import java.io.IOException; @@ -18,15 +19,9 @@ public interface SignatureFactory { * Signs a document using the XAdES signature format. * * @param sbdDocument the SBD document to be signed - * @param originalDocument the original document that is being signed - * @param transferDelegation the transfer delegation associated with the document * @param messageContext the SBD message context * @return the signed document as a byte array - * @throws IOException if an I/O error occurs during the signing process - * @throws SAXException if an error occurs while parsing the document - * @throws TransformerException if an error occurs during the transformation process - * @throws XPathExpressionException if an error occurs during XPath expression evaluation - * @throws XMLSecurityException This exception is thrown if canonicalization fails (e.g. malformed input XML). Also thrown if canonicalization engine cannot be initialized. + * @throws SignatureException if an exception occurs while signing the document */ - public byte[] signDocument(byte[] sbdDocument, byte[] originalDocument, byte[] transferDelegation, SBDMessageContext messageContext) throws IOException, SAXException, TransformerException, XPathExpressionException, XMLSecurityException; + public byte[] signDocument(byte[] sbdDocument, SBDMessageContext messageContext) throws SignatureException; } diff --git a/src/main/java/dk/erst/oxalis/as4/signature/SignatureFactoryImpl.java b/src/main/java/dk/erst/oxalis/as4/signature/SignatureFactoryImpl.java index eaf72b1..7cdd5cb 100644 --- a/src/main/java/dk/erst/oxalis/as4/signature/SignatureFactoryImpl.java +++ b/src/main/java/dk/erst/oxalis/as4/signature/SignatureFactoryImpl.java @@ -1,112 +1,130 @@ package dk.erst.oxalis.as4.signature; import com.google.inject.Provider; +import dk.erst.oxalis.as4.EDeliverySpecification; import dk.erst.oxalis.as4.error.ErrorCodes; import dk.erst.oxalis.as4.util.DocumentUtil; import dk.erst.oxalis.as4.util.SBDMessageContext; +import dk.erst.oxalis.as4.util.SBDPayloadExtractor; import dk.erst.oxalis.as4.util.XPathUtil; import dk.erst.oxalis.as4.validation.signature.SignatureValidator; -import eu.europa.esig.dss.asic.xades.ASiCWithXAdESSignatureParameters; -import eu.europa.esig.dss.asic.xades.signature.ASiCWithXAdESService; -import eu.europa.esig.dss.enumerations.ASiCContainerType; -import eu.europa.esig.dss.enumerations.SignatureLevel; -import eu.europa.esig.dss.model.*; -import eu.europa.esig.dss.model.x509.CertificateToken; +import eu.europa.esig.dss.enumerations.MimeTypeEnum; +import eu.europa.esig.dss.model.DSSDocument; +import eu.europa.esig.dss.model.InMemoryDocument; +import eu.europa.esig.dss.model.SignatureValue; +import eu.europa.esig.dss.model.ToBeSigned; import eu.europa.esig.dss.token.AbstractKeyStoreTokenConnection; import eu.europa.esig.dss.token.DSSPrivateKeyEntry; +import eu.europa.esig.dss.xades.XAdESSignatureParameters; +import eu.europa.esig.dss.xades.signature.XAdESService; import network.oxalis.vefa.peppol.sbdh.Ns; import org.apache.cxf.helpers.MapNamespaceContext; -import org.apache.xml.security.exceptions.XMLSecurityException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; -import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilder; -import javax.xml.transform.TransformerException; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.util.ArrayList; +import java.nio.charset.StandardCharsets; import java.util.Base64; -import java.util.Date; -import java.util.List; /** - * Implementation of the signature ASiC creation. + * Signature factory which can sign Standard Business Documents in compliance with the Nemhandel e-Delivery 1.2 specification */ public class SignatureFactoryImpl implements SignatureFactory { - private final ASiCWithXAdESService signatureService; + private final Provider signatureServiceProvider; private final AbstractKeyStoreTokenConnection signatureToken; private final DSSPrivateKeyEntry signaturePrivateKey; private final Provider documentBuilderProvider; + private final SBDPayloadExtractor payloadExtractor; + private final Provider signatureParametersProvider; /** * Instantiates a new Signature factory. * - * @param signatureService the signature service, see {@link ASiCWithXAdESService} + * @param signatureServiceProvider the signature service, see {@link XAdESService} * @param signatureToken the signature token, see {@link AbstractKeyStoreTokenConnection} * @param signaturePrivateKey the signature private key, see {@link DSSPrivateKeyEntry} * @param documentBuilderProvider the document builder provider, see {@link DocumentBuilder} + * @param payloadExtractor The extractor to use in extracting the payload from the SBD. + * @param signatureParametersProvider A provider of {@link XAdESSignatureParameters} to use for the signing process. */ - public SignatureFactoryImpl(ASiCWithXAdESService signatureService, AbstractKeyStoreTokenConnection signatureToken, DSSPrivateKeyEntry signaturePrivateKey, Provider documentBuilderProvider) { - this.signatureService = signatureService; + public SignatureFactoryImpl(Provider signatureServiceProvider, + AbstractKeyStoreTokenConnection signatureToken, + DSSPrivateKeyEntry signaturePrivateKey, + Provider documentBuilderProvider, + SBDPayloadExtractor payloadExtractor, + Provider signatureParametersProvider) { + this.signatureServiceProvider = signatureServiceProvider; this.signatureToken = signatureToken; this.signaturePrivateKey = signaturePrivateKey; this.documentBuilderProvider = documentBuilderProvider; + this.payloadExtractor = payloadExtractor; + this.signatureParametersProvider = signatureParametersProvider; } /** - * Creates a signed ASiC container from a SBD, the original SBD (if present) and a transfer delegation and adds it to returned SBD. - * Note: If the {@code originalDocument} is from C1 and unsigned, it is not included in the ASiC container. + * Signs the provided SBD {@code document} in compliance with the Nemhandel e-Delivery 1.2 specification. + *

+ * The signature is added to the provided SBD header as a {@code } with Type = NEMHANDEL_EDELIVERY_SIGNATURE. + *

+ * Note: only the payload of the SBD is signed and it is canonicalized using Exclusive XML Canonicalization v1.0 specification (xml-exc-c14n for short, algorithm name "http://www.w3.org/2001/10/xml-exc-c14n#") before signing. * - * @param document the SBD the ASiC container is added to - * @param originalDocument the original SBD if present - * @param transferDelegation the transfer delegation + * @param document the SBD containing the payload to sign. * @param messageContext the message context {@link SBDMessageContext} - * @return the signed SBD a byte array containing the signed SBD - * @throws IOException if an I/O error occurs during the signing process - * @throws SAXException if an error occurs while parsing the document - * @throws TransformerException if an error occurs during the transformation process - * @throws XPathExpressionException if an error occurs during XPath expression evaluation + * @return the SBD as a byte array containing with the signature added as a business scope in the SBD header. + * @throws SignatureException if an error occurs during the signing process */ @Override - public byte[] signDocument(byte[] document, byte[] originalDocument, byte[] transferDelegation, SBDMessageContext messageContext) throws IOException, SAXException, TransformerException, XPathExpressionException, XMLSecurityException { - // Create ASiC container Xpath + public byte[] signDocument(byte[] document, SBDMessageContext messageContext) throws SignatureException { + if (document == null || document.length == 0) { + throw new IllegalArgumentException(ErrorCodes.SIGNING_EDELIVERY_SBD_EMPTY_OR_NULL.formatErrorMessage()); + } + XPathFactory xPathFactory = XPathFactory.newInstance(); XPath xpath = xPathFactory.newXPath(); MapNamespaceContext nsCtx = new MapNamespaceContext(); nsCtx.addNamespace("sbd", Ns.SBDH); xpath.setNamespaceContext(nsCtx); - // Create ASiC container - byte[] cleanSbd = removeAsicInSbdh(document, xpath); - cleanSbd = DocumentUtil.canonicalize(cleanSbd); - byte [] asicContainer = createNemhandelEDeliverySignature(cleanSbd, originalDocument, transferDelegation, messageContext); - DocumentBuilder db = documentBuilderProvider.get(); - try (ByteArrayInputStream bis = new ByteArrayInputStream(cleanSbd)) { + try (ByteArrayInputStream bis = new ByteArrayInputStream(document)) { Document doc = db.parse(bis); - InsertAsicInSbdh(doc, asicContainer, xpath); + + Document payload = payloadExtractor.extractPayload(doc); + + /** + * NOTE: we need to serialize to byte-array before canonicalization, + * Otherwise namespaces may not be correctly available during canonicalization which will lead to validation errors. + * To avoid this SBDPayloadExtractor implementation must be changed to e.g. use XPath to extract payload + */ + DSSDocument unsigned = new InMemoryDocument(DocumentUtil.canonicalize(DocumentUtil.toByteArray(payload)), SignatureValidator.XAdES_SIGNATURE_PAYLOAD_NAME, MimeTypeEnum.XML); + DSSDocument signature = createXAdESSignature(unsigned, signatureParametersProvider.get()); + + appendNemhandelSignatureBusinessScope(doc, signature, xpath); // Convert document to byte array byte[] updatedDocByteArray = DocumentUtil.toByteArray(doc); return updatedDocByteArray; + } catch (Exception e) { + throw new SignatureException("Error while signing document", e); } } /** - * Adds the ASiC container to the SBD in the NEMHANDEL_EDELIVERY_SIGNATURE Scope. + * Adds the Nemhandel e-Delivery document signature to the SBD in the NEMHANDEL_EDELIVERY_SIGNATURE Scope. * - * @param document the SBD the ASiC container should be added to - * @param asicContainer a byte array containing the ASiC container + * @param document the SBD the signature should be added to + * @param signature the Nemhandel e-Delivery signature to include in the business scope * @param xpath the xpath instance * @throws XPathExpressionException if an error occurs during XPath expression evaluation */ - private void InsertAsicInSbdh(Document document, byte[] asicContainer, XPath xpath) throws XPathExpressionException { + private void appendNemhandelSignatureBusinessScope(Document document, DSSDocument signature, XPath xpath) throws XPathExpressionException, IOException { Element scope = document.createElementNS(Ns.SBDH, "Scope"); Element type = document.createElementNS(Ns.SBDH, "Type"); @@ -114,9 +132,18 @@ public class SignatureFactoryImpl implements SignatureFactory { scope.appendChild(type); Element instanceId = document.createElementNS(Ns.SBDH, "InstanceIdentifier"); - instanceId.setTextContent(Base64.getEncoder().encodeToString(asicContainer)); + try(ByteArrayOutputStream bos = new ByteArrayOutputStream()) { + signature.writeTo(bos); + byte[] signatureContent = bos.toByteArray(); + String encoded = new String(Base64.getEncoder().encode(signatureContent), StandardCharsets.UTF_8); + instanceId.setTextContent(encoded); + } scope.appendChild(instanceId); + Element identifier = document.createElementNS(Ns.SBDH, "Identifier"); + identifier.setTextContent(EDeliverySpecification.NEMHANDEL_EDELIVERY_1_2.value()); + scope.appendChild(identifier); + String expr = "/sbd:StandardBusinessDocument/sbd:StandardBusinessDocumentHeader/sbd:BusinessScope"; Node node = XPathUtil.evaluateXPathNode(document, xpath, expr); if(node != null) { @@ -124,98 +151,11 @@ public class SignatureFactoryImpl implements SignatureFactory { } } - /** - * Removes the ASiC container if present. - * This is done to ensure that the signed document itself does not contain the old ASiC container if any is present. - * - * @param document the SBD the ASiC container is added to - * @param xpath the xpath instance - * @return the SBD a byte array containing the SBD without the ASiC container - * @throws IOException if an I/O error occurs during the creation of the input stream - * @throws SAXException if an error occurs while parsing the document - * @throws TransformerException if an error occurs during the transformation process - * @throws XPathExpressionException if an error occurs during XPath expression evaluation - */ - private byte[] removeAsicInSbdh(byte[] document, XPath xpath) throws XPathExpressionException, IOException, SAXException, TransformerException { - - if (document == null || document.length == 0) { - throw new IllegalArgumentException(ErrorCodes.SIGNING_EDELIVERY_SBD_EMPTY_OR_NULL.formatErrorMessage()); - } - DocumentBuilder db = documentBuilderProvider.get(); - try (ByteArrayInputStream bis = new ByteArrayInputStream(document)) { - Document doc = db.parse(bis); - - String exprAsic = "/sbd:StandardBusinessDocument/sbd:StandardBusinessDocumentHeader/sbd:BusinessScope/sbd:Scope[sbd:Type='NEMHANDEL_EDELIVERY_SIGNATURE']"; - Node nodeAsic = XPathUtil.evaluateXPathNode(doc, xpath, exprAsic); - if(nodeAsic != null) { - nodeAsic.getParentNode().removeChild(nodeAsic); - } - - byte[] res = DocumentUtil.toByteArray(doc); - return res; - } - } - - /** - * Creates a Nemhandel E Delivery Signature ASiC container from a SBD, the original SBD (if present) and a transfer delegation. - * Note: If the {@code originalDocument} is from C1 and unsigned, it is not included in the ASiC container. - * - * @param sbd the SBD the ASiC container is added to - * @param originalDoc the original SBD if present - * @param transferDelegation the transfer delegation - * @param messageContext the message context {@link SBDMessageContext} - * @return the ASiC container a byte array containing the signed ASiC container - * @throws IOException if an I/O error occurs during the signing process - * @throws SAXException if an error occurs while parsing the document - * @throws TransformerException if an error occurs during the transformation process - * @throws XPathExpressionException if an error occurs during XPath expression evaluation - */ - private byte[] createNemhandelEDeliverySignature(byte[] sbd, byte[] originalDoc, byte[] transferDelegation, SBDMessageContext messageContext) { - List documentsToSign = new ArrayList<>(); - if (sbd != null && sbd.length > 0) { - documentsToSign.add(new InMemoryDocument(sbd, SignatureValidator.ASIC_SBD_FILENAME, MimeType.XML)); - } else { - throw new IllegalArgumentException(ErrorCodes.SIGNING_EDELIVERY_SBD_EMPTY_OR_NULL.formatErrorMessage()); - } - - if(originalDoc != null && originalDoc.length > 0) { - documentsToSign.add(new InMemoryDocument(originalDoc, SignatureValidator.ASIC_ORIGINAL_SBD_FILENAME, MimeType.XML)); - } else if(messageContext == null || (!messageContext.isInternalMessageLevelResponse() && messageContext.isDocumentSignatureRequired())) { - // only allow original to be null for internal MLR or when C1s SBD is unsigned. The messageContext.isDocumentSignatureRequired() controls when an SBD is required to be signed. - // Currently this is only the SBD sent by C1. For everything else the original SBD should be present - throw new IllegalArgumentException(ErrorCodes.SIGNING_EDELIVERY_ORIGINAL_SBD_EMPTY_OR_NULL.formatErrorMessage()); - } - - if(transferDelegation != null && transferDelegation.length > 0) { - documentsToSign.add(new InMemoryDocument(transferDelegation, SignatureValidator.ASIC_TRANSFER_DELEGATION_FILENAME, MimeType.XML)); - } else { - throw new IllegalArgumentException(ErrorCodes.SIGNING_EDELIVERY_TRANSFER_DELEGATION_EMPTY_OR_NULL.formatErrorMessage()); - } - - DSSDocument asic = createASiCContainer(documentsToSign, getSignatureParameters(signaturePrivateKey.getCertificate(), signaturePrivateKey.getCertificateChain())); - - try(ByteArrayOutputStream bos = new ByteArrayOutputStream()) { - asic.writeTo(bos); - return bos.toByteArray(); - } catch (IOException e) { - throw new RuntimeException(ErrorCodes.SIGNING_EDELIVERY_ASIC_CONTAINER_TRANSFORMING_ERROR.formatErrorMessage()); - } - } - - private DSSDocument createASiCContainer(List documentsToSign, ASiCWithXAdESSignatureParameters signatureParameters) { - ToBeSigned dataToSign = signatureService.getDataToSign(documentsToSign, signatureParameters); + private DSSDocument createXAdESSignature(DSSDocument unsigned, XAdESSignatureParameters signatureParameters) { + XAdESService service = signatureServiceProvider.get(); + ToBeSigned dataToSign = service.getDataToSign(unsigned, signatureParameters); SignatureValue signatureValue = signatureToken.sign(dataToSign, signatureParameters.getDigestAlgorithm(), signaturePrivateKey); - DSSDocument signedDocument = signatureService.signDocument(documentsToSign, signatureParameters, signatureValue); - return signedDocument; + return service.signDocument(unsigned, signatureParameters, signatureValue); } - private ASiCWithXAdESSignatureParameters getSignatureParameters(CertificateToken certificateToken, CertificateToken[] certificateChain) { - ASiCWithXAdESSignatureParameters signatureParameters = new ASiCWithXAdESSignatureParameters(); - signatureParameters.bLevel().setSigningDate(new Date()); - signatureParameters.setSigningCertificate(certificateToken); - signatureParameters.setCertificateChain(certificateChain); - signatureParameters.setSignatureLevel(SignatureLevel.XAdES_BASELINE_B); - signatureParameters.aSiC().setContainerType(ASiCContainerType.ASiC_E); - return signatureParameters; - } } diff --git a/src/main/java/dk/erst/oxalis/as4/signature/TransferDelegationException.java b/src/main/java/dk/erst/oxalis/as4/signature/TransferDelegationException.java deleted file mode 100644 index 1b2409f..0000000 --- a/src/main/java/dk/erst/oxalis/as4/signature/TransferDelegationException.java +++ /dev/null @@ -1,52 +0,0 @@ -package dk.erst.oxalis.as4.signature; - -/** - * Exception for errors relating to the creation of transfer delegations. - */ -public class TransferDelegationException extends Throwable { - /** - * Instantiates a new Transfer delegation exception. - */ - public TransferDelegationException() { - } - - /** - * Instantiates a new Transfer delegation exception with a message. - * - * @param message the Transfer delegation exception message - */ - public TransferDelegationException(String message) { - super(message); - } - - /** - * Instantiates a new Transfer delegation exception with a message and root cause. - * - * @param message the Transfer delegation exception message - * @param cause the Transfer delegation exception cause - */ - public TransferDelegationException(String message, Throwable cause) { - super(message, cause); - } - - /** - * Instantiates a new Transfer delegation exception with a root cause. - * - * @param cause the Transfer delegation exception cause - */ - public TransferDelegationException(Throwable cause) { - super(cause); - } - - /** - * Instantiates a new Transfer delegation exception with a message, a root cause and a flag for if we enable suppression and a flag for if the stacktrace is writable. - * - * @param message the Transfer delegation exception message - * @param cause the Transfer delegation exception cause - * @param enableSuppression the enable suppression see {@link Throwable} - * @param writableStackTrace the writable stack trace see {@link Throwable} - */ - public TransferDelegationException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } -} diff --git a/src/main/java/dk/erst/oxalis/as4/signature/TransferDelegationFactory.java b/src/main/java/dk/erst/oxalis/as4/signature/TransferDelegationFactory.java deleted file mode 100644 index 510c26d..0000000 --- a/src/main/java/dk/erst/oxalis/as4/signature/TransferDelegationFactory.java +++ /dev/null @@ -1,24 +0,0 @@ -package dk.erst.oxalis.as4.signature; - -import javax.xml.bind.JAXBException; - -/** - * The interface Transfer delegation factory provides a contract for implementing classes that wants to create a transfer delegation. - */ -public interface TransferDelegationFactory { - /** - * The constant ASIC_TRANSFER_DELEGATION_SCHEME_CVR. - */ - String ASIC_TRANSFER_DELEGATION_SCHEME_CVR = "DK:CVR"; - - /** - * Create transfer delegation from a sender CVR and receiver CVR. - * - * @param senderCVR the sender cvr - * @param receiverCVR the receiver cvr - * @return a byte array contains the transfer delegation - * @throws JAXBException if an error occurs during the jaxb marshalling - * @throws TransferDelegationException if an error occurs during the creation of the transfer delegation - */ - public byte[] createTransferDelegation(String senderCVR, String receiverCVR) throws JAXBException, TransferDelegationException; -} diff --git a/src/main/java/dk/erst/oxalis/as4/signature/TransferDelegationFactoryImpl.java b/src/main/java/dk/erst/oxalis/as4/signature/TransferDelegationFactoryImpl.java deleted file mode 100644 index 2078b6a..0000000 --- a/src/main/java/dk/erst/oxalis/as4/signature/TransferDelegationFactoryImpl.java +++ /dev/null @@ -1,75 +0,0 @@ -package dk.erst.oxalis.as4.signature; - -import dk.erst.oxalis.as4.error.ErrorCodes; -import dk.erst.oxalis.transferdelegation.ObjectFactory; -import dk.erst.oxalis.transferdelegation.Receiver; -import dk.erst.oxalis.transferdelegation.Sender; -import dk.erst.oxalis.transferdelegation.TransferDelegation; -import org.apache.commons.lang3.StringUtils; - -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBElement; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Marshaller; -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -/** - * Implementation of the transfer delegation creation. - */ -public class TransferDelegationFactoryImpl implements TransferDelegationFactory{ - - private JAXBContext jaxbContext; - - /** - * Instantiates a new Transfer delegation factory. - * - * @throws JAXBException if an error occurs while creating a JAXBContext. - */ - public TransferDelegationFactoryImpl() throws JAXBException { - jaxbContext = JAXBContext.newInstance(TransferDelegation.class); - } - - /** - * Create transfer delegation from a sender CVR and receiver CVR. - * - * @param senderCVR the sender cvr - * @param receiverCVR the receiver cvr - * @return a byte array contains the transfer delegation - * @throws JAXBException if an error occurs during the JAXB marshalling - * @throws TransferDelegationException if an error occurs during the creation of the transfer delegation - */ - public byte[] createTransferDelegation(String senderCVR, String receiverCVR) throws JAXBException, TransferDelegationException { - if(senderCVR == null || StringUtils.isBlank(senderCVR)) { - throw new TransferDelegationException(ErrorCodes.SIGNING_EDELIVERY_TRANSFER_DELEGATION_EMPTY_OR_NULL_SENDER_CVR.formatErrorMessage()); - } else if (receiverCVR == null || StringUtils.isBlank(receiverCVR)) { - throw new TransferDelegationException(ErrorCodes.SIGNING_EDELIVERY_TRANSFER_DELEGATION_EMPTY_OR_NULL_RECEIVER_CVR.formatErrorMessage()); - } - - Sender tdSender = new Sender(); - tdSender.setSchemeID(TransferDelegationFactory.ASIC_TRANSFER_DELEGATION_SCHEME_CVR); - tdSender.setValue(senderCVR); - - Receiver tdReceiver = new Receiver(); - tdReceiver.setSchemeID(TransferDelegationFactory.ASIC_TRANSFER_DELEGATION_SCHEME_CVR); - tdReceiver.setValue(receiverCVR); - - TransferDelegation td = new TransferDelegation(); - td.setSender(tdSender); - td.setReceiver(tdReceiver); - - ObjectFactory of = new ObjectFactory(); - - Marshaller mar = jaxbContext.createMarshaller(); - mar.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); - byte[] transferDelegation; - try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) { - JAXBElement element = of.createTransferDelegation(td); - mar.marshal(element, bos); - transferDelegation = bos.toByteArray(); - return transferDelegation; - } catch (IOException e) { - throw new RuntimeException(ErrorCodes.SIGNING_EDELIVERY_TRANSFER_DELEGATION_MARSHALLING_ERROR.formatErrorMessage()); - } - } -} diff --git a/src/main/java/dk/erst/oxalis/as4/signature/XAdESServiceProvider.java b/src/main/java/dk/erst/oxalis/as4/signature/XAdESServiceProvider.java new file mode 100644 index 0000000..b7fcad6 --- /dev/null +++ b/src/main/java/dk/erst/oxalis/as4/signature/XAdESServiceProvider.java @@ -0,0 +1,28 @@ +package dk.erst.oxalis.as4.signature; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import eu.europa.esig.dss.validation.CertificateVerifier; +import eu.europa.esig.dss.xades.signature.XAdESService; + +/** + * Provider for constructing {@link XAdESService} instances. + *

+ * Note: It is not completely clear from the DSS documentation whether {@link XAdESService} is thread-safe. + * They mention that {@link CertificateVerifier} (which is a constructor parameter for {@link XAdESService}) is not always thread-safe - especially when using adjunct certificate sources. + * While we don't currently use adjunct certificate sources, we will still err on the side of caution to be safe and construct a new instance for each signing operation, since there does not seem to be much cost associated with constructing a new {@link XAdESService}. + */ +public class XAdESServiceProvider implements Provider { + + private Provider certificateVerifierProvider; + + @Inject + public XAdESServiceProvider(Provider certificateVerifierProvider) { + this.certificateVerifierProvider = certificateVerifierProvider; + } + + @Override + public XAdESService get() { + return new XAdESService(certificateVerifierProvider.get()); + } +} diff --git a/src/main/java/dk/erst/oxalis/as4/signature/XAdESSignatureParametersProvider.java b/src/main/java/dk/erst/oxalis/as4/signature/XAdESSignatureParametersProvider.java new file mode 100644 index 0000000..88ef06d --- /dev/null +++ b/src/main/java/dk/erst/oxalis/as4/signature/XAdESSignatureParametersProvider.java @@ -0,0 +1,46 @@ +package dk.erst.oxalis.as4.signature; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import eu.europa.esig.dss.enumerations.SignatureLevel; +import eu.europa.esig.dss.enumerations.SignaturePackaging; +import eu.europa.esig.dss.token.DSSPrivateKeyEntry; +import eu.europa.esig.dss.xades.XAdESSignatureParameters; + +import java.util.Date; + +/** + * Provider for constructing {@link XAdESSignatureParameters} instances. + */ +public class XAdESSignatureParametersProvider implements Provider { + + private final DSSPrivateKeyEntry signaturePrivateKey; + + /** + * Instantiates a new XAdESSignatureParametersProvider with the given dependencies. + * The dependencies are automatically injected by Guice.
+ *
+ * @param signaturePrivateKey The private key from which to extract the certificate and certificate chain to include in the signature. + */ + @Inject + public XAdESSignatureParametersProvider(DSSPrivateKeyEntry signaturePrivateKey) { + this.signaturePrivateKey = signaturePrivateKey; + } + + /** + * Provides an instance of {@link XAdESSignatureParameters} suitable for use in creating a + * Nemhandel e-Delivery 1.2 compliant signature.
+ *
+ * @return the instantiated {@link XAdESSignatureParameters} object. + */ + @Override + public XAdESSignatureParameters get() { + XAdESSignatureParameters signatureParameters = new XAdESSignatureParameters(); + signatureParameters.bLevel().setSigningDate(new Date()); + signatureParameters.setSigningCertificate(signaturePrivateKey.getCertificate()); + signatureParameters.setCertificateChain(signaturePrivateKey.getCertificateChain()); + signatureParameters.setSignatureLevel(SignatureLevel.XAdES_BASELINE_B); + signatureParameters.setSignaturePackaging(SignaturePackaging.DETACHED); + return signatureParameters; + } +} diff --git a/src/main/java/dk/erst/oxalis/as4/util/DocumentSender.java b/src/main/java/dk/erst/oxalis/as4/util/DocumentSender.java index 43bc261..727f918 100644 --- a/src/main/java/dk/erst/oxalis/as4/util/DocumentSender.java +++ b/src/main/java/dk/erst/oxalis/as4/util/DocumentSender.java @@ -16,19 +16,7 @@ public interface DocumentSender { * @param message the message to send. * @param messageContext context object used to pass additional information when sending an SBD. * @return the result of the transmission as a {@link TransmissionResponse} object. - * @throws OutboundException + * @throws OutboundException if an error occurs while sending the message */ TransmissionResponse send(Message message, SBDMessageContext messageContext) throws OutboundException; - - /** - * Signs a message and returns the signed document as a byte array. - * - * @param sbd the document to sign. - * @param originalDocument the original document, if any, to include in the signature. - * @param receiverCVR the receivers CVR number. - * @param messageContext context object used to pass additional information when sending an SBD. - * @return the signed document as a byte array. - * @throws OutboundException if an error occurs while signing the message or creating the transfer delegation. - */ - byte[] signMessage(byte[] sbd, byte[] originalDocument, String receiverCVR, SBDMessageContext messageContext) throws OutboundException; } diff --git a/src/main/java/dk/erst/oxalis/as4/util/DocumentUtil.java b/src/main/java/dk/erst/oxalis/as4/util/DocumentUtil.java index 8267d71..fbd52ae 100644 --- a/src/main/java/dk/erst/oxalis/as4/util/DocumentUtil.java +++ b/src/main/java/dk/erst/oxalis/as4/util/DocumentUtil.java @@ -3,6 +3,7 @@ package dk.erst.oxalis.as4.util; import org.apache.xml.security.c14n.Canonicalizer; import org.apache.xml.security.exceptions.XMLSecurityException; import org.w3c.dom.Document; +import org.w3c.dom.Node; import javax.xml.XMLConstants; import javax.xml.transform.Transformer; @@ -53,7 +54,7 @@ public class DocumentUtil { * @return The input XML-document as a byte array, canonicalized according to "xml-exc-c14n" specification * @throws XMLSecurityException This exception is thrown if canonicalization fails (e.g. malformed input XML). Also * thrown if canonicalization engine cannot be initialized. - * @throws IOException Should never be thrown, since internally the method uses ByteArrayOutputStream which will + * @throws IOException Should never be thrown, since internally the method uses ByteArrayOutputStream which will not * throw this exception (but to satisfy compiler it is added). */ public static byte[] canonicalize(byte[] xml) throws XMLSecurityException, IOException { @@ -66,5 +67,4 @@ public class DocumentUtil { } } - } diff --git a/src/main/java/dk/erst/oxalis/as4/util/OxalisDocumentSender.java b/src/main/java/dk/erst/oxalis/as4/util/OxalisDocumentSender.java index 952bdf2..b49865b 100644 --- a/src/main/java/dk/erst/oxalis/as4/util/OxalisDocumentSender.java +++ b/src/main/java/dk/erst/oxalis/as4/util/OxalisDocumentSender.java @@ -6,38 +6,34 @@ import com.google.inject.Singleton; import dk.erst.oxalis.as4.error.ErrorCodes; import dk.erst.oxalis.as4.handlers.OutboundException; import dk.erst.oxalis.as4.handlers.OutboundValidationException; +import dk.erst.oxalis.as4.handlers.dto.ValidationResult; import dk.erst.oxalis.as4.mode.Mode; import dk.erst.oxalis.as4.persistence.model.Message; import dk.erst.oxalis.as4.persistence.model.MessageLog; import dk.erst.oxalis.as4.persistence.model.NemhandelLog; +import dk.erst.oxalis.as4.signature.SignatureException; import dk.erst.oxalis.as4.signature.SignatureFactory; -import dk.erst.oxalis.as4.signature.TransferDelegationException; -import dk.erst.oxalis.as4.signature.TransferDelegationFactoryImpl; import dk.erst.oxalis.as4.validation.MessageValidator; import dk.erst.oxalis.as4.validation.ValidationException; -import dk.erst.oxalis.as4.handlers.dto.ValidationResult; import dk.erst.oxalis.as4.validation.ValidationType; import dk.erst.oxalis.as4.validation.signature.SignatureValidatorImpl; import network.oxalis.api.lang.OxalisContentException; import network.oxalis.api.lang.OxalisTransmissionException; -import network.oxalis.api.lookup.LookupService; import network.oxalis.api.outbound.TransmissionRequest; import network.oxalis.api.outbound.TransmissionResponse; import network.oxalis.api.outbound.Transmitter; import network.oxalis.as4.lang.OxalisAs4TransmissionException; -import network.oxalis.commons.header.SbdhHeaderParser; import network.oxalis.outbound.transmission.TransmissionRequestBuilder; -import network.oxalis.sniffer.PeppolStandardBusinessHeader; -import network.oxalis.sniffer.identifier.PeppolDocumentTypeId; import network.oxalis.vefa.peppol.common.lang.PeppolParsingException; -import network.oxalis.vefa.peppol.common.model.*; +import network.oxalis.vefa.peppol.common.model.DocumentTypeIdentifier; +import network.oxalis.vefa.peppol.common.model.ParticipantIdentifier; +import network.oxalis.vefa.peppol.common.model.ProcessIdentifier; import network.oxalis.vefa.peppol.lookup.api.LookupException; import network.oxalis.vefa.peppol.lookup.api.NotFoundException; import network.oxalis.vefa.peppol.sbdh.Ns; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.cxf.helpers.MapNamespaceContext; -import org.apache.xml.security.exceptions.XMLSecurityException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; @@ -46,16 +42,16 @@ import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import javax.persistence.EntityManager; -import javax.xml.bind.JAXBException; import javax.xml.parsers.DocumentBuilder; import javax.xml.transform.TransformerException; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; -import java.io.*; -import java.security.cert.X509Certificate; -import java.util.Date; +import java.io.ByteArrayInputStream; +import java.io.IOException; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static dk.erst.oxalis.as4.util.XPathUtil.evaluateXPath; @@ -72,13 +68,11 @@ public class OxalisDocumentSender implements DocumentSender { private final Transmitter transmitter; private final MessageValidator messageValidator; private final SignatureFactory signatureFactory; - private final TransferDelegationFactoryImpl transferDelegationFactoryImpl; - private final LookupService lookupService; - private final X509Certificate accessPointCertificate; - private final SbdhHeaderParser headerParser; private final Mode mode; private final Provider documentBuilderProvider; + private final Pattern OXALIS_SMP_LOOKUP_ERROR_PATTERN = Pattern.compile("^Combination of receiver \\((\\S+)\\) and document type identifier \\((\\S+)\\) is not supported\\.$"); + /** * Constructs a new instance of this class with the specified parameters. * @@ -86,10 +80,6 @@ public class OxalisDocumentSender implements DocumentSender { * @param transmitter Executes transmission requests by sending the payload to the requested destination. * @param messageValidator Performs validation for a message. * @param signatureFactory Contains the logic for creating signatures and signing documents. - * @param transferDelegationFactoryImpl Contains the logic for creating transfer delegations. - * @param lookupService An SMP lookup service. - * @param accessPointCertificate The Access Point's own certificate. - * @param headerParser For parsing of the Standard Business Document Header (SBDH). * @param mode The type mode. Is used to identify the current mode of the Access Point. * @param documentBuilderProvider {@link Provider} for providing {@link DocumentBuilder} objects. Resolved by Guice. * @param em A {@link Provider} for providing {@link EntityManager} objects. Resolved by Guice. @@ -99,10 +89,6 @@ public class OxalisDocumentSender implements DocumentSender { Provider requestBuilderProvider, Transmitter transmitter, MessageValidator messageValidator, SignatureFactory signatureFactory, - TransferDelegationFactoryImpl transferDelegationFactoryImpl, - LookupService lookupService, - X509Certificate accessPointCertificate, - SbdhHeaderParser headerParser, Mode mode, Provider documentBuilderProvider, Provider em){ @@ -111,40 +97,10 @@ public class OxalisDocumentSender implements DocumentSender { this.transmitter = transmitter; this.messageValidator = messageValidator; this.signatureFactory = signatureFactory; - this.transferDelegationFactoryImpl = transferDelegationFactoryImpl; - this.lookupService = lookupService; - this.accessPointCertificate = accessPointCertificate; - this.headerParser = headerParser; this.mode = mode; this.documentBuilderProvider = documentBuilderProvider; } - @Override - public byte[] signMessage(byte[] sbd, byte[] originalDocument, String receiverCVR, SBDMessageContext messageContext) throws OutboundException { - String senderCvr = CertificateUtil.extractCVR(accessPointCertificate); - try { - byte[] transferDelegation = transferDelegationFactoryImpl.createTransferDelegation(senderCvr, receiverCVR); - return signatureFactory.signDocument(sbd, originalDocument, transferDelegation, messageContext); - } - catch (JAXBException e) { - throw new OutboundException(messageContext.getMessageUuid(), ErrorCodes.OUTBOUND_EXCEPTION.getErrorCode(), "JAXB error while signing message", e); - } catch (IOException e) { - throw new OutboundException(messageContext.getMessageUuid(), ErrorCodes.OUTBOUND_EXCEPTION.getErrorCode(), "IO error while signing message", e); - } catch (SAXException e) { - throw new OutboundException(messageContext.getMessageUuid(), ErrorCodes.OUTBOUND_EXCEPTION.getErrorCode(), "SAX error while signing message", e); - } catch (TransformerException e) { - throw new OutboundException(messageContext.getMessageUuid(), ErrorCodes.OUTBOUND_EXCEPTION.getErrorCode(),"Transformer error while signing message", e); - } catch (XPathExpressionException e) { - throw new OutboundException(messageContext.getMessageUuid(), ErrorCodes.OUTBOUND_EXCEPTION.getErrorCode(),"XPath error while signing message", e); - } catch (TransferDelegationException e) { - throw new OutboundException(messageContext.getMessageUuid(), ErrorCodes.OUTBOUND_EXCEPTION.getErrorCode(), "Error while creating transfer delegation", e); - } catch (XMLSecurityException e) { - throw new OutboundException(messageContext.getMessageUuid(), ErrorCodes.OUTBOUND_EXCEPTION.getErrorCode(), "Error during canonicalization while signing message", e); - } - - } - - @Override public TransmissionResponse send(Message message, SBDMessageContext messageContext) throws OutboundException { NemhandelLog.WithMDC.cleanMDC(); @@ -152,8 +108,6 @@ public class OxalisDocumentSender implements DocumentSender { List messageLogList; try { - byte[] originalDocument = message.getMessageContent().getData(); - DataPairs.Tuple2, ValidationResult> tuplePair = messageValidator.validate(message, ValidationType.ALL, messageContext); messageLogList = tuplePair.t1; @@ -168,34 +122,30 @@ public class OxalisDocumentSender implements DocumentSender { // addUUID checks if a UUID is sent from C1 otherwise it adds one to MessageContent // A separate SBD is made because C2 can potentially change the original document. byte[] sbd = addUUID(message); + message.getMessageContent().setData(sbd); TransmissionRequestBuilder requestBuilder = requestBuilderProvider.get(); requestBuilder.reset(); - if(mode.isNemhandelEdeliveryMode()) { - Endpoint endpoint = lookupEndpoint(message); - String receiverCVR = CertificateUtil.extractCVR(endpoint.getCertificate()); - messageLogList.add(NemhandelLog.createMessageLog(message, ErrorCodes.C2_CREATE_TRANSFER_DELEGATION_INIT)); - messageLogList.add(NemhandelLog.createMessageLog(message, ErrorCodes.C2_SINGNING_DOCUMENT_INIT)); + if(mode.isNemhandelEdeliveryMode() && !isSigned(sbd)) { + messageLogList.add(NemhandelLog.createMessageLog(message, ErrorCodes.C2_SIGNING_DOCUMENT_INIT)); - // for internal MLR do not include an original-standard-business-document in signed documents (the MLR would be the first document in this case) - // if original document is unsigned then we dont include that in the signature as it cannot be trusted by subsequent corners anyway - boolean includeOriginalSbd = (messageContext != null && !messageContext.isInternalMessageLevelResponse()) && isSigned(originalDocument); - byte[] signedSBD = signMessage(sbd, includeOriginalSbd ? originalDocument : null, receiverCVR, messageContext); + byte[] signedSBD = signatureFactory.signDocument(sbd, messageContext); message.getMessageContent().setData(signedSBD); - messageLogList.add(NemhandelLog.createMessageLog(message, ErrorCodes.C2_CREATE_TRANSFER_DELEGATION_DONE)); - messageLogList.add(NemhandelLog.createMessageLog(message, ErrorCodes.C2_SINGNING_DOCUMENT_DONE)); - - final Message msg = message; - if(messageContext != null && messageContext.isInternalMessageLevelResponse()) { - message = em.get().merge(msg); - } else { - message = TxUtil.doInTxResult(em, entityManager -> entityManager.merge(msg)); - } + messageLogList.add(NemhandelLog.createMessageLog(message, ErrorCodes.C2_SIGNING_DOCUMENT_DONE)); + } else { + logger.info("Skipping signing of message {} because the document is already signed.", message.getMessageUuid()); + } + final Message msg = message; + if(messageContext != null && messageContext.isInternalMessageLevelResponse()) { + message = em.get().merge(msg); + } else { + message = TxUtil.doInTxResult(em, entityManager -> entityManager.merge(msg)); } + messageLogList.add(NemhandelLog.createMessageLog(message, ErrorCodes.C2_SMP_LOOK_UP_INIT)); requestBuilder = requestBuilder .sender(ParticipantIdentifier.parse(message.getSender())) @@ -238,6 +188,15 @@ public class OxalisDocumentSender implements DocumentSender { if (notFoundException >= 0) { throw new OutboundException(message.getMessageUuid(), ErrorCodes.OUTBOUND_SMP_NOT_FOUND_ERROR.getErrorCode(), ErrorCodes.OUTBOUND_SMP_NOT_FOUND_ERROR.formatErrorMessage(), e); } else { + if(e.getCause() != null && e.getCause().getMessage() != null) { + // check if the error is because the document type is not supported by the receiver according to SMP lookup + Matcher m = OXALIS_SMP_LOOKUP_ERROR_PATTERN.matcher(e.getCause().getMessage()); + if(m.matches()) { + String receiverIdent = m.group(1); + String docType = m.group(2); + throw new OutboundException(message.getMessageUuid(), ErrorCodes.OUTBOUND_RECEIVER_DOES_NOT_EXIST_ERROR.getErrorCode(), ErrorCodes.OUTBOUND_RECEIVER_DOES_NOT_EXIST_ERROR.formatErrorMessage(receiverIdent, docType), e); + } + } throw new OutboundException(message.getMessageUuid(), ErrorCodes.OUTBOUND_SMP_ERROR.getErrorCode(), ErrorCodes.OUTBOUND_SMP_ERROR.formatErrorMessage(), e); } } else if (e.getClass().equals(OxalisAs4TransmissionException.class) @@ -254,34 +213,11 @@ public class OxalisDocumentSender implements DocumentSender { throw new OutboundException(message.getMessageUuid(), ErrorCodes.OUTBOUND_EXCEPTION.getErrorCode(), "Error while parsing peppol values", e); } catch (SAXException e) { throw new OutboundException(message.getMessageUuid(), ErrorCodes.OUTBOUND_EXCEPTION.getErrorCode(), "Error while parsing xml", e); + } catch (SignatureException e) { + throw new OutboundException(messageContext.getMessageUuid(), ErrorCodes.OUTBOUND_EXCEPTION.getErrorCode(), "Error while signing document", e); } } - private Endpoint lookupEndpoint(Message message) throws PeppolParsingException, OxalisTransmissionException, OxalisContentException, OutboundException { - DocumentTypeIdentifier documentTypeIdentifier = DocumentTypeIdentifier.parse(message.getDocumentTypeId()); - - PeppolStandardBusinessHeader parsedSbdh = null; - try { - Header header = this.headerParser.parse(new ByteArrayInputStream(message.getMessageContent().getData())); - parsedSbdh = new PeppolStandardBusinessHeader(header); - } catch (OxalisContentException var3) { - throw new OutboundException(message.getMessageUuid(), ErrorCodes.SBD_HEADER_PARSE_ERROR.getErrorCode(), ErrorCodes.SBD_HEADER_PARSE_ERROR.formatErrorMessage()); - } - - PeppolDocumentTypeId documentTypeId = PeppolDocumentTypeId.valueOf(documentTypeIdentifier.getIdentifier()); - InstanceIdentifier identifier = parsedSbdh.toVefa().getIdentifier(); - Header header = Header.of( - ParticipantIdentifier.parse(message.getSender()), - ParticipantIdentifier.parse(message.getReceiver()), - message.getPeppolProcessId() != null ? ProcessIdentifier.parse(message.getPeppolProcessId()) : null, - documentTypeIdentifier, - identifier != null ? identifier : InstanceIdentifier.generateUUID(), - InstanceType.of(documentTypeId.getRootNameSpace(), documentTypeId.getLocalName(), documentTypeId.getVersion()), - parsedSbdh.getCreationDateAndTime() != null ? parsedSbdh.getCreationDateAndTime() : new Date()); - Endpoint endpoint = lookupService.lookup(header); - return endpoint; - } - private byte[] addUUID(Message message) throws OutboundException, XPathExpressionException, IOException, TransformerException { byte[] sbd = message.getMessageContent().getData(); String messageUuid = null; diff --git a/src/main/java/dk/erst/oxalis/as4/util/SBDMessageContext.java b/src/main/java/dk/erst/oxalis/as4/util/SBDMessageContext.java index e525109..ff8797b 100644 --- a/src/main/java/dk/erst/oxalis/as4/util/SBDMessageContext.java +++ b/src/main/java/dk/erst/oxalis/as4/util/SBDMessageContext.java @@ -117,7 +117,7 @@ public class SBDMessageContext { /** * Indicates whether the document signature is required. Document signature is optional for C1 when sending through C2. * Thus, this property is only used in signature validation to determine whether to accept unsigned SBDs. - *

+ *

* Note: the document signature is always required in C3, so C2 must sign the document before sending to C3. * * @return true if the document signature is required, false otherwise. diff --git a/src/main/java/dk/erst/oxalis/as4/util/XPathUtil.java b/src/main/java/dk/erst/oxalis/as4/util/XPathUtil.java index 8e4c11f..3417ab0 100644 --- a/src/main/java/dk/erst/oxalis/as4/util/XPathUtil.java +++ b/src/main/java/dk/erst/oxalis/as4/util/XPathUtil.java @@ -25,19 +25,19 @@ public class XPathUtil { * Evaluates an XPath expression on the given XML document using the provided XPath object * and returns the result as a String. * - * @param document The XML document on which to evaluate the XPath expression. + * @param node The XML document or node on which to evaluate the XPath expression. * @param xpath The XPath object used for evaluating the expression. * @param xpathExpression The XPath expression to be evaluated. * @param defaultValue The default value to be returned if no matching node is found or if an XPathExpressionException occurs. * @return The result of the XPath evaluation as a String. If no matching node is found or an exception occurs, the defaultValue is returned. */ - public static String evaluateXPath(Document document, XPath xpath, String xpathExpression, String defaultValue) { + public static String evaluateXPath(Node node, XPath xpath, String xpathExpression, String defaultValue) { try{ - Node node = evaluateXPathNode(document, xpath, xpathExpression); - if(node == null){ + Node n = evaluateXPathNode(node, xpath, xpathExpression); + if(n == null){ return defaultValue; }else{ - return node.getTextContent(); + return n.getTextContent(); } } catch(XPathExpressionException e){ logger.warn("Error evaluating xpath expression {}: {}", xpathExpression, e.getMessage()); @@ -49,18 +49,18 @@ public class XPathUtil { * Evaluates an XPath expression on the given XML document using the provided XPath object * and returns the resulting Node object. * - * @param document The XML document on which to evaluate the XPath expression. + * @param node The XML document or node on which to evaluate the XPath expression. * @param xpath The XPath object used for evaluating the expression. * @param xpathExpression The XPath expression to be evaluated. * @return The resulting Node object from the evaluation of the XPath expression. * @throws XPathExpressionException If an error occurs during the evaluation of the XPath expression. */ - public static Node evaluateXPathNode(Document document, XPath xpath, String xpathExpression) throws XPathExpressionException { - Node node = (Node)xpath + public static Node evaluateXPathNode(Node node, XPath xpath, String xpathExpression) throws XPathExpressionException { + Node n = (Node)xpath .compile(xpathExpression) - .evaluate(document, XPathConstants.NODE); + .evaluate(node, XPathConstants.NODE); - return node; + return n; } /** diff --git a/src/main/java/dk/erst/oxalis/as4/validation/MessageValidatorImpl.java b/src/main/java/dk/erst/oxalis/as4/validation/MessageValidatorImpl.java index 07ffff0..818099a 100644 --- a/src/main/java/dk/erst/oxalis/as4/validation/MessageValidatorImpl.java +++ b/src/main/java/dk/erst/oxalis/as4/validation/MessageValidatorImpl.java @@ -11,6 +11,7 @@ import dk.erst.oxalis.as4.persistence.model.MessageLog; import dk.erst.oxalis.as4.persistence.model.NemhandelLog; import dk.erst.oxalis.as4.util.DataPairs; import dk.erst.oxalis.as4.util.SBDMessageContext; +import dk.erst.oxalis.as4.validation.registration.RegistrationValidator; import dk.erst.oxalis.as4.validation.schema.SchemaValidator; import dk.erst.oxalis.as4.validation.schematron.SchematronValidator; import dk.erst.oxalis.as4.validation.signature.SignatureValidator; @@ -47,6 +48,7 @@ public class MessageValidatorImpl implements MessageValidator { private final Provider documentBuilderProvider; private final SignatureValidator signatureValidator; private final VersionValidator versionValidator; + private final RegistrationValidator registrationValidator; /** * Constructs a new instance of this class with the specified {@link DocumentTypeConfigResolver}, {@link SchemaValidator} for XSD validation, @@ -61,13 +63,15 @@ public class MessageValidatorImpl implements MessageValidator { */ public MessageValidatorImpl(DocumentTypeConfigResolver documentTypeConfigResolver, SchemaValidator schemaValidator, SchematronValidator schematronValidator, Provider documentBuilderProvider, - SignatureValidator signatureValidator, VersionValidator versionValidator) { + SignatureValidator signatureValidator, VersionValidator versionValidator, + RegistrationValidator registrationValidator) { this.documentTypeConfigResolver = documentTypeConfigResolver; this.schemaValidator = schemaValidator; this.schematronValidator = schematronValidator; this.documentBuilderProvider = documentBuilderProvider; this.signatureValidator = signatureValidator; this.versionValidator = versionValidator; + this.registrationValidator = registrationValidator; } /** @@ -88,6 +92,7 @@ public class MessageValidatorImpl implements MessageValidator { if(validationType == null) validationType = ValidationType.ALL; boolean isC2 = message.getDirection().equals(MessageDirection.OUT) || (messageContext != null && messageContext.getDirection().equals(Direction.OUT)); + boolean isOriginator = isC2 && !messageContext.isInternalMessageLevelResponse(); List messageLogList = new LinkedList<>(); ValidationResult result = new ValidationResult(); @@ -117,6 +122,15 @@ public class MessageValidatorImpl implements MessageValidator { throw new ValidationException("No suitable configuration found for document type. Cannot perform validation of message " + message.getId()); } + if(isOriginator) { + ValidationResult partialValidationResult = registrationValidator.validate(message, documentTypeConfig); + if (partialValidationResult != null && partialValidationResult.hasErrors()) { + result.addAll(partialValidationResult); + messageLogList.add(NemhandelLog.createMessageLog(message, partialValidationResult.getErrors().get(0))); + return new DataPairs.Tuple2<>(messageLogList, result); + } + } + // perform schema (XSD validation) if(ValidationType.ALL.equals(validationType) || ValidationType.SYNCHRONOUS.equals(validationType)) { diff --git a/src/main/java/dk/erst/oxalis/as4/validation/ValidationModule.java b/src/main/java/dk/erst/oxalis/as4/validation/ValidationModule.java index 8513faf..2392097 100644 --- a/src/main/java/dk/erst/oxalis/as4/validation/ValidationModule.java +++ b/src/main/java/dk/erst/oxalis/as4/validation/ValidationModule.java @@ -6,10 +6,9 @@ import com.google.inject.*; import dk.erst.oxalis.as4.config.documenttype.DocumentTypeConfig; import dk.erst.oxalis.as4.config.documenttype.DocumentTypeConfigResolver; import dk.erst.oxalis.as4.util.SBDPayloadExtractor; -import dk.erst.oxalis.as4.validation.schema.SchemaResolver; -import dk.erst.oxalis.as4.validation.schema.SchemaResolverImpl; -import dk.erst.oxalis.as4.validation.schema.SchemaValidator; -import dk.erst.oxalis.as4.validation.schema.SchemaValidatorImpl; +import dk.erst.oxalis.as4.validation.registration.RegistrationValidator; +import dk.erst.oxalis.as4.validation.registration.RegistrationValidatorImpl; +import dk.erst.oxalis.as4.validation.schema.*; import dk.erst.oxalis.as4.validation.schematron.*; import dk.erst.oxalis.as4.validation.signature.DSSModule; import dk.erst.oxalis.as4.validation.signature.SignatureValidator; @@ -21,6 +20,7 @@ import org.slf4j.LoggerFactory; import javax.xml.parsers.DocumentBuilder; import javax.xml.transform.Templates; +import javax.xml.validation.Schema; import java.util.List; import java.util.Set; import java.util.concurrent.ExecutionException; @@ -48,8 +48,8 @@ public class ValidationModule extends AbstractModule { @Provides @Singleton - SchemaResolver provideSchemaResolver() { - return new SchemaResolverImpl(); + SchemaResolver provideSchemaResolver(@SchemaCache LoadingCache schemaCache) { + return new SchemaResolverImpl(schemaCache); } @Provides @@ -75,8 +75,10 @@ public class ValidationModule extends AbstractModule { @Singleton MessageValidator provideDocumentValidator(DocumentTypeConfigResolver documentTypeConfigResolver, SchemaValidator schemaValidator, SchematronValidator schematronValidator, Provider documentBuilderProvider, - SignatureValidator signatureValidator, VersionValidator versionValidator) { - return new MessageValidatorImpl(documentTypeConfigResolver, schemaValidator, schematronValidator, documentBuilderProvider, signatureValidator, versionValidator); + SignatureValidator signatureValidator, VersionValidator versionValidator, + RegistrationValidator registrationValidator) { + return new MessageValidatorImpl(documentTypeConfigResolver, schemaValidator, schematronValidator, + documentBuilderProvider, signatureValidator, versionValidator, registrationValidator); } @Provides @@ -85,6 +87,12 @@ public class ValidationModule extends AbstractModule { return new VersionValidatorImpl(injector); } + @Provides + @Singleton + RegistrationValidator provideregistrationValidator(Injector injector) { + return new RegistrationValidatorImpl(injector); + } + @Provides @Singleton VersionCache provideVersionCache() { @@ -104,15 +112,45 @@ public class ValidationModule extends AbstractModule { return CacheBuilder.newBuilder().build(cacheLoader); } + @Provides + @Singleton + SchemaCacheLoader schemaCacheLoader() { + return new SchemaCacheLoader(); + } + + @Provides + @Singleton + @SchemaCache + LoadingCache provideSchemaCache(SchemaCacheLoader cacheLoader) { + return CacheBuilder.newBuilder().build(cacheLoader); + } + /** - * Preloads Schematron stylesheets by compiling and caching them using a provided XSLT cache. + * Preloads XSD Schemas and Schematron stylesheets by compiling and caching them using a provided caches. * - * @param xsltCache The XSLT cache used for loading and caching the stylesheets. + * @param xsltCache The cache used for loading and caching the XSLT stylesheets. + * @param schemaCache The cache used for loading and caching the XSD Schemas * @param documentTypeConfigs The set of {@link DocumentTypeConfig}. * @throws ExecutionException If an exception occurs while loading the {@link XSLTCache}. */ @Inject - public void preloadSchematronStylesheets(@XSLTCache LoadingCache xsltCache, Set documentTypeConfigs) throws ExecutionException { + public void preloadSchemaAndSchematron(@XSLTCache LoadingCache xsltCache, @SchemaCache LoadingCache schemaCache, Set documentTypeConfigs) throws ExecutionException { + List preloadSchemaPaths = documentTypeConfigs.stream() + .filter(DocumentTypeConfig::isPreloadSchema) + .map(DocumentTypeConfig::getSchemaPath) + .distinct() + .collect(Collectors.toList()); + if(!preloadSchemaPaths.isEmpty()) { + log.info("Starting preload of {} XSD schema (parsing and caching)", preloadSchemaPaths.size()); + /** + * Note: The cache is created with a CacheLoader implementation which can load XSD files. + * Calling get or getAll on the cache will load the XSD's which are not already cached! + */ + schemaCache.getAll(preloadSchemaPaths); + log.info("Preloading of XSD schemas done"); + + } + List preloadSchematronPaths = documentTypeConfigs.stream() .filter(DocumentTypeConfig::isPreloadSchematron) .flatMap(dc -> dc.getSchematronDocuments().stream()) diff --git a/src/main/java/dk/erst/oxalis/as4/validation/registration/RegistrationValidator.java b/src/main/java/dk/erst/oxalis/as4/validation/registration/RegistrationValidator.java new file mode 100644 index 0000000..bf7ecbc --- /dev/null +++ b/src/main/java/dk/erst/oxalis/as4/validation/registration/RegistrationValidator.java @@ -0,0 +1,27 @@ +package dk.erst.oxalis.as4.validation.registration; + + +import dk.erst.oxalis.as4.config.documenttype.DocumentTypeConfig; +import dk.erst.oxalis.as4.handlers.dto.ValidationResult; +import dk.erst.oxalis.as4.persistence.model.Message; +import dk.erst.oxalis.as4.validation.ValidationException; +import dk.erst.oxalis.as4.validation.version.VersionCache; +import dk.erst.oxalis.as4.validation.version.VersionValidationException; +import dk.erst.oxalis.as4.validation.version.model.EdelComponentVersion; +import network.oxalis.api.lang.OxalisTransmissionException; +import network.oxalis.peppol.busdox.jaxb.smp.ExtensionType; + +/** + * Performs a SMP lookup to validate that the sender (C2) is correctly registered to receive MLR/AR responses + */ +public interface RegistrationValidator { + + /** + * Validate by a SMP lookup that it is possible to send an asynchronous response to the sender of a message. + * @param message The message to validate. Currently only the sender identification of the message is used. + * @param documentTypeConfig The {@link DocumentTypeConfig} of the message. Currently only the + * MessageLevelResponseType of the DocumentTypeConfig is used. + */ + ValidationResult validate(Message message, DocumentTypeConfig documentTypeConfig) throws ValidationException, OxalisTransmissionException; + +} diff --git a/src/main/java/dk/erst/oxalis/as4/validation/registration/RegistrationValidatorImpl.java b/src/main/java/dk/erst/oxalis/as4/validation/registration/RegistrationValidatorImpl.java new file mode 100644 index 0000000..3ef8e0c --- /dev/null +++ b/src/main/java/dk/erst/oxalis/as4/validation/registration/RegistrationValidatorImpl.java @@ -0,0 +1,115 @@ +package dk.erst.oxalis.as4.validation.registration; + +import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.name.Names; +import dk.erst.oxalis.as4.config.documenttype.DocumentTypeConfig; +import dk.erst.oxalis.as4.config.documenttype.MessageLevelResponseType; +import dk.erst.oxalis.as4.handlers.dto.ValidationResult; +import dk.erst.oxalis.as4.persistence.model.Message; +import dk.erst.oxalis.as4.validation.ValidationException; +import network.oxalis.api.lang.OxalisTransmissionException; +import network.oxalis.api.lookup.LookupService; +import network.oxalis.vefa.peppol.common.lang.EndpointNotFoundException; +import network.oxalis.vefa.peppol.common.lang.PeppolParsingException; +import network.oxalis.vefa.peppol.common.model.DocumentTypeIdentifier; +import network.oxalis.vefa.peppol.common.model.Header; +import network.oxalis.vefa.peppol.common.model.ParticipantIdentifier; +import network.oxalis.vefa.peppol.common.model.ProcessIdentifier; +import network.oxalis.vefa.peppol.lookup.api.LookupException; +import org.hibernate.query.criteria.internal.expression.function.AggregationFunction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +import static dk.erst.oxalis.as4.error.ErrorCodes.OUTBOUND_SENDER_DOES_NOT_EXIST_ERROR; +import static dk.erst.oxalis.as4.error.ErrorCodes.OUTBOUND_SMP_ERROR; + +/** + * Implementation: + * Performs a SMP lookup to validate that the sender (C2) is correctly registered to receive MLR/AR responses + */ +public class RegistrationValidatorImpl implements RegistrationValidator { + + private static final Logger log = LoggerFactory.getLogger(RegistrationValidatorImpl.class); + private final LookupService lookupService; + private final static long MAX_LOOP_COUNTER= 10000; + + /** + * Constructs a new {@code RegistrationValidatorImpl} with the specified injector. + * + * @param injector The injector used to retrieve an instance of {@link LookupService}. + */ + public RegistrationValidatorImpl(Injector injector) { + this.lookupService = injector.getInstance(LookupService.class); + + } + + public static final String OIOUBL_AR_DOCUMENTTYPEID = + "busdox-docid-qns::urn:oasis:names:specification:ubl:schema:xsd:ApplicationResponse-2::ApplicationResponse##OIOUBL-2.1::2.1"; + public static final String PEPPOL_MLR_DOCUMENTTYPEID = + "busdox-docid-qns::urn:oasis:names:specification:ubl:schema:xsd:ApplicationResponse-2::ApplicationResponse##urn:fdc:peppol.eu:poacc:trns:mlr:3::2.1"; + // The OIOUBL_TECRES_PROCESSID process is used for positive acknowledgements, which we do not support yet + //public static final String OIOUBL_TECRES_PROCESSID = "oioubl-procid-ubl::Procurement-TecRes-1.0"; + public static final String OIOUBL_BILSIM_PROCESSID = "oioubl-procid-ubl::Procurement-BilSim-1.0"; + public static final String PEPPOL_MLR_PROCESSID = "cenbii-procid-ubl::urn:fdc:peppol.eu:poacc:bis:mlr:3"; + + @Override + public ValidationResult validate(Message message, DocumentTypeConfig documentTypeConfig) throws ValidationException, OxalisTransmissionException { + log.info("RegistrationValidator has been called."); + ValidationResult result = new ValidationResult(); + ParticipantIdentifier receiver; + DocumentTypeIdentifier documentTypeIdentifier; + List processIdentifiers = new ArrayList<>(); + MessageLevelResponseType messageLevelResponseType = documentTypeConfig.getMessageLevelResponseType(); + try { + // sender for message is receiver for MLR/AR + receiver = ParticipantIdentifier.parse(message.getSender()); + if (MessageLevelResponseType.OIOUBL_APPLICATION_RESPONSE.equals(messageLevelResponseType)) { + documentTypeIdentifier = DocumentTypeIdentifier.parse(OIOUBL_AR_DOCUMENTTYPEID); + // The OIOUBL_TECRES_PROCESSID process is used for positive acknowledgements, which we do not support yet + //processIdentifiers.add(ProcessIdentifier.parse(OIOUBL_TECRES_PROCESSID)); + processIdentifiers.add(ProcessIdentifier.parse(OIOUBL_BILSIM_PROCESSID)); + } else if (MessageLevelResponseType.PEPPOL_MESSAGE_LEVEL_RESPONSE.equals(messageLevelResponseType)) { + documentTypeIdentifier = DocumentTypeIdentifier.parse(PEPPOL_MLR_DOCUMENTTYPEID); + processIdentifiers.add(ProcessIdentifier.parse(PEPPOL_MLR_PROCESSID)); + } else { + throw new ValidationException("Unknown MessageLevelResponseType"); + } + } + catch (PeppolParsingException e) { + throw new ValidationException(e); + } + + for(ProcessIdentifier process : processIdentifiers) { + Header header = Header.of(null, receiver, process, documentTypeIdentifier); + try { + lookupService.lookup(header); + } catch (OxalisTransmissionException e) { + Throwable cause = e; + // we want to find out whether the exception was caused by a missing SMP entry as opposed to a failure + // in performing the SMP lookup + for (int i =0; i < MAX_LOOP_COUNTER; i++) { + if(cause == null || cause instanceof LookupException || cause instanceof EndpointNotFoundException) { + break; + } + cause = cause.getCause(); + } + if(cause instanceof LookupException || cause instanceof EndpointNotFoundException) { + log.debug("Sender endpoint not found.", e); + result.addError(OUTBOUND_SENDER_DOES_NOT_EXIST_ERROR.getErrorCode(), + OUTBOUND_SENDER_DOES_NOT_EXIST_ERROR.formatErrorMessage(receiver.toString(), + process.toString(), documentTypeIdentifier.toString())); + } + else { + log.warn("SMP lookup failed", e); + throw e; + } + } + } + log.info("RegistrationValidator has completed."); + return result; + } +} diff --git a/src/main/java/dk/erst/oxalis/as4/validation/schema/SchemaCache.java b/src/main/java/dk/erst/oxalis/as4/validation/schema/SchemaCache.java new file mode 100644 index 0000000..acf49bc --- /dev/null +++ b/src/main/java/dk/erst/oxalis/as4/validation/schema/SchemaCache.java @@ -0,0 +1,18 @@ +package dk.erst.oxalis.as4.validation.schema; + +import javax.inject.Qualifier; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Qualifier for the cache used for caching XSD schemas. + */ +@Qualifier +@Target({ FIELD, PARAMETER, METHOD }) +@Retention(RUNTIME) +public @interface SchemaCache { + +} diff --git a/src/main/java/dk/erst/oxalis/as4/validation/schema/SchemaCacheLoader.java b/src/main/java/dk/erst/oxalis/as4/validation/schema/SchemaCacheLoader.java new file mode 100644 index 0000000..f2ce06f --- /dev/null +++ b/src/main/java/dk/erst/oxalis/as4/validation/schema/SchemaCacheLoader.java @@ -0,0 +1,112 @@ +package dk.erst.oxalis.as4.validation.schema; + +import com.google.common.cache.CacheLoader; +import com.sun.org.apache.xerces.internal.util.XMLCatalogResolver; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.xml.sax.SAXException; + +import javax.xml.XMLConstants; +import javax.xml.transform.Source; +import javax.xml.transform.stream.StreamSource; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Loads and parses XSD Schemas to {@link Schema} for caching in a {@link com.google.common.cache.LoadingCache} + * using the file path as the key. + */ +public class SchemaCacheLoader extends CacheLoader { + private static final Logger log = LoggerFactory.getLogger(SchemaCacheLoader.class); + + private String[] catalogs; + + /** + * Default constructor + */ + public SchemaCacheLoader() { + String catalog = "META-INF/Schemas/catalog.xml"; + this.catalogs = new String[]{getAbsolutePathForResource(catalog)}; + } + + /** + * Loads and parses the XSD schema from the given path. + * Will first attempt to load the XSD as a resource and if that fails attempt to load it as an absolute path. + * + * @param schemaPath the path to the XSD schema file to be loaded. + * @return The parsed XSD schema as a {@link Schema} object suitable for caching. + * @throws IllegalArgumentException if {@code schemaPath} is null. + * @throws FileNotFoundException if {@code schemapath} is an absolute file path which doesn't exist. + * @throws Exception if the schema cannot be loaded. + */ + @Override + public Schema load(String schemaPath) throws Exception { + if (schemaPath == null) throw new IllegalArgumentException("schemaPath cannot be null"); + SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + //to be able to parse XSD schemas with maxOccurs with value larger than 5000. + factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, false); + + XMLCatalogResolver xmlCatalogResolver = new XMLCatalogResolver(); + xmlCatalogResolver.setPreferPublic(true); + xmlCatalogResolver.setCatalogList(catalogs); + factory.setResourceResolver(xmlCatalogResolver); + return loadSchema(factory, schemaPath); + } + + private Schema loadSchema(SchemaFactory schemaFactory, String schemaPath) throws IOException, SAXException { + Schema schema = null; + // try to load schema from resources then afterwards as file + URL url = this.getClass().getClassLoader().getResource(schemaPath); + if (url != null) { + schema = schemaFactory.newSchema(url); + } else { + if (!Paths.get(schemaPath).toFile().exists()) { + throw new FileNotFoundException("Could not find XSD Schema. Path: " + schemaPath + " does not exist"); + } + + Source source = new StreamSource(schemaPath); + schema = schemaFactory.newSchema(source); + } + + return schema; + } + + /** + * Loads all the XSD schemas in the given iterable. + * + * @param xsdPaths An iterable containing the list of XSD Schema paths to load. + * @return A {@link Map} containing the loaded XSD schemas suitable for caching. The key will be the paths from the input iterable. + * @throws Exception if an error occurs while loading an XSD schema. + */ + @Override + public Map loadAll(Iterable xsdPaths) throws Exception { + if (xsdPaths == null) return Collections.emptyMap(); + + final SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + Map loaded = new HashMap<>(); + for (String xsdPath : xsdPaths) { + Schema t = loadSchema(factory, xsdPath); + loaded.put(xsdPath, t); + } + return loaded; + } + + private String getAbsolutePathForResource(String filePath) { + URL url = this.getClass().getClassLoader().getResource(filePath); + if (url != null) { + if (log.isDebugEnabled()) { + log.debug("catalog url: {}", url); + } + return url.toString(); + } + log.error("Couldn't find resource: {}", filePath); + return null; + } +} diff --git a/src/main/java/dk/erst/oxalis/as4/validation/schema/SchemaResolver.java b/src/main/java/dk/erst/oxalis/as4/validation/schema/SchemaResolver.java index acb2b33..78991ec 100644 --- a/src/main/java/dk/erst/oxalis/as4/validation/schema/SchemaResolver.java +++ b/src/main/java/dk/erst/oxalis/as4/validation/schema/SchemaResolver.java @@ -5,6 +5,7 @@ import dk.erst.oxalis.as4.config.documenttype.DocumentTypeConfigResolverExceptio import org.w3c.dom.Document; import javax.xml.validation.Schema; +import java.util.concurrent.ExecutionException; /** * Interface for resolving schemas for a {@link Document} based on a {@link DocumentTypeConfig}. @@ -13,10 +14,8 @@ public interface SchemaResolver { /** * Resolves the {@link Schema} for a {@link Document} based on a {@link DocumentTypeConfig}. * - * @param document the {@link Document} for which to resolve the schema. - * @param documentTypeConfig the {@link DocumentTypeConfig} configuration for the document type. + * @param schemaPath the file path to the XSD schema. * @return the resolved {@link Schema} for the document. - * @throws DocumentTypeConfigResolverException if an error occurs while resolving the @{link DocumentTypeConfig} configuration. */ - public Schema resolveSchema(Document document, DocumentTypeConfig documentTypeConfig) throws DocumentTypeConfigResolverException; + public Schema resolveSchema(String schemaPath) throws ExecutionException; } diff --git a/src/main/java/dk/erst/oxalis/as4/validation/schema/SchemaResolverImpl.java b/src/main/java/dk/erst/oxalis/as4/validation/schema/SchemaResolverImpl.java index 3422798..64bcffd 100644 --- a/src/main/java/dk/erst/oxalis/as4/validation/schema/SchemaResolverImpl.java +++ b/src/main/java/dk/erst/oxalis/as4/validation/schema/SchemaResolverImpl.java @@ -1,5 +1,6 @@ package dk.erst.oxalis.as4.validation.schema; +import com.google.common.cache.LoadingCache; import dk.erst.oxalis.as4.config.documenttype.DocumentTypeConfig; import dk.erst.oxalis.as4.config.documenttype.DocumentTypeConfigResolverException; import org.slf4j.Logger; @@ -11,6 +12,7 @@ import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import java.net.URL; +import java.util.concurrent.ExecutionException; /** * Implementation for resolving schemas for a {@link Document} based on a {@link DocumentTypeConfig}. @@ -18,34 +20,19 @@ import java.net.URL; public class SchemaResolverImpl implements SchemaResolver { private static final Logger log = LoggerFactory.getLogger(SchemaResolverImpl.class); + private final LoadingCache schemaCache; /** * Constructs a new instance of this class. */ - public SchemaResolverImpl() { + public SchemaResolverImpl(LoadingCache schemaCache) { + this.schemaCache = schemaCache; } @Override - public Schema resolveSchema(Document document, DocumentTypeConfig documentTypeConfig) throws DocumentTypeConfigResolverException { - if(document == null || documentTypeConfig == null) return null; + public Schema resolveSchema(String schemaPath) throws ExecutionException { + if(schemaPath == null) return null; - String schemaPath = documentTypeConfig.getSchemaPath(); - SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); - - Schema schema = null; - // try to load schema from resources then afterwards as file - try { - URL url = this.getClass().getClassLoader().getResource(schemaPath); - schema = factory.newSchema(url); - } catch (Exception e) { - Source source = new StreamSource(schemaPath); - try { - schema = factory.newSchema(source); - } catch (Exception ex) { - log.debug("Could not resolve schema for element [{{}]{}", document.getDocumentElement().getNamespaceURI(), document.getDocumentElement().getLocalName()); - } - } - - return schema; + return schemaCache.get(schemaPath); } } diff --git a/src/main/java/dk/erst/oxalis/as4/validation/schema/SchemaValidatorImpl.java b/src/main/java/dk/erst/oxalis/as4/validation/schema/SchemaValidatorImpl.java index 4df28e8..94eac63 100644 --- a/src/main/java/dk/erst/oxalis/as4/validation/schema/SchemaValidatorImpl.java +++ b/src/main/java/dk/erst/oxalis/as4/validation/schema/SchemaValidatorImpl.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; /** @@ -48,7 +49,7 @@ public class SchemaValidatorImpl implements SchemaValidator { try { Document payload = payloadExtractor.extractPayload(document); - Schema schema = schemaResolver.resolveSchema(payload, documentTypeConfig); + Schema schema = schemaResolver.resolveSchema(documentTypeConfig.getSchemaPath()); if(schema != null) { Validator validator = schema.newValidator(); CollectingErrorHandler errorHandler = new CollectingErrorHandler(messageContext); @@ -58,7 +59,7 @@ public class SchemaValidatorImpl implements SchemaValidator { result.setErrors(errorHandler.getErrors()); result.setWarnings(errorHandler.getWarnings()); } - } catch (SAXException | IOException | ParserConfigurationException e) { + } catch (SAXException | IOException | ParserConfigurationException | ExecutionException e) { throw new SchemaValidationException("Error while performing schema validation", e); } diff --git a/src/main/java/dk/erst/oxalis/as4/validation/signature/DSSModule.java b/src/main/java/dk/erst/oxalis/as4/validation/signature/DSSModule.java index faebf4c..c6f0399 100644 --- a/src/main/java/dk/erst/oxalis/as4/validation/signature/DSSModule.java +++ b/src/main/java/dk/erst/oxalis/as4/validation/signature/DSSModule.java @@ -4,11 +4,12 @@ import com.google.inject.AbstractModule; import com.google.inject.Provider; import com.google.inject.Provides; import com.google.inject.Singleton; +import dk.erst.oxalis.as4.mode.Mode; import dk.erst.oxalis.as4.signature.SignatureFactory; import dk.erst.oxalis.as4.signature.SignatureFactoryImpl; -import dk.erst.oxalis.as4.signature.TransferDelegationFactoryImpl; -import eu.europa.esig.dss.asic.xades.ASiCWithXAdESSignatureParameters; -import eu.europa.esig.dss.asic.xades.signature.ASiCWithXAdESService; +import dk.erst.oxalis.as4.signature.XAdESServiceProvider; +import dk.erst.oxalis.as4.signature.XAdESSignatureParametersProvider; +import dk.erst.oxalis.as4.util.SBDPayloadExtractor; import eu.europa.esig.dss.policy.ValidationPolicy; import eu.europa.esig.dss.service.crl.OnlineCRLSource; import eu.europa.esig.dss.service.http.commons.CommonsDataLoader; @@ -21,24 +22,27 @@ import eu.europa.esig.dss.spi.x509.KeyStoreCertificateSource; import eu.europa.esig.dss.spi.x509.revocation.crl.CRLSource; import eu.europa.esig.dss.token.DSSPrivateKeyEntry; import eu.europa.esig.dss.validation.CertificateVerifier; +import java.io.IOException; +import java.io.InputStream; +import java.security.KeyStore; +import java.security.Security; +import javax.inject.Named; +import javax.xml.parsers.DocumentBuilder; + +import eu.europa.esig.dss.validation.DocumentValidatorFactory; +import eu.europa.esig.dss.xades.XAdESSignatureParameters; +import eu.europa.esig.dss.xades.signature.XAdESService; +import eu.europa.esig.dss.xades.validation.XMLDocumentValidatorFactory; import network.oxalis.api.lang.OxalisLoadingException; import network.oxalis.api.settings.Settings; import network.oxalis.commons.security.KeyStoreConf; import network.oxalis.commons.settings.SettingsBuilder; -import dk.erst.oxalis.as4.mode.Mode; +import org.apache.wss4j.dom.engine.WSSConfig; import org.bouncycastle.jce.provider.BouncyCastleProvider; -import javax.inject.Named; -import javax.xml.bind.JAXBException; -import javax.xml.parsers.DocumentBuilder; -import java.io.IOException; -import java.io.InputStream; -import java.security.KeyStore; -import java.security.Security; -import java.security.cert.X509Certificate; /** - * Module containing classes for validating ASiC containers using the Digital Signature Service framework (DSS) + * Module containing classes for creating and validating Nemhandel e-Delivery document signatures using the Digital Signature Service framework (DSS) */ public class DSSModule extends AbstractModule { @@ -48,14 +52,21 @@ public class DSSModule extends AbstractModule { Security.addProvider(new BouncyCastleProvider()); } - // Initialize apache Santuario canonicalization - org.apache.xml.security.Init.init(); + /** + * Initialize WSS4J just in case since XML security is needed for the DSS framework + * NOTE: don't initialize Apache XML Security directly here through org.apache.xml.security.Init.init(); + * as that will cause issues with the resource bundles at runtime for certain errors while verifying + * the WS-security signature which is part of the AS4 protocol! + */ + WSSConfig.init(); // Bind settings SettingsBuilder.with(binder(), SignatureValidationConf.class); bind(ValidationPolicy.class).toProvider(SignatureValidationPolicyProvider.class); bind(CertificateVerifier.class).toProvider(CertificateVerifierProvider.class); + bind(XAdESSignatureParameters.class).toProvider(XAdESSignatureParametersProvider.class); + bind(XAdESService.class).toProvider(XAdESServiceProvider.class); } @Provides @@ -64,8 +75,9 @@ public class DSSModule extends AbstractModule { Provider policyProvider, Provider documentBuilderProvider, Mode mode, - X509Certificate accessPointCertificate) { - return new SignatureValidatorImpl(verifierProvider, policyProvider, documentBuilderProvider, mode, accessPointCertificate); + SBDPayloadExtractor payloadExtractor, + DocumentValidatorFactory documentValidatorFactory) { + return new SignatureValidatorImpl(verifierProvider, policyProvider, documentBuilderProvider, mode, payloadExtractor, documentValidatorFactory); } @Provides @@ -76,7 +88,7 @@ public class DSSModule extends AbstractModule { String trustStorePath = mode.getString("security.truststore.ap"); String keystoreType = trustStorePath.endsWith(".jks") ? "JKS" : "PKCS12"; try (InputStream inputStream = getClass().getResourceAsStream(trustStorePath)) { - CertificateSource source = new KeyStoreCertificateSource(inputStream, keystoreType, mode.getString("security.truststore.password")); + CertificateSource source = new KeyStoreCertificateSource(inputStream, keystoreType, mode.getString("security.truststore.password").toCharArray()); CommonTrustedCertificateSource trustSource = new CommonTrustedCertificateSource(); trustSource.importAsTrusted(source); return trustSource; @@ -85,13 +97,6 @@ public class DSSModule extends AbstractModule { } } - @Provides - @Singleton - ASiCWithXAdESService provideAsicXAdESService(Provider certificateVerifierProvider) { - ASiCWithXAdESService service = new ASiCWithXAdESService(certificateVerifierProvider.get()); - return service; - } - @Provides @Singleton CRLSource provideCRLSource(Settings settings) { @@ -104,7 +109,7 @@ public class DSSModule extends AbstractModule { properties.setHost(settings.getString(SignatureValidationConf.CRL_PROXY_HOST)); properties.setPort(settings.getInt(SignatureValidationConf.CRL_PROXY_PORT)); properties.setUser(settings.getString(SignatureValidationConf.CRL_PROXY_USER)); - properties.setPassword(settings.getString(SignatureValidationConf.CRL_PROXY_PASSWORD)); + properties.setPassword(settings.getString(SignatureValidationConf.CRL_PROXY_PASSWORD).toCharArray()); ProxyConfig proxyConfig = new ProxyConfig(); proxyConfig.setHttpProperties(properties); proxyConfig.setHttpsProperties(properties); @@ -112,7 +117,7 @@ public class DSSModule extends AbstractModule { } cacheDataLoader.setDataLoader(dataLoader); - cacheDataLoader.setCacheExpirationTime(3600000L); + cacheDataLoader.setCacheExpirationTime(1800_000L); source.setDataLoader(cacheDataLoader); return source; } @@ -131,13 +136,18 @@ public class DSSModule extends AbstractModule { @Provides @Singleton - SignatureFactory provideSignatureFactory(ASiCWithXAdESService signatureService, KeystoreSignatureTokenConnectionWrapper signatureToken, ASiCWithXAdESSignatureParameters signatureParameters, DSSPrivateKeyEntry signaturePrivateKey, Provider documentBuilderProvider) { - return new SignatureFactoryImpl(signatureService, signatureToken, signaturePrivateKey, documentBuilderProvider); + SignatureFactory provideSignatureFactory(Provider signatureServiceProvider, + KeystoreSignatureTokenConnectionWrapper signatureToken, + DSSPrivateKeyEntry signaturePrivateKey, + Provider documentBuilderProvider, + SBDPayloadExtractor payloadExtractor, + Provider signatureParametersProvider) { + return new SignatureFactoryImpl(signatureServiceProvider, signatureToken, signaturePrivateKey, documentBuilderProvider, payloadExtractor, signatureParametersProvider); } @Provides @Singleton - TransferDelegationFactoryImpl transferDelegationFactory () throws JAXBException { - return new TransferDelegationFactoryImpl(); + DocumentValidatorFactory provideXMLDocumentValidatorFactory() { + return new XMLDocumentValidatorFactory(); // Used to create Validator instances for XAdES } } diff --git a/src/main/java/dk/erst/oxalis/as4/validation/signature/SignatureValidationPolicyProvider.java b/src/main/java/dk/erst/oxalis/as4/validation/signature/SignatureValidationPolicyProvider.java index 33cd79d..dd848ad 100644 --- a/src/main/java/dk/erst/oxalis/as4/validation/signature/SignatureValidationPolicyProvider.java +++ b/src/main/java/dk/erst/oxalis/as4/validation/signature/SignatureValidationPolicyProvider.java @@ -2,8 +2,11 @@ package dk.erst.oxalis.as4.validation.signature; import com.google.inject.Provider; import com.google.inject.ProvisionException; +import eu.europa.esig.dss.enumerations.SignatureLevel; import eu.europa.esig.dss.policy.ValidationPolicy; import eu.europa.esig.dss.policy.ValidationPolicyFacade; +import eu.europa.esig.dss.policy.jaxb.Level; +import eu.europa.esig.dss.policy.jaxb.MultiValuesConstraint; import org.xml.sax.SAXException; import javax.xml.bind.JAXBException; @@ -24,7 +27,13 @@ public class SignatureValidationPolicyProvider implements Provider validationPolicyProvider; private final Provider documentBuilderProvider; private final Mode mode; - private final X509Certificate accessPointCertificate; + private static final String EDELIVERY_SIGNATURE_SCOPE_XPATH = "/sbd:StandardBusinessDocument/sbd:StandardBusinessDocumentHeader/sbd:BusinessScope/sbd:Scope[sbd:Type='NEMHANDEL_EDELIVERY_SIGNATURE']"; /** - * Xpath to where the nemhandel e-delivery signature is located in the document structure. + * Xpath to where the Nemhandel e-delivery signature is located in the document structure. */ - public static final String EDELIVERY_SIGNATURE_XPATH = "/sbd:StandardBusinessDocument/sbd:StandardBusinessDocumentHeader/sbd:BusinessScope/sbd:Scope[sbd:Type='NEMHANDEL_EDELIVERY_SIGNATURE']/sbd:InstanceIdentifier"; + public static final String EDELIVERY_SIGNATURE_XPATH = EDELIVERY_SIGNATURE_SCOPE_XPATH + "/sbd:InstanceIdentifier"; + + /** + * XPath to where the Nemhandel e-delivery signature specification version is located in the document structure. + */ + public static final String EDELIVERY_SIGNATURE_SPEC_VERSION_XPATH = EDELIVERY_SIGNATURE_SCOPE_XPATH + "/sbd:Identifier"; + + /** + * Detail error message for when the signature validation is rejected because the signature is not in XAdES format or contains no signatures. + */ + public static final String INVALID_SIGNATURE_FORMAT_DETAIL_MESSAGE = "Signature is not in XAdES format or contains no signatures"; + private final SBDPayloadExtractor payloadExtractor; + private final DocumentValidatorFactory documentValidatorFactory; /** @@ -71,15 +81,21 @@ public class SignatureValidatorImpl implements SignatureValidator { * @param validationPolicy {@link Provider} for providing {@link ValidationPolicy} objects. Resolved by Guice. * @param documentBuilderProvider {@link Provider} for providing {@link DocumentBuilder} objects. Resolved by Guice. * @param mode The type mode. Is used to identify the current mode of the Access Point. - * @param accessPointCertificate The Access Point's own certificate. + * @param documentValidatorFactory Factory for creating {@link eu.europa.esig.dss.validation.DocumentValidator} instances. + * @param payloadExtractor The extractor to use in extracting the payload from the SBD. */ - public SignatureValidatorImpl(Provider certificateVerifier, Provider validationPolicy, - Provider documentBuilderProvider, Mode mode, X509Certificate accessPointCertificate) { + public SignatureValidatorImpl(Provider certificateVerifier, + Provider validationPolicy, + Provider documentBuilderProvider, + Mode mode, + SBDPayloadExtractor payloadExtractor, + DocumentValidatorFactory documentValidatorFactory) { this.certificateVerifierProvider = certificateVerifier; this.validationPolicyProvider = validationPolicy; this.documentBuilderProvider = documentBuilderProvider; this.mode = mode; - this.accessPointCertificate = accessPointCertificate; + this.payloadExtractor = payloadExtractor; + this.documentValidatorFactory = documentValidatorFactory; } @Override @@ -96,23 +112,15 @@ public class SignatureValidatorImpl implements SignatureValidator { ValidationResult result = new ValidationResult(); byte[] messageContent = message.getMessageContent().getData(); - MapNamespaceContext namespaceContext = new MapNamespaceContext(); - namespaceContext.addNamespace("sbd", Ns.SBDH); - XPath xpath = XPathFactory.newInstance().newXPath(); - xpath.setNamespaceContext(namespaceContext); try(ByteArrayInputStream bis = new ByteArrayInputStream(messageContent)) { DocumentBuilder documentBuilder = documentBuilderProvider.get(); Document document = documentBuilder.parse(bis); - ValidationContext context = new ValidationContext(); - context.setDocumentBuilder(documentBuilder); - context.setRecursionLevel(0); - context.setSbdXPath(xpath); - context.setMessageContext(messageContext); - validateDocumentSignature(document, result, context); - } catch(IOException | SAXException | XPathExpressionException | TransformerException | XMLSecurityException e) { + validateDocumentSignature(document, result, messageContext); + } catch(IOException | SAXException | XPathExpressionException | TransformerException | XMLSecurityException | + ParserConfigurationException e) { throw new ValidationException("Error while validating document", e); } @@ -134,235 +142,102 @@ public class SignatureValidatorImpl implements SignatureValidator { return documentSignatures; } - private void validateDocumentSignature(Document document, ValidationResult result, ValidationContext validationContext) throws IOException, SAXException, XPathExpressionException, TransformerException, XMLSecurityException { + private void validateDocumentSignature(Document document, ValidationResult result, SBDMessageContext messageContext) throws IOException, SAXException, XPathExpressionException, TransformerException, XMLSecurityException, ParserConfigurationException { if(logger.isTraceEnabled()) { - logger.trace("Validating Nemhandel e-Delivery signature for recursion level {}", validationContext.getRecursionLevel()); + logger.trace("Validating Nemhandel e-Delivery signature"); } - NodeList documentSignatures = getEdeliverySignatureNodes(document, validationContext.getSbdXPath()); - if(validationContext.getRecursionLevel() == 0 && !validationContext.getMessageContext().isDocumentSignatureRequired() && documentSignatures != null && documentSignatures.getLength() <= 0) { + MapNamespaceContext namespaceContext = new MapNamespaceContext(); + namespaceContext.addNamespace("sbd", Ns.SBDH); + + XPath xpath = XPathFactory.newInstance().newXPath(); + xpath.setNamespaceContext(namespaceContext); + + NodeList documentSignatures = getEdeliverySignatureNodes(document, xpath); + + if(!messageContext.isDocumentSignatureRequired() && documentSignatures != null && documentSignatures.getLength() <= 0) { logger.debug("No Nemhandel e-Delivery signature found. Skipping signature validation since the signature is marked as optional."); return; } if (documentSignatures == null || documentSignatures.getLength() <= 0) { - result.addError(ValidationMessage.fromErrorCode(validationContext.getMessageContext().isOutbound() ? ErrorCodes.SENDER_EDELIVERY_SIGNATURE_MISSING : ErrorCodes.RECEIVER_EDELIVERY_SIGNATURE_MISSING, EDELIVERY_SIGNATURE_XPATH, validationContext.getRecursionLevel())); - } else if(documentSignatures != null && documentSignatures.getLength() > 1) { - result.addError(ValidationMessage.fromErrorCode(validationContext.getMessageContext().isOutbound() ? ErrorCodes.SENDER_EDELIVERY_TOO_MANY_SIGNATURES : ErrorCodes.RECEIVER_EDELIVERY_TOO_MANY_SIGNATURES, EDELIVERY_SIGNATURE_XPATH, documentSignatures.getLength(), validationContext.getRecursionLevel())); + result.addError(ValidationMessage.fromErrorCode(messageContext.isOutbound() ? ErrorCodes.SENDER_EDELIVERY_SIGNATURE_MISSING : ErrorCodes.RECEIVER_EDELIVERY_SIGNATURE_MISSING, EDELIVERY_SIGNATURE_XPATH)); + } else if(documentSignatures.getLength() > 1) { + result.addError(ValidationMessage.fromErrorCode(messageContext.isOutbound() ? ErrorCodes.SENDER_EDELIVERY_TOO_MANY_SIGNATURES : ErrorCodes.RECEIVER_EDELIVERY_TOO_MANY_SIGNATURES, EDELIVERY_SIGNATURE_XPATH, documentSignatures.getLength())); } if(result.hasErrors()) { if(logger.isTraceEnabled()) { - logger.trace("Nemhandel e-Delivery signature for recursion level {} is not valid", validationContext.getRecursionLevel()); + logger.trace("Nemhandel e-Delivery signature is not valid"); } return; } - Node signatureNode = documentSignatures.item(0); - String txt = signatureNode.getTextContent(); - byte[] decoded = Base64.getDecoder().decode(txt); - - InMemoryDocument doc = new InMemoryDocument(decoded); - SignedDocumentValidator validator = SignedDocumentValidator.fromDocument(doc); - CertificateVerifier cv = certificateVerifierProvider.get(); - validator.setCertificateVerifier(cv); - ValidationPolicy policy = validationPolicyProvider.get(); - Reports reports = validator.validateDocument(policy); - if(!isValid(reports)) { - addSignatureValidationErrors(reports, result, validationContext); + String specVersion = XPathUtil.evaluateXPath(document, xpath, EDELIVERY_SIGNATURE_SPEC_VERSION_XPATH, null); + EDeliverySpecification spec = EDeliverySpecification.fromValue(specVersion); + if(!EDeliverySpecification.NEMHANDEL_EDELIVERY_1_2.equals(spec)) { + List specList = Arrays.stream(EDeliverySpecification.values()) + .map(e -> e.value()) + .sorted() + .collect(Collectors.toList()); + result.addError(ValidationMessage.fromErrorCode(messageContext.isOutbound() ? ErrorCodes.SENDER_EDELIVERY_SIGNATURE_INVALID_SPECIFICATION_VERSION : ErrorCodes.RECEIVER_EDELIVERY_SIGNATURE_INVALID_SPECIFICATION_VERSION, EDELIVERY_SIGNATURE_SPEC_VERSION_XPATH, specList)); } - DSSDocument originalSbd = null; - if(!result.hasErrors()) { - // Integrity of ASiC-E is ok, now check for presence of expected signed documents - ASiCWithXAdESContainerExtractor extractor = new ASiCWithXAdESContainerExtractor(doc); - ASiCContent content = extractor.extract(); - - List signedDocuments = content.getSignedDocuments(); - DSSDocument sbd = null; - - DSSDocument transferDelegation = null; - for(DSSDocument signed : signedDocuments) { - if(SignatureValidator.ASIC_SBD_FILENAME.equals(signed.getName())) { - sbd = signed; - } else if(SignatureValidator.ASIC_ORIGINAL_SBD_FILENAME.equals(signed.getName())) { - originalSbd = signed; - } else if(SignatureValidator.ASIC_TRANSFER_DELEGATION_FILENAME.equals(signed.getName())) { - transferDelegation = signed; - } - } - - validateTransferDelegation(transferDelegation, reports, result, validationContext); - validateStandardBusinessDocument(sbd, document, result, validationContext); - } - - if(result.hasErrors() && logger.isTraceEnabled()) { - logger.trace("Nemhandel e-Delivery signature for recursion level {} is not valid", validationContext.getRecursionLevel()); - } + Node signatureNode = documentSignatures.item(0); + String txt = signatureNode.getTextContent(); + byte[] decoded = Base64.getDecoder().decode(txt.getBytes(StandardCharsets.UTF_8)); - if(!result.hasErrors() && originalSbd != null) { - if(logger.isTraceEnabled()) { - logger.trace("Checking for recursively nested Nemhandel e-Delivery signature."); - } - // if original-standard-business-document.xml exists in ASiC container we need to recursively verify those as well. - try(InputStream bis = originalSbd.openStream()) { - Document originalDocument = validationContext.getDocumentBuilder().parse(bis); - - Element root = originalDocument.getDocumentElement(); - if(Ns.QNAME_SBD.getNamespaceURI().equals(root.getNamespaceURI()) && Ns.QNAME_SBD.getLocalPart().equals(root.getLocalName())) { - validationContext.setRecursionLevel(validationContext.getRecursionLevel() + 1); - validateDocumentSignature(originalDocument, result, validationContext); - } else { - result.addError(ValidationMessage.fromErrorCode(validationContext.getMessageContext().isOutbound() ? ErrorCodes.SENDER_EDELIVERY_ORIGINAL_SBD_IN_ASIC_IS_NOT_SBD : ErrorCodes.RECEIVER_EDELIVERY_ORIGINAL_SBD_IN_ASIC_IS_NOT_SBD, EDELIVERY_SIGNATURE_XPATH, validationContext.getRecursionLevel())); - } - } - } - } + InMemoryDocument signature = new InMemoryDocument(decoded); + try { + SignedDocumentValidator validator = documentValidatorFactory.create(signature); - private void validateStandardBusinessDocument(DSSDocument sbd, Document document, ValidationResult result, ValidationContext validationContext) throws XPathExpressionException, IOException, TransformerException, - XMLSecurityException { - if(sbd == null) { - result.addError(ValidationMessage.fromErrorCode(validationContext.getMessageContext().isOutbound() ? ErrorCodes.SENDER_EDELIVERY_MISSING_SBD_IN_ASIC : ErrorCodes.RECEIVER_EDELIVERY_MISSING_SBD_IN_ASIC, EDELIVERY_SIGNATURE_XPATH, validationContext.getRecursionLevel())); - } else { - /* - Verify that SBD matches the one from ASIC container. - Note: the only difference should be the added ASIC container so we remove that and compare the digest values. - This changes the document so we should not rely on the document afterwards! + Document payloadDocument = payloadExtractor.extractPayload(document); + /** + * NOTE: we need to serialize to byte-array before canonicalization, + * Otherwise namespaces may not be correctly available during canonicalization which will lead to validation errors. + * To avoid this SBDPayloadExtractor implementation must be changed to e.g. use XPath to extract payload */ - NodeList edeliverySignatureScopes = XPathUtil.evaluateXPathNodeList(document, - validationContext.getSbdXPath(), - "/sbd:StandardBusinessDocument/sbd:StandardBusinessDocumentHeader/sbd:BusinessScope/sbd:Scope[sbd:Type='NEMHANDEL_EDELIVERY_SIGNATURE']"); - if(edeliverySignatureScopes != null && edeliverySignatureScopes.getLength() > 0) { - for(int i = 0; i < edeliverySignatureScopes.getLength(); i++) { - Node sig = edeliverySignatureScopes.item(i); - sig.getParentNode().removeChild(sig); - } - } + DSSDocument payload = new InMemoryDocument(DocumentUtil.canonicalize(DocumentUtil.toByteArray(payloadDocument))); + validator.setDetachedContents(Collections.singletonList(payload)); - if(!documentMatchesSBDInAsicContainer(document, sbd)) { - result.addError(ValidationMessage.fromErrorCode(validationContext.getMessageContext().isOutbound() ? ErrorCodes.SENDER_EDELIVERY_SBD_DOES_NOT_MATCH_ASIC : ErrorCodes.RECEIVER_EDELIVERY_SBD_DOES_NOT_MATCH_ASIC, EDELIVERY_SIGNATURE_XPATH, validationContext.getRecursionLevel())); - } - } - } - - private void validateTransferDelegation(DSSDocument transferDelegation, Reports reports, ValidationResult result, ValidationContext validationContext) throws IOException, SAXException { - if(logger.isTraceEnabled()) { - logger.trace("Validating transfer-delegation.xml for recursion level {}", validationContext.getRecursionLevel()); - } - - if(transferDelegation == null) { - result.addError(ValidationMessage.fromErrorCode(validationContext.getMessageContext().isOutbound() ? ErrorCodes.SENDER_EDELIVERY_MISSING_TRANSFER_DELEGATION : ErrorCodes.RECEIVER_EDELIVERY_MISSING_TRANSFER_DELEGATION, EDELIVERY_SIGNATURE_XPATH, validationContext.getRecursionLevel())); - return; - } - - DiagnosticData diagnosticData = reports.getDiagnosticData(); + CertificateVerifier cv = certificateVerifierProvider.get(); + validator.setCertificateVerifier(cv); - try(InputStream is = transferDelegation.openStream()) { - Document document = validationContext.getDocumentBuilder().parse(is); - - XPath xpath = XPathFactory.newInstance().newXPath(); - MapNamespaceContext namespaceContext = new MapNamespaceContext(); - namespaceContext.addNamespace("td", "http://nemhandel.dk/e-delivery/transfer-delegation"); - xpath.setNamespaceContext(namespaceContext); - - String receiver = XPathUtil.evaluateXPath(document, xpath,"/td:TransferDelegation/td:Receiver", null); - if(receiver == null || receiver.isEmpty()) { - result.addError(ValidationMessage.fromErrorCode(validationContext.getMessageContext().isOutbound() ? ErrorCodes.SENDER_EDELIVERY_TRANSFER_DELEGATION_MISSING_RECEIVER_CVR : ErrorCodes.RECEIVER_EDELIVERY_TRANSFER_DELEGATION_MISSING_RECEIVER_CVR, EDELIVERY_SIGNATURE_XPATH, validationContext.getRecursionLevel())); - } else { - String receiverScheme = XPathUtil.evaluateXPath(document, xpath, "/td:TransferDelegation/td:Receiver/@schemeID", null); - if (!SignatureValidator.ASIC_TRANSFER_DELEGATION_SCHEME_CVR.equals(receiverScheme)) { - result.addError(ValidationMessage.fromErrorCode(validationContext.getMessageContext().isOutbound() ? ErrorCodes.SENDER_EDELIVERY_TRANSFER_DELEGATION_INVALID_RECEIVER_SCHEME : ErrorCodes.RECEIVER_EDELIVERY_TRANSFER_DELEGATION_INVALID_RECEIVER_SCHEME, EDELIVERY_SIGNATURE_XPATH, receiverScheme, SignatureValidator.ASIC_TRANSFER_DELEGATION_SCHEME_CVR, validationContext.getRecursionLevel())); - } - - if (validationContext.getRecursionLevel() == 0) { - // ensure we are the intended receiver (as per CVR) of the top-level transfer-delegation - String apCVR = CertificateUtil.extractCVR(accessPointCertificate); - - if (!receiver.equals(apCVR)) { - result.addError(ValidationMessage.fromErrorCode(validationContext.getMessageContext().isOutbound() ? ErrorCodes.SENDER_EDELIVERY_TRANSFER_DELEGATION_INVALID_RECEIVER_CVR : ErrorCodes.RECEIVER_EDELIVERY_TRANSFER_DELEGATION_INVALID_RECEIVER_CVR, EDELIVERY_SIGNATURE_XPATH, receiver, apCVR, validationContext.getRecursionLevel())); - } - } else if(validationContext.getRecursionLevel() > 0) { - // for non-top level transfer-delegations the receiver CVR should match the signing certificate of the previous ASiC container (since we are going top-down order) - // and the transfer delegation "chain" should be unbroken, i.e. receiver x = sender x-1 (because validation is going top-down) - String previousSigningCertCvr = CertificateUtil.extractCVR(validationContext.getPreviousSigningCertificate()); - - if (!receiver.equals(previousSigningCertCvr) || !receiver.equals(validationContext.getPreviousSenderCvr())) { - result.addError(ValidationMessage.fromErrorCode(validationContext.getMessageContext().isOutbound() ? ErrorCodes.SENDER_EDELIVERY_TRANSFER_DELEGATION_INVALID_RECEIVER_CVR : ErrorCodes.RECEIVER_EDELIVERY_TRANSFER_DELEGATION_INVALID_RECEIVER_CVR, EDELIVERY_SIGNATURE_XPATH, receiver, previousSigningCertCvr, validationContext.getRecursionLevel())); - } - } - } - - String sender = XPathUtil.evaluateXPath(document, xpath,"/td:TransferDelegation/td:Sender", null); - if(sender == null || sender.isEmpty()) { - result.addError(ValidationMessage.fromErrorCode(validationContext.getMessageContext().isOutbound() ? ErrorCodes.SENDER_EDELIVERY_TRANSFER_DELEGATION_MISSING_SENDER_CVR : ErrorCodes.RECEIVER_EDELIVERY_TRANSFER_DELEGATION_MISSING_SENDER_CVR, EDELIVERY_SIGNATURE_XPATH, validationContext.getRecursionLevel())); - } else { - String senderScheme = XPathUtil.evaluateXPath(document, xpath, "/td:TransferDelegation/td:Sender/@schemeID", null); - if (!SignatureValidator.ASIC_TRANSFER_DELEGATION_SCHEME_CVR.equals(senderScheme)) { - result.addError(ValidationMessage.fromErrorCode(validationContext.getMessageContext().isOutbound() ? ErrorCodes.SENDER_EDELIVERY_TRANSFER_DELEGATION_INVALID_SENDER_SCHEME : ErrorCodes.RECEIVER_EDELIVERY_TRANSFER_DELEGATION_INVALID_SENDER_SCHEME, EDELIVERY_SIGNATURE_XPATH, senderScheme, SignatureValidator.ASIC_TRANSFER_DELEGATION_SCHEME_CVR, validationContext.getRecursionLevel())); - } - - // validate that the Sender CVR specified in the transfer-delegation is the CVR from the signing certificate - // E.g. for a transfer delegation from C1 to C2 the sender should be C1s CVR and C1 should have signed the ASiC-E container - CertificateWrapper certWrapper = findTransferDelegationSignatureCertificate(diagnosticData); - String senderCertCvr = CertificateUtil.extractCVR(certWrapper); - if (!sender.equals(senderCertCvr)) { - result.addError(ValidationMessage.fromErrorCode(validationContext.getMessageContext().isOutbound() ? ErrorCodes.SENDER_EDELIVERY_TRANSFER_DELEGATION_INVALID_SENDER_CVR : ErrorCodes.RECEIVER_EDELIVERY_TRANSFER_DELEGATION_INVALID_SENDER_CVR, EDELIVERY_SIGNATURE_XPATH, sender, senderCertCvr, validationContext.getRecursionLevel())); - } - - validationContext.setPreviousSigningCertificate(certWrapper); - validationContext.setPreviousSenderCvr(sender); + ValidationPolicy policy = validationPolicyProvider.get(); + Reports reports = validator.validateDocument(policy); + if (!isValid(reports)) { + addSignatureValidationErrors(reports, result, messageContext); } + } catch(IllegalInputException e) { + // DSS throws IllegalInputException from SignedDocumentValidator.fromDocument(signature) if signature is not XML + result.addError(ValidationMessage.fromErrorCode(messageContext.isOutbound() ? ErrorCodes.SENDER_EDELIVERY_SIGNATURE_INVALID : ErrorCodes.RECEIVER_EDELIVERY_SIGNATURE_INVALID, EDELIVERY_SIGNATURE_XPATH, INVALID_SIGNATURE_FORMAT_DETAIL_MESSAGE)); } - } - private CertificateWrapper findTransferDelegationSignatureCertificate(DiagnosticData data) { - for(String id : data.getSignatureIdList()) { - List signedDocuments = data.getSignerDocuments(id); - if(signedDocuments != null) { - boolean found = signedDocuments.stream().anyMatch(d -> SignatureValidator.ASIC_TRANSFER_DELEGATION_FILENAME.equals(d.getReferencedName())); - if(found) { - SignatureWrapper wrapper = data.getSignatureById(id); - return wrapper.getSigningCertificate(); - } - } + if(result.hasErrors() && logger.isTraceEnabled()) { + logger.trace("Nemhandel e-Delivery signature is not valid"); } - return null; } - private void addSignatureValidationErrors(Reports reports, ValidationResult result, ValidationContext validationContext) { + private void addSignatureValidationErrors(Reports reports, ValidationResult result, SBDMessageContext messageContext) { SimpleReport simpleReport = reports.getSimpleReport(); List signatureIdList = simpleReport.getSignatureIdList(); - if(signatureIdList != null && !signatureIdList.isEmpty()) { + if(signatureIdList == null || signatureIdList.isEmpty()) { + result.addError(ValidationMessage.fromErrorCode(messageContext.isOutbound() ? ErrorCodes.SENDER_EDELIVERY_SIGNATURE_INVALID : ErrorCodes.RECEIVER_EDELIVERY_SIGNATURE_INVALID, EDELIVERY_SIGNATURE_XPATH, INVALID_SIGNATURE_FORMAT_DETAIL_MESSAGE)); + } else { for(String sigId : signatureIdList) { List validationMessages = simpleReport.getAdESValidationErrors(sigId); if(validationMessages != null && !validationMessages.isEmpty()) { for(eu.europa.esig.dss.jaxb.object.Message m : validationMessages) { String dssError = m.getKey() + ": " + m.getValue(); - result.addError(ValidationMessage.fromErrorCode(validationContext.getMessageContext().isOutbound() ? ErrorCodes.SENDER_EDELIVERY_SIGNATURE_INVALID : ErrorCodes.RECEIVER_EDELIVERY_SIGNATURE_INVALID, EDELIVERY_SIGNATURE_XPATH, validationContext.getRecursionLevel(), dssError)); + result.addError(ValidationMessage.fromErrorCode(messageContext.isOutbound() ? ErrorCodes.SENDER_EDELIVERY_SIGNATURE_INVALID : ErrorCodes.RECEIVER_EDELIVERY_SIGNATURE_INVALID, EDELIVERY_SIGNATURE_XPATH, dssError)); } } } } } - private boolean documentMatchesSBDInAsicContainer(Document document, DSSDocument sbd) throws IOException, TransformerException, - XMLSecurityException { - byte[] canonicalizedOuterXml = DocumentUtil.canonicalize(DocumentUtil.toByteArray(document)); - - byte[] canonicalizedXmlFromAsicContainer; - try(InputStream is = sbd.openStream()){ - canonicalizedXmlFromAsicContainer = DocumentUtil.canonicalize(IOUtils.toByteArray(is)); - } - - DSSDocument canonicalizedParsed = new InMemoryDocument(canonicalizedOuterXml); - DSSDocument canonicalizedFromAsicContainer = new InMemoryDocument(canonicalizedXmlFromAsicContainer); - String digestParsed = canonicalizedParsed.getDigest(DigestAlgorithm.SHA256); - String digestFromAsic = canonicalizedFromAsicContainer.getDigest(DigestAlgorithm.SHA256); - return digestParsed.equals(digestFromAsic); - } - private boolean isValid(Reports reports) { boolean validSignatureCountMatches = reports.getSimpleReport().getSignaturesCount() == reports.getSimpleReport().getValidSignaturesCount(); if(!validSignatureCountMatches || reports.getSimpleReport().getValidSignaturesCount() <= 0) @@ -381,62 +256,4 @@ public class SignatureValidatorImpl implements SignatureValidator { } return true; } - - private static class ValidationContext { - private int recursionLevel; - private DocumentBuilder documentBuilder; - private XPath sbdXPath; - private CertificateWrapper previousSigningCertificate; - private String previousSenderCvr; - - private SBDMessageContext messageContext; - - public int getRecursionLevel() { - return recursionLevel; - } - - public void setRecursionLevel(int recursionLevel) { - this.recursionLevel = recursionLevel; - } - - public DocumentBuilder getDocumentBuilder() { - return documentBuilder; - } - - public void setDocumentBuilder(DocumentBuilder documentBuilder) { - this.documentBuilder = documentBuilder; - } - - public CertificateWrapper getPreviousSigningCertificate() { - return previousSigningCertificate; - } - - public void setPreviousSigningCertificate(CertificateWrapper previousSigningCertificate) { - this.previousSigningCertificate = previousSigningCertificate; - } - - public XPath getSbdXPath() { - return sbdXPath; - } - - public void setSbdXPath(XPath sbdXPath) { - this.sbdXPath = sbdXPath; - } - - public String getPreviousSenderCvr() { - return previousSenderCvr; - } - - public void setPreviousSenderCvr(String previousSenderCvr) { - this.previousSenderCvr = previousSenderCvr; - } - - public SBDMessageContext getMessageContext() { - return messageContext; - } - - public void setMessageContext(SBDMessageContext messageContext) { - this.messageContext = messageContext; - } - } } diff --git a/src/main/java/dk/erst/oxalis/as4/validation/version/VersionValidator.java b/src/main/java/dk/erst/oxalis/as4/validation/version/VersionValidator.java index 88da88a..b5b998b 100644 --- a/src/main/java/dk/erst/oxalis/as4/validation/version/VersionValidator.java +++ b/src/main/java/dk/erst/oxalis/as4/validation/version/VersionValidator.java @@ -16,7 +16,7 @@ public interface VersionValidator { *
* The receiver and document type identifier in this message is used to do a SMP lookup (if not already up-to-date versions exist in {@link VersionCache}). * The SMP response contains a {@link ExtensionType} object that contains {@link EdelComponentVersion} objects with - * the receivers running version. The running oxalis & schematron versions of this instance will be validated that they are not outdated before sending the message. + * the receivers running version. The running oxalis and schematron versions of this instance will be validated that they are not outdated before sending the message. * * @param message The message to be sent. * @return The {@link ValidationResult} containing any errors or warnings. diff --git a/src/main/java/dk/erst/oxalis/as4/validation/version/VersionValidatorImpl.java b/src/main/java/dk/erst/oxalis/as4/validation/version/VersionValidatorImpl.java index a268c15..72cc5ac 100644 --- a/src/main/java/dk/erst/oxalis/as4/validation/version/VersionValidatorImpl.java +++ b/src/main/java/dk/erst/oxalis/as4/validation/version/VersionValidatorImpl.java @@ -15,6 +15,7 @@ import network.oxalis.vefa.peppol.lookup.LookupClient; import network.oxalis.vefa.peppol.lookup.api.LookupException; import network.oxalis.vefa.peppol.security.lang.PeppolSecurityException; import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.maven.artifact.versioning.DefaultArtifactVersion; @@ -164,7 +165,7 @@ public class VersionValidatorImpl implements VersionValidator { private static boolean isVersionNumberFormatValid(String currentVersionString) { DefaultArtifactVersion currentVersion = new DefaultArtifactVersion(currentVersionString); //if qualifier is null, the parsing went well. - return currentVersion.getQualifier() == null; + return currentVersion.getQualifier() == null || currentVersion.getQualifier().equals("RC"); } private String getOutOfDateTextMandatory(String component) { @@ -195,10 +196,11 @@ public class VersionValidatorImpl implements VersionValidator { )); } - private String getSoonOutOfDateText(DateTime date, String component) { + protected String getSoonOutOfDateText(DateTime date, String component) { + DateTime localTime = new DateTime(date).withZone(DateTimeZone.forID("Europe/Copenhagen")); return PrintUtil.prettyPrint(component +" is soon to be out of date!", Arrays.asList( - "On " + date.toString("EEEE, MMM dd, yyyy HH:mm:ss", Locale.ENGLISH) + " this " + component, + "On " + localTime.toString("EEEE, MMM dd, yyyy HH:mm:ss", Locale.ENGLISH) + " this " + component, "will be obsolete!", "Please make sure you have upgraded before then", "as by then it can no longer serve to receive or send documents.", diff --git a/src/main/java/dk/erst/oxalis/as4/validation/version/model/JodaTimeAdapter.java b/src/main/java/dk/erst/oxalis/as4/validation/version/model/JodaTimeAdapter.java index 1b2cdef..acdda94 100644 --- a/src/main/java/dk/erst/oxalis/as4/validation/version/model/JodaTimeAdapter.java +++ b/src/main/java/dk/erst/oxalis/as4/validation/version/model/JodaTimeAdapter.java @@ -2,7 +2,6 @@ package dk.erst.oxalis.as4.validation.version.model; import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; @@ -17,7 +16,7 @@ public class JodaTimeAdapter extends XmlAdapter { /** * The DateTimeFormatter used to parse and format DateTime objects. */ - private static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss").withZone(DateTimeZone.UTC); + private static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ssZ"); /** * Marshals the provided DateTime object into its XML string representation. @@ -41,4 +40,3 @@ public class JodaTimeAdapter extends XmlAdapter { return DATETIME_FORMATTER.parseDateTime(v); } } - diff --git a/src/main/java/network/oxalis/as4/config/As4Conf.java b/src/main/java/network/oxalis/as4/config/As4Conf.java index ac22d81..29e8f60 100644 --- a/src/main/java/network/oxalis/as4/config/As4Conf.java +++ b/src/main/java/network/oxalis/as4/config/As4Conf.java @@ -42,7 +42,7 @@ public enum As4Conf { TYPE, @Path("oxalis.as4.sbdh.limit") - //Default value 60 MB for now. Should be enough to support 10 MB payload in 3 asic containers. + //Default value 60 MB for now. @DefaultValue("60000000") SBDH_LIMIT } diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_Alphanumeric40LetterNonemptyText.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_Alphanumeric40LetterNonemptyText.xsd new file mode 100644 index 0000000..3715fa8 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_Alphanumeric40LetterNonemptyText.xsd @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_BICIdentifier.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_BICIdentifier.xsd new file mode 100644 index 0000000..6da88d8 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_BICIdentifier.xsd @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_BankAccountUnavailable.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_BankAccountUnavailable.xsd new file mode 100644 index 0000000..3f96e1d --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_BankAccountUnavailable.xsd @@ -0,0 +1,14 @@ + + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_BankAccountUnavailableCode.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_BankAccountUnavailableCode.xsd new file mode 100644 index 0000000..8ff6b35 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_BankAccountUnavailableCode.xsd @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_BankAccountUnavailableText.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_BankAccountUnavailableText.xsd new file mode 100644 index 0000000..73acf99 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_BankAccountUnavailableText.xsd @@ -0,0 +1,5 @@ + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_ForeignBankAccount.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_ForeignBankAccount.xsd new file mode 100644 index 0000000..e0b0c80 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_ForeignBankAccount.xsd @@ -0,0 +1,13 @@ + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_ForeignBankAccountIdentifier.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_ForeignBankAccountIdentifier.xsd new file mode 100644 index 0000000..639e68a --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_ForeignBankAccountIdentifier.xsd @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_ForeignBankBranchCode.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_ForeignBankBranchCode.xsd new file mode 100644 index 0000000..edc6946 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_ForeignBankBranchCode.xsd @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_ForeignBankInfoStructure.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_ForeignBankInfoStructure.xsd new file mode 100644 index 0000000..b445b40 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_ForeignBankInfoStructure.xsd @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_ForeignPersonInfoStructure.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_ForeignPersonInfoStructure.xsd new file mode 100644 index 0000000..2590341 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_ForeignPersonInfoStructure.xsd @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_IBANIdentifier.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_IBANIdentifier.xsd new file mode 100644 index 0000000..dca26a7 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_IBANIdentifier.xsd @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_InvalidMessageFormat.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_InvalidMessageFormat.xsd new file mode 100644 index 0000000..489c95c --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_InvalidMessageFormat.xsd @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_InvalidMessageFormatCode.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_InvalidMessageFormatCode.xsd new file mode 100644 index 0000000..88c71bd --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_InvalidMessageFormatCode.xsd @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_InvalidMessageFormatText.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_InvalidMessageFormatText.xsd new file mode 100644 index 0000000..42a06a1 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_InvalidMessageFormatText.xsd @@ -0,0 +1,5 @@ + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_InvalidMessageFormathighestseverityCode.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_InvalidMessageFormathighestseverityCode.xsd new file mode 100644 index 0000000..73e175e --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_InvalidMessageFormathighestseverityCode.xsd @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_LookupDateTime.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_LookupDateTime.xsd new file mode 100644 index 0000000..084c0c9 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_LookupDateTime.xsd @@ -0,0 +1,5 @@ + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoAccountHolder.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoAccountHolder.xsd new file mode 100644 index 0000000..f8c6920 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoAccountHolder.xsd @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoAccountInformation.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoAccountInformation.xsd new file mode 100644 index 0000000..2a525f9 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoAccountInformation.xsd @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoCurrencyCode.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoCurrencyCode.xsd new file mode 100644 index 0000000..2e3738b --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoCurrencyCode.xsd @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoPrivatUdbetalerTransporterRequest.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoPrivatUdbetalerTransporterRequest.xsd new file mode 100644 index 0000000..2540fcd --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoPrivatUdbetalerTransporterRequest.xsd @@ -0,0 +1,14 @@ + + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoPrivatUdbetalerTransporterResponse.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoPrivatUdbetalerTransporterResponse.xsd new file mode 100644 index 0000000..5e11e99 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoPrivatUdbetalerTransporterResponse.xsd @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoRequest.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoRequest.xsd new file mode 100644 index 0000000..6a8b02b --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoRequest.xsd @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoRequestError.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoRequestError.xsd new file mode 100644 index 0000000..ee9ec95 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoRequestError.xsd @@ -0,0 +1,13 @@ + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoRequestErrorCode.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoRequestErrorCode.xsd new file mode 100644 index 0000000..39791c3 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoRequestErrorCode.xsd @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoRequestErrorText.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoRequestErrorText.xsd new file mode 100644 index 0000000..04c3f75 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoRequestErrorText.xsd @@ -0,0 +1,5 @@ + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoResponse.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoResponse.xsd new file mode 100644 index 0000000..107198f --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoResponse.xsd @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_ProductionUnitIdentificationStructure.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_ProductionUnitIdentificationStructure.xsd new file mode 100644 index 0000000..38aae8c --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_ProductionUnitIdentificationStructure.xsd @@ -0,0 +1,16 @@ + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_Requester.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_Requester.xsd new file mode 100644 index 0000000..81c643e --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_Requester.xsd @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_RequesterPaymentReference.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_RequesterPaymentReference.xsd new file mode 100644 index 0000000..1fd0f8b --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_RequesterPaymentReference.xsd @@ -0,0 +1,6 @@ + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_RequesterPrimaryInternalReference.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_RequesterPrimaryInternalReference.xsd new file mode 100644 index 0000000..68cdc88 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_RequesterPrimaryInternalReference.xsd @@ -0,0 +1,6 @@ + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_RequesterSecondaryInternalReference.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_RequesterSecondaryInternalReference.xsd new file mode 100644 index 0000000..89f735e --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_RequesterSecondaryInternalReference.xsd @@ -0,0 +1,6 @@ + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_SEnumberIdentificationStructure.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_SEnumberIdentificationStructure.xsd new file mode 100644 index 0000000..6608eef --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_SEnumberIdentificationStructure.xsd @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_SEnumberIdentifier.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_SEnumberIdentifier.xsd new file mode 100644 index 0000000..12fa174 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_SEnumberIdentifier.xsd @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_TransporterAgreementIdentifier.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_TransporterAgreementIdentifier.xsd new file mode 100644 index 0000000..883857d --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_TransporterAgreementIdentifier.xsd @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_TransporterGroupIdentifier.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_TransporterGroupIdentifier.xsd new file mode 100644 index 0000000..e366b5c --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_TransporterGroupIdentifier.xsd @@ -0,0 +1,7 @@ + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_TransporterHeader.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_TransporterHeader.xsd new file mode 100644 index 0000000..67d2035 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_TransporterHeader.xsd @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_TransporterHeaderError.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_TransporterHeaderError.xsd new file mode 100644 index 0000000..1135c68 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_TransporterHeaderError.xsd @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_TransporterHeaderErrorCode.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_TransporterHeaderErrorCode.xsd new file mode 100644 index 0000000..716c7ed --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_TransporterHeaderErrorCode.xsd @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_TransporterHeaderErrorText.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_TransporterHeaderErrorText.xsd new file mode 100644 index 0000000..8dcb17a --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_TransporterHeaderErrorText.xsd @@ -0,0 +1,5 @@ + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_TransporterSystemIdentifier.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_TransporterSystemIdentifier.xsd new file mode 100644 index 0000000..dae7b57 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_TransporterSystemIdentifier.xsd @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_VansHeader.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_VansHeader.xsd new file mode 100644 index 0000000..a8e165a --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_VansHeader.xsd @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_VansNemkontoEnvironmentCode.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_VansNemkontoEnvironmentCode.xsd new file mode 100644 index 0000000..65b0ca9 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_VansNemkontoEnvironmentCode.xsd @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_VansRecipientAddress.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_VansRecipientAddress.xsd new file mode 100644 index 0000000..b41c536 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_VansRecipientAddress.xsd @@ -0,0 +1,6 @@ + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_VansSenderAddress.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_VansSenderAddress.xsd new file mode 100644 index 0000000..3b5ca91 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_VansSenderAddress.xsd @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_alphanumeric40letternonemptytext.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_alphanumeric40letternonemptytext.xsd.meta.xml new file mode 100644 index 0000000..00a674d --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_alphanumeric40letternonemptytext.xsd.meta.xml @@ -0,0 +1,9 @@ + + + Alphanumeric40LetterNonemptyText + + No description + Definition på alfanummerisk tekst – min 1 tegn, max 40 tegn tekst + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_alphanumeric40letternonemptytext.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_alphanumeric40letternonemptytext.xsd.system.xml new file mode 100644 index 0000000..6467e3d --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_alphanumeric40letternonemptytext.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bankaccountunavailable.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bankaccountunavailable.xsd.meta.xml new file mode 100644 index 0000000..3f8873a --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bankaccountunavailable.xsd.meta.xml @@ -0,0 +1,16 @@ + + + BankAccountUnavailable + + No description + En struktur bestående af en kode og en tekst der angiver at indehaveren er kendt men at NemKontoen for den efterspurgte indehaver ikke findes eller er lukket. + + + BankAccountUnavailable + BankKontoUtilgængelig + + Lars Harritshøj + + BankAccountUnavailable + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bankaccountunavailable.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bankaccountunavailable.xsd.system.xml new file mode 100644 index 0000000..b6265ab --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bankaccountunavailable.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bankaccountunavailablecode.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bankaccountunavailablecode.xsd.meta.xml new file mode 100644 index 0000000..4c1cf4c --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bankaccountunavailablecode.xsd.meta.xml @@ -0,0 +1,15 @@ + + + BankAccountUnavailableCode + + No description + En kode der angiver årsagen til at kontooplysninger ikke kan leveres. + + + BankAccountUnavailableCode + Kode for utilgængelig konto + + + BankAccountUnavailableCode + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bankaccountunavailablecode.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bankaccountunavailablecode.xsd.system.xml new file mode 100644 index 0000000..40d16f2 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bankaccountunavailablecode.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bankaccountunavailabletext.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bankaccountunavailabletext.xsd.meta.xml new file mode 100644 index 0000000..35c45dc --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bankaccountunavailabletext.xsd.meta.xml @@ -0,0 +1,16 @@ + + + BankAccountUnavailableText + + No description + En beskrivende tekst, der angiver årsagen til at kontooplysninger ikke kan leveres. + + + BankAccountUnavailableText + Årsagstekst for utilgængelig konto + + Lars Harritshøj + + BankAccountUnavailableText + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bankaccountunavailabletext.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bankaccountunavailabletext.xsd.system.xml new file mode 100644 index 0000000..2ba49a0 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bankaccountunavailabletext.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bicidentifier.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bicidentifier.xsd.meta.xml new file mode 100644 index 0000000..8e786c6 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bicidentifier.xsd.meta.xml @@ -0,0 +1,23 @@ + + + BICIdentifier + + No description + SWIFT – også kaldet BIC (”Bank Identifier Code”) - er en international bank-identifikationskode på 8 eller 11 bogstaver og tal. + BIC er en struktureret adresse som identifierer parter i finansielle transaktioner. BIC anvendes i hele verden af finansielle systemer især i forbindelse med udveksling af data mellem it-systemer. + + BICs opbygning + De første 4 tegn er altid bogstaver og identificerer banken. Herefter kommer 2 bogstaver som er koden for det land, betalingen skal gå til. De første tegn for Danmarks Nationalbank vil derfor være: DKNBDK + Herefter kommer 2 tal eller bogstaver som identificerer lokationen (byen). For Danmarks Nationalbanks vedkommende: KK. Koden bliver altså samlet: DKNBDKKK + Dette er et pengeinstituts ”hovedadresse”. Skal et beløb sendes til en afdeling af en bank, KAN der i SWIFT-koden være 3 yderligere bogstaver/tal, som kaldes "branchcode" (afdelingskode). + Bemærk mærke til at SWIFT-koden altid skal skrives ud i ét, ingen mellemrum. Og altid 8 eller 11 tegn. + + Mere information kan findes på: http://www.swift.com/ + + + + BICIdentifier + BICIdentifier + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bicidentifier.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bicidentifier.xsd.system.xml new file mode 100644 index 0000000..47d857c --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_bicidentifier.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankaccount.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankaccount.xsd.meta.xml new file mode 100644 index 0000000..26497f2 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankaccount.xsd.meta.xml @@ -0,0 +1,16 @@ + + + ForeignBankAccount + + No description + En struktur som indeholder to overordnede strukturer ForeignBankInfoStructure og ForeignPersonStructure. + + + ForeignBankAccount + Udenlandsk bankkontostruktur + + Lars Harritshøj + + Udenlandskbankkontostruktur + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankaccount.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankaccount.xsd.system.xml new file mode 100644 index 0000000..d99ff92 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankaccount.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankaccountidentifier.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankaccountidentifier.xsd.meta.xml new file mode 100644 index 0000000..9d37f50 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankaccountidentifier.xsd.meta.xml @@ -0,0 +1,12 @@ + + + ForeignBankAccountIdentifier + + No description + Udenlandsk kontonummer. + + + Udenlandsk kontonummer + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankaccountidentifier.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankaccountidentifier.xsd.system.xml new file mode 100644 index 0000000..b1515ad --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankaccountidentifier.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankbranchcode.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankbranchcode.xsd.meta.xml new file mode 100644 index 0000000..a878ee8 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankbranchcode.xsd.meta.xml @@ -0,0 +1,9 @@ + + + ForeignBankBranchCode + + No description + Udenlandsk registreringsnummer. + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankbranchcode.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankbranchcode.xsd.system.xml new file mode 100644 index 0000000..d7118fa --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankbranchcode.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankinfostructure.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankinfostructure.xsd.meta.xml new file mode 100644 index 0000000..383516a --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankinfostructure.xsd.meta.xml @@ -0,0 +1,13 @@ + + + ForeignBankInfoStructure + + No description + Strukturtype som indeholder en underliggende struktur ForeignBankAccountIdentifier og andre felter + + + ForeignBankInfoText + Udenlandsk bank informationsstruktur. + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankinfostructure.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankinfostructure.xsd.system.xml new file mode 100644 index 0000000..a186e17 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignbankinfostructure.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignpersoninfostructure.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignpersoninfostructure.xsd.meta.xml new file mode 100644 index 0000000..f2e4dfb --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignpersoninfostructure.xsd.meta.xml @@ -0,0 +1,13 @@ + + + ForeignPersonInfoStructure + + No description + Strukturtype som indeholder en underliggende struktur ForeignBankAccountIdentifier og andre felter + + + ForeignBankInfoText + Udenlandsk bank informationsstruktur. + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignpersoninfostructure.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignpersoninfostructure.xsd.system.xml new file mode 100644 index 0000000..d6dc07e --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_foreignpersoninfostructure.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_ibanidentifier.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_ibanidentifier.xsd.meta.xml new file mode 100644 index 0000000..273b7ef --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_ibanidentifier.xsd.meta.xml @@ -0,0 +1,27 @@ + + + IBANIdentifier + + No description + IBAN-nummeret (”International Bank Account Number”) er den internationale udgave af et kontonummer. + IBAN er en entydig identifikation af en konto i et pengeinstitut i EU og andre vestlige lande. IBAN afløser ikke det oprindelige kontonummer, men er et supplement der benyttes i international sammenhæng. + Det gør en automatisk behandling af betalinger over landegrænser mulig, da IBAN kontrolleres for gyldighed af afsendende og modtagende pengeinstitut. + Alle pengeinstitutter i EU har indført IBAN. Derudover har flere lande uden for EU indført IBAN, så IBAN kan også kan benyttes til overførsler til fx USA eller Japan. + IBAN-nummeret kan typisk ses på kontoudtog eller oplyses ved henvendelse til sit pengeinstitut. + IBAN’s opbygning + IBAN består af op til 34 tegn, og et dansk IBAN kunne fx se sådan ud: DK9912344231123456. + IBAN-nummeret består af fire elementer: + - landekode – for Danmarks vedkommende DK + - et kontrolnummer på 2 cifre + - bankens registreringsnummer + - det normale kontonummer + IBAN består af op til 34 tegn, og et dansk IBAN kunne fx se sådan ud: DK9912344231123456. + Mere information kan findes på: http://www.ecbs.org/ + + + + IBAN, International Bank Account Number + IBAN, International Bank Account Number fjernes International bankkontonummer + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_ibanidentifier.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_ibanidentifier.xsd.system.xml new file mode 100644 index 0000000..4894b61 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_ibanidentifier.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformat.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformat.xsd.meta.xml new file mode 100644 index 0000000..37f2698 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformat.xsd.meta.xml @@ -0,0 +1,13 @@ + + + InvalidMessageFormat + + No description + Denne struktur indeholder den alvorligste fejltype, koder og de tilhørende forklarende tekster der beskriver fejlene og sendes hvis betalingsmeddelelsen ikke overholder den gældende version af OIOXML specifikationen + + + InvalidMessageFormat + Fejltekststruktur + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformat.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformat.xsd.system.xml new file mode 100644 index 0000000..67b4fc3 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformat.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformatcode.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformatcode.xsd.meta.xml new file mode 100644 index 0000000..82fe464 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformatcode.xsd.meta.xml @@ -0,0 +1,13 @@ + + + InvalidMessageFormatCode + + No description + En kode der angiver at input ikke overholder en af de gældende OIOXML specifikationer. + + + InvalidMessageFormatCode + Fejlkode + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformatcode.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformatcode.xsd.system.xml new file mode 100644 index 0000000..f2c0c0d --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformatcode.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformathighestseveritycode.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformathighestseveritycode.xsd.meta.xml new file mode 100644 index 0000000..f161ee5 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformathighestseveritycode.xsd.meta.xml @@ -0,0 +1,13 @@ + + + InvalidMessageFormathighestseverityCode + + No description + En kode der angiver den alvorligste fejltype fundet ved læsning af XML. + + + InvalidMessageFormathighestseverityCode + Alvorligste fejltype + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformathighestseveritycode.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformathighestseveritycode.xsd.system.xml new file mode 100644 index 0000000..d905c37 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformathighestseveritycode.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformattext.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformattext.xsd.meta.xml new file mode 100644 index 0000000..1330808 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformattext.xsd.meta.xml @@ -0,0 +1,13 @@ + + + InvalidMessageFormatText + + No description + En tekst der beskriver den modtagne fejlkode + + + InvalidMessageFormatText + Fejltekst + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformattext.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformattext.xsd.system.xml new file mode 100644 index 0000000..dfbc9f2 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_invalidmessageformattext.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_lookupdatetime.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_lookupdatetime.xsd.meta.xml new file mode 100644 index 0000000..56bdd25 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_lookupdatetime.xsd.meta.xml @@ -0,0 +1,13 @@ + + + LookupDateTime + + No description + Behandlingstidspunktet for en forespørgsel + + + LookupDataTime + Tid + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_lookupdatetime.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_lookupdatetime.xsd.system.xml new file mode 100644 index 0000000..55057e7 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_lookupdatetime.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoaccountholder.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoaccountholder.xsd.meta.xml new file mode 100644 index 0000000..b431521 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoaccountholder.xsd.meta.xml @@ -0,0 +1,13 @@ + + + NemkontoAccountHolder + + No description + En struktur med indehaveren af en nemkonto som er identificeret ved et CPR-, et CVR-, SE- eller et P-nummer. + + + NemkontoAccountHolderType + Nemkontoejer + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoaccountholder.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoaccountholder.xsd.system.xml new file mode 100644 index 0000000..f348df0 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoaccountholder.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoaccountinformation.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoaccountinformation.xsd.meta.xml new file mode 100644 index 0000000..257d496 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoaccountinformation.xsd.meta.xml @@ -0,0 +1,13 @@ + + + NemkontoAccountInformation + + No description + Struktur der angiver indehavers kontonummer i form af en indenlandsk eller udenlandsk konto eller en besked om nemkontoen ikke er tilgængelig. + + + NemkontoAccountInformation. + Nemkontoinformation + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoaccountinformation.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoaccountinformation.xsd.system.xml new file mode 100644 index 0000000..041db4b --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoaccountinformation.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontocurrencycode.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontocurrencycode.xsd.meta.xml new file mode 100644 index 0000000..b271821 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontocurrencycode.xsd.meta.xml @@ -0,0 +1,9 @@ + + + NemkontoCurrencyCode + + No description + Valutakode - Et repræsentativt udsnit af ISO 4217 valutakode standarden + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontocurrencycode.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontocurrencycode.xsd.system.xml new file mode 100644 index 0000000..916e694 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontocurrencycode.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoprivatudbetalertransporterrequest.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoprivatudbetalertransporterrequest.xsd.meta.xml new file mode 100644 index 0000000..8b97f1c --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoprivatudbetalertransporterrequest.xsd.meta.xml @@ -0,0 +1,13 @@ + + + NemkontoPrivatUdbetalerTransporterRequest + + No description + Struktur som indeholder strukturerne TransporterHeader og NemkontoRequest. + + + NemkontoPrivateUdbetalerTransportRequester + Nemkonto Forespørgsel med Header + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoprivatudbetalertransporterrequest.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoprivatudbetalertransporterrequest.xsd.system.xml new file mode 100644 index 0000000..fd0ed91 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoprivatudbetalertransporterrequest.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoprivatudbetalertransporterresponse.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoprivatudbetalertransporterresponse.xsd.meta.xml new file mode 100644 index 0000000..ccc840e --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoprivatudbetalertransporterresponse.xsd.meta.xml @@ -0,0 +1,13 @@ + + + NemkontoPrivatUdbetalerTransporterResponse + + No description + Struktur der indeholder strukturerne: InvalidMessageFormat, TrasporterHeaderError eller TransporterHeader og NemkontoResponse + + + NemkontoPrivateUdbetalerTransportResponse + Nemkonto svar med Header + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoprivatudbetalertransporterresponse.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoprivatudbetalertransporterresponse.xsd.system.xml new file mode 100644 index 0000000..a451924 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoprivatudbetalertransporterresponse.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequest.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequest.xsd.meta.xml new file mode 100644 index 0000000..2d540a7 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequest.xsd.meta.xml @@ -0,0 +1,13 @@ + + + NemkontoRequest + + No description + Strukturen indeholder strukturerne Requester, RequesterPaymentReference, RequesterPrimaryInternalReference, RequesterSecondaryInternalReference og NemkontoAccountHolder. + + + NemkontoRequest + Nemkonto Forespørgsel uden Header + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequest.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequest.xsd.system.xml new file mode 100644 index 0000000..ef58795 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequest.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequesterror.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequesterror.xsd.meta.xml new file mode 100644 index 0000000..c5d3dc7 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequesterror.xsd.meta.xml @@ -0,0 +1,13 @@ + + + NemkontoRequestError + + No description + En struktur bestående af en kode og en tekst, der beskriver årsagen til at nemkontonummer ikke kan returneres. Fx fordi den forespurgte Nemkonto indehaver er ukendt eller den private udbetaler er registreret i misligholdelsesregisteret. + + + NemkontoRequestError + Valideringsfejl + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequesterror.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequesterror.xsd.system.xml new file mode 100644 index 0000000..00510b3 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequesterror.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequesterrorcode.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequesterrorcode.xsd.meta.xml new file mode 100644 index 0000000..cd6d1e3 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequesterrorcode.xsd.meta.xml @@ -0,0 +1,13 @@ + + + NemkontoRequestErrorCode + + No description + En kode der angiver hvorfor forespørgslen ikke blev behandlet + + + NemkontoRequestErrorCode + Valideringsfejlkode + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequesterrorcode.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequesterrorcode.xsd.system.xml new file mode 100644 index 0000000..0f4ee4c --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequesterrorcode.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequesterrortext.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequesterrortext.xsd.meta.xml new file mode 100644 index 0000000..82bc207 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequesterrortext.xsd.meta.xml @@ -0,0 +1,13 @@ + + + NemkontoRequestErrorText + + No description + En beskrivende tekst, bestemt af koden, der fortæller hvorfor betalingsmeddelelsen ikke blev behandlet + + + NemkontoRequestErrorText + Valideringsfejltekst + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequesterrortext.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequesterrortext.xsd.system.xml new file mode 100644 index 0000000..876e890 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontorequesterrortext.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoresponse.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoresponse.xsd.meta.xml new file mode 100644 index 0000000..826776a --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoresponse.xsd.meta.xml @@ -0,0 +1,13 @@ + + + NemkontoResponse + + No description + Struktur der indeholder strukturerne: NemkontoRequest, LookupDateTime, og enten NemkontoAccountInformation eller NemkontorequestError + + + NemkontoResponse + Nemkonto Forespørgsel uden Header + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoresponse.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoresponse.xsd.system.xml new file mode 100644 index 0000000..13f2769 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_nemkontoresponse.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_productionunitidentificationstructure.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_productionunitidentificationstructure.xsd.meta.xml new file mode 100644 index 0000000..8cedb30 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_productionunitidentificationstructure.xsd.meta.xml @@ -0,0 +1,9 @@ + + + ProductionUnitIdentificationStructure + + No description + Strukturen indeholder et CVR-nummer- og et produktionsenhedsnummer element, da det øger sikkerheden for korrekt identifikation og dermed mindsker risikoen for fejludbetalinger. + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_productionunitidentificationstructure.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_productionunitidentificationstructure.xsd.system.xml new file mode 100644 index 0000000..266f497 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_productionunitidentificationstructure.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requester.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requester.xsd.meta.xml new file mode 100644 index 0000000..e21c173 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requester.xsd.meta.xml @@ -0,0 +1,13 @@ + + + Requester + + No description + Strukturen indeholder CVRnumberIdentifier, PersonCivilRegistrationIdentifier eller strukturen SEnumberIdentifier. + + + Requester + Forespørger + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requester.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requester.xsd.system.xml new file mode 100644 index 0000000..f341646 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requester.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requesterpaymentreference.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requesterpaymentreference.xsd.meta.xml new file mode 100644 index 0000000..7f960d8 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requesterpaymentreference.xsd.meta.xml @@ -0,0 +1,12 @@ + + + RequesterPaymentReference + + No desprintion + En reference til den private udbetalers system som identificerer den udbetaling, som NemKonto forespørgslen skal bruges til at gennemføre. + + + RequesterPaymentReference + Identifikation af forespørgsel + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requesterpaymentreference.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requesterpaymentreference.xsd.system.xml new file mode 100644 index 0000000..c1aa7a2 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requesterpaymentreference.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requesterprimaryinternalreference.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requesterprimaryinternalreference.xsd.meta.xml new file mode 100644 index 0000000..32a7152 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requesterprimaryinternalreference.xsd.meta.xml @@ -0,0 +1,13 @@ + + + RequesterPrimaryInternalReference + + No description + Dette element er valgfrit og kan indeholde en reference som den private udbetaler kan benytte efter behov. + + + RequesterPrimaryInternalReference + Valgfri reference 1 + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requesterprimaryinternalreference.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requesterprimaryinternalreference.xsd.system.xml new file mode 100644 index 0000000..9a30d27 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requesterprimaryinternalreference.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requestersecondaryinternalreference.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requestersecondaryinternalreference.xsd.meta.xml new file mode 100644 index 0000000..8b7e394 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requestersecondaryinternalreference.xsd.meta.xml @@ -0,0 +1,12 @@ + + + RequesterSecondaryInternalReference + + No description + Dette element er valgfrit og kan indeholde en reference som den private udbetaler kan benytte efter behov. + + + RequesterSecondaryInternalReference + Valgfri reference 2 + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requestersecondaryinternalreference.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requestersecondaryinternalreference.xsd.system.xml new file mode 100644 index 0000000..98345a7 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_requestersecondaryinternalreference.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_senumberidentificationstructure.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_senumberidentificationstructure.xsd.meta.xml new file mode 100644 index 0000000..f75597f --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_senumberidentificationstructure.xsd.meta.xml @@ -0,0 +1,9 @@ + + + SEnumberIdentificationStructure + + No description + Struktur som indeholder strukturerne CVRnumberIdentifier og SEnumberIdentifier + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_senumberidentificationstructure.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_senumberidentificationstructure.xsd.system.xml new file mode 100644 index 0000000..a78bc88 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_senumberidentificationstructure.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_senumberidentifier.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_senumberidentifier.xsd.meta.xml new file mode 100644 index 0000000..a09ebe5 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_senumberidentifier.xsd.meta.xml @@ -0,0 +1,13 @@ + + + SEnumberIdentifier + + No description + SE-nummer (SE står for Stamregister over erhvervsdrivende) er det nummer man får tildelt af SKAT i forbindelse med registreringen som en virksomhed, der skal trække bidrag og A-skat. Det er det nummer, som virksomheden er registreret under i det register, som SKAT har oprettet til brug for bl.a. opkrævningen af AM-bidrag, SP-bidrag og A-skat. SE-numre bruges af SKAT til regnskabsmæssig opdeling af virksomheden. + + + SEnumberIdentifier + SE nummer + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_senumberidentifier.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_senumberidentifier.xsd.system.xml new file mode 100644 index 0000000..282bf1e --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_senumberidentifier.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporteragreementidentifier.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporteragreementidentifier.xsd.meta.xml new file mode 100644 index 0000000..b4b2361 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporteragreementidentifier.xsd.meta.xml @@ -0,0 +1,13 @@ + + + TransporterAgreementIdentifier + + No description + Aftalenummeret som den private betalingsformidler har med NemKonto. + + + TransporterAgreementIdentifier + Aftalenummer + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporteragreementidentifier.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporteragreementidentifier.xsd.system.xml new file mode 100644 index 0000000..ebb1dbd --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporteragreementidentifier.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transportergroupidentifier.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transportergroupidentifier.xsd.meta.xml new file mode 100644 index 0000000..2a401bb --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transportergroupidentifier.xsd.meta.xml @@ -0,0 +1,13 @@ + + + TransporterGroupIdentifier + + No description + Identificerer bundtet overfor såvel NKS PU som PBF. + + + TransporterGroupIdentifier + BundtId + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transportergroupidentifier.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transportergroupidentifier.xsd.system.xml new file mode 100644 index 0000000..ffcb88c --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transportergroupidentifier.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheader.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheader.xsd.meta.xml new file mode 100644 index 0000000..67c3f69 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheader.xsd.meta.xml @@ -0,0 +1,13 @@ + + + TransporterHeader + + No description + Struktur som indeholder TransporterAgreementIdentifier, TrasporterSystemIdentifier, TransporterGroupIdentifier samt eventuelt VansHeader + + + TransporterHeader + TransporterHeader + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheader.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheader.xsd.system.xml new file mode 100644 index 0000000..7981963 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheader.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheadererror.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheadererror.xsd.meta.xml new file mode 100644 index 0000000..a47af42 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheadererror.xsd.meta.xml @@ -0,0 +1,13 @@ + + + TransporterHeaderError + + No description + Struktur der indeholder TransporterHeader og TransporterheaderErrorCode og TransporterHeaderErrorText + + + TransporterHeaderError + TransporterHeader fejl + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheadererror.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheadererror.xsd.system.xml new file mode 100644 index 0000000..c369cf6 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheadererror.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheadererrorcode.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheadererrorcode.xsd.meta.xml new file mode 100644 index 0000000..c1d8e1a --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheadererrorcode.xsd.meta.xml @@ -0,0 +1,13 @@ + + + TransporterHeaderErrorCode + + No description + En kode der optræder når data i TransporterHeader ikke kan accepteres. + + + TransporterHeaderErrorCode + TransporterHeader fejlkode + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheadererrorcode.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheadererrorcode.xsd.system.xml new file mode 100644 index 0000000..f45acd0 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheadererrorcode.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheadererrortext.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheadererrortext.xsd.meta.xml new file mode 100644 index 0000000..08a7acb --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheadererrortext.xsd.meta.xml @@ -0,0 +1,13 @@ + + + TransporterHeaderErrorText + + No description + En tekst der angiver en begrundelse for at input i TransporterHeader ikke kunne accepteres + + + TransporterHeaderErrorText + TransporterHeader fejltekst + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheadererrortext.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheadererrortext.xsd.system.xml new file mode 100644 index 0000000..87964e2 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transporterheadererrortext.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transportersystemidentifier.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transportersystemidentifier.xsd.meta.xml new file mode 100644 index 0000000..e6ba032 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transportersystemidentifier.xsd.meta.xml @@ -0,0 +1,13 @@ + + + TransporterSystemIdentifier + + No description + Navnet på det system, som er afsender af meddelelsen. + + + TransporterSystemIdentifier + SystemId + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transportersystemidentifier.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transportersystemidentifier.xsd.system.xml new file mode 100644 index 0000000..8321b2c --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_transportersystemidentifier.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vansheader.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vansheader.xsd.meta.xml new file mode 100644 index 0000000..9025d93 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vansheader.xsd.meta.xml @@ -0,0 +1,13 @@ + + + VansHeader + + No description + Struktur der indeholder VansSenderAddress og VansRecipientAddress + + + VansHeader + VansHeader + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vansheader.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vansheader.xsd.system.xml new file mode 100644 index 0000000..d60d850 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vansheader.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vansnemkontoenvironmentcode.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vansnemkontoenvironmentcode.xsd.meta.xml new file mode 100644 index 0000000..d6bcf59 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vansnemkontoenvironmentcode.xsd.meta.xml @@ -0,0 +1,13 @@ + + + VansNemkontoEnvironmentCode + + No description + En kode der angiver hvilket miljø meddelelsen skal leveres til + + + VansNemkontoEnviromentCode + Vans miljø kode + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vansnemkontoenvironmentcode.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vansnemkontoenvironmentcode.xsd.system.xml new file mode 100644 index 0000000..884728a --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vansnemkontoenvironmentcode.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vansrecipientaddress.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vansrecipientaddress.xsd.meta.xml new file mode 100644 index 0000000..598cb0f --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vansrecipientaddress.xsd.meta.xml @@ -0,0 +1,13 @@ + + + VansRecipientAddress + + No description + Anvendt i en request sendes VansNemkontoEnvironmentCode fra afsender miljøet. Anvendt i et response sendes EAN131Identifier på PBF. + + + VansRecipientAddress + Vans modtager + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vansrecipientaddress.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vansrecipientaddress.xsd.system.xml new file mode 100644 index 0000000..3a5601b --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vansrecipientaddress.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vanssenderaddress.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vanssenderaddress.xsd.meta.xml new file mode 100644 index 0000000..8625183 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vanssenderaddress.xsd.meta.xml @@ -0,0 +1,13 @@ + + + VansSenderAddress + + No description + Anvendt i en request sendes EAN131Identifier på PBF. Anvendt i et response sendes VansNemkontoEnvironmentCode fra afsender miljøet + + + VansSenderAddress + Vans afsender + + Lars Harritshøj + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vanssenderaddress.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vanssenderaddress.xsd.system.xml new file mode 100644 index 0000000..11ef21a --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/nkspu_vanssenderaddress.xsd.system.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto.ebms/xml/schemas/2006/05/01/EBMS_Common.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto.ebms/xml/schemas/2006/05/01/EBMS_Common.xsd new file mode 100644 index 0000000..1e40c44 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto.ebms/xml/schemas/2006/05/01/EBMS_Common.xsd @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + + + + + + + + + Information om afsender + + + + + + PartyId(1) skal indeholde kortnavn, PartyId(2) skal indeholde EAN-nummer. + + + + + + + + + Information om modtager + + + + + + PartyId(1) skal indeholde kortnavn, PartyId(2) skal indeholde EAN-nummer. + + + + + + + + + Unik reference og timestamp på meddelelsen + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Betalingsmeddelelse C2NKS og NKS2C + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_A_GeneralInformation.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_A_GeneralInformation.xsd new file mode 100644 index 0000000..39af752 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_A_GeneralInformation.xsd @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_B_OriginalGroupReferenceInformation.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_B_OriginalGroupReferenceInformation.xsd new file mode 100644 index 0000000..2dee7bb --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_B_OriginalGroupReferenceInformation.xsd @@ -0,0 +1,39 @@ + + + + + + + + Bundtreference + + + + + + + + + + + + Status på forsendelse + + + + + Fejlkode + + + + + + + Uddybende tekst + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_C_OriginalPaymentInformation.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_C_OriginalPaymentInformation.xsd new file mode 100644 index 0000000..0d1d755 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_C_OriginalPaymentInformation.xsd @@ -0,0 +1,22 @@ + + + + + + + + + + + + + Debiteringstekst + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_Common.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_Common.xsd new file mode 100644 index 0000000..5618327 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_Common.xsd @@ -0,0 +1,502 @@ + + + + + + + + + + + + + Modtagers navn for en udenlansk komplet betaling + + + + + Modtagers adresse for en udenlandsk komplet betaling + + + + + + + + + + + + + + + + + + + + + CPR + + + + + + + + + SE nummer + + + + + + + + + + PI aftalenummer + + + + + + + + + + + + + + Modtager PI Swift kode + + + + + Branch Kode + + + + + + Modtager PI navn + + + + + + + + + + + + + + + + + + + + + + + CVR- el. P-nummer + + + + + Kode for CVR- eller P-nummer + + + + + + + + + + + + + + + Branch Kode + + + + + + + + + + + + + + + + + + + + Modtagers reg.nr. og kontonummer + + + + + IBAN nummer + + + + + + + + + + + + + + + + FI-kreditnummer eller Girokontonummer + + + + + + + + + + Debitors betalingsreference + + + + + + BetalingsID (UPR) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Modtagers adresse + + + + + Modtagers postkode + + + + + Modtagers by + + + + + Modtagers landekode + + + + + + + + + Modtager PI adresse incl. postkode og by + + + + + Modtager PI landekode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Beløb incl. valuta + + + + + + + + + + + Beløb incl. valuta + + + + + Modtagervalutakode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_D_OriginalTransactionReferenceInformationAndStatus.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_D_OriginalTransactionReferenceInformationAndStatus.xsd new file mode 100644 index 0000000..707d240 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_D_OriginalTransactionReferenceInformationAndStatus.xsd @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_E_OriginalTransactionInformation.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_E_OriginalTransactionInformation.xsd new file mode 100644 index 0000000..fee6450 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_E_OriginalTransactionInformation.xsd @@ -0,0 +1,18 @@ + + + + + + + + + Ikke komplet betaling + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_NKSPayment.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_NKSPayment.xsd new file mode 100644 index 0000000..f650f1c --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_NKSPayment.xsd @@ -0,0 +1,252 @@ + + + + + + + + + + + Bundt referencenummer + + + + + Creation Date Time + + + + + NKS aftalenummer + + + + + Kontrolsum + + + + + + + + + + Antal transaktioner + + + + + True if one debitor + + + + + + + + + + + + + + + Udbetalingsdato + + + + + Credit transfer (TRF) + + + + + + + + + + Betalingstype + + + + + + + + + + + + Debiteringstekst + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Gebyrkode + + + + + + + + + + Ikke komplet betaling + + + + + + Ydelsesart korttekst + + + + + + + + + + + Adviseringstekst + + + + + + + + + + + + + + + + + Faktura nummer + + + + + Læselinie + + + + + + + + + Straksadvisering mk + + + + + + + + + + Instruktion til bogføringscentral + + + + + + + + + + + + + + + Afsenders reg.nr. og kontonummer + + + + + IBAN nummer + + + + + + + + + + + + + + + Tekst til modtagers kontoudtog + + + + + + + + + + Kode for instruktion + + + + + Valg af service + + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_Package.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_Package.xsd new file mode 100644 index 0000000..92f38f7 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto.swift/xml/schemas/2006/05/01/SWIFT_Package.xsd @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSPayment.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSPayment.xsd new file mode 100644 index 0000000..6d48677 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSPayment.xsd @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSReceipt0.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSReceipt0.xsd new file mode 100644 index 0000000..70424f1 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSReceipt0.xsd @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSReceipt1.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSReceipt1.xsd new file mode 100644 index 0000000..f3ae69e --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSReceipt1.xsd @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse2.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse2.xsd new file mode 100644 index 0000000..49b8122 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse2.xsd @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse5.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse5.xsd new file mode 100644 index 0000000..d690906 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse5.xsd @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse7.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse7.xsd new file mode 100644 index 0000000..ea2ea66 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse7.xsd @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse8.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse8.xsd new file mode 100644 index 0000000..47c3814 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse8.xsd @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse9.xsd b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse9.xsd new file mode 100644 index 0000000..86311be --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse9.xsd @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/CPR_CompletePostalLabelText.xsd b/src/main/resources/META-INF/Schemas/NemKonto/other/CPR_CompletePostalLabelText.xsd new file mode 100644 index 0000000..e9e7d1d --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/CPR_CompletePostalLabelText.xsd @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/CPR_PersonCivilRegistrationIdentifier.xsd b/src/main/resources/META-INF/Schemas/NemKonto/other/CPR_PersonCivilRegistrationIdentifier.xsd new file mode 100644 index 0000000..bb7f5a0 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/CPR_PersonCivilRegistrationIdentifier.xsd @@ -0,0 +1,42 @@ + + + + + + CivilRegistrationNumber (PNR) + + Description: + Unique identification of a person + + The Civil Registration System contains: + - Data on persons, who after 1968 April 2nd Danish registry of citizens. + As for Greenland the corresponding date is 1972 may 1st. + - Danish citizens living outside Denmark (who must pay duty and ATP) + has also been given a civil registration number. + - Civil registration numbers are also assigned for other administrative purposes. + + + Value space: + The civil registration number consists of two parts. + The first part is the valid birthday in the form DDMMYY. + The following part is a serial number of four digits. + The civil registration number may also hold the value 0000000000. + This value is used where the civil registration number is required but unknown. + + Lifecycle: + The civil registration number is generated and assigned at birth, entry and change of civil registration number of for administrative reasons. + The civil registration number may be assigned via hospitals. + + The civil registration number is not to be deleted. + + Remarks: + 1994 June 11th the civil registration number was changed according to this description. + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/CPR_SecondaryPostalLabel.xsd b/src/main/resources/META-INF/Schemas/NemKonto/other/CPR_SecondaryPostalLabel.xsd new file mode 100644 index 0000000..ddca114 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/CPR_SecondaryPostalLabel.xsd @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/CVR_CVRnumberIdentifier.xsd b/src/main/resources/META-INF/Schemas/NemKonto/other/CVR_CVRnumberIdentifier.xsd new file mode 100644 index 0000000..93ef4a6 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/CVR_CVRnumberIdentifier.xsd @@ -0,0 +1,14 @@ + + + + + Unique and generally usable identifier for all legal units included i CVR. + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/CVR_ProductionUnitIdentifier.xsd b/src/main/resources/META-INF/Schemas/NemKonto/other/CVR_ProductionUnitIdentifier.xsd new file mode 100644 index 0000000..066e75c --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/CVR_ProductionUnitIdentifier.xsd @@ -0,0 +1,13 @@ + + + + + Unique and generally usable identifier for all production units (“workplaces”). + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/DKCC_CountryIdentificationCode.xsd b/src/main/resources/META-INF/Schemas/NemKonto/other/DKCC_CountryIdentificationCode.xsd new file mode 100644 index 0000000..2439f5f --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/DKCC_CountryIdentificationCode.xsd @@ -0,0 +1,59 @@ + + + + + + The country code based on the 4 diffent schemes. + Landeidentifikations kode baseret på de 4 forskellige formater. + + + + + + + + + + + + This is a support type for CountryIdentificationCodeType. The pattern is a choice of 4 different patterns for different schems. ISO 3166 standard, alpha 2: [a-z,A-Z]{2}. Eksample "DK" for Danmark. ISO 3166 standard, alpha 3: [a-z,A-Z]{3}. Eksample "DKN" for Danmark. UN Statistics Divisions country codes: [0-9]{3}. Eksample "208" for Danmark AuthorityCode from the Central Office of Civil Registration: [0-9]{4}. Eksample "5100" for Danmark. + Dette er en støttetype til CountryIdentificationCodeType. Det regulære udtryk er et valg for de 4 forskellige regulære udtryk for de forskellige formater. ISO 3166 standard, alpha 2: [a-z,A-Z]{2}. Eksempel "DK" for Danmark. ISO 3166 standard, alpha 3: [a-z,A-Z]{3}. Eksempel "DKN" for Danmark. UN Statistics Divisions country codes: [0-9]{3}. Eksempel "208" for Danmark AuthorityCode from the Central Office of Civil Registration: [0-9]{4}. Eksempel "5100" for Danmark. + + + + + + + + This is a support type for CountryIdentificationCodeType. + Dette er en støttetype til CountryIdentificationCodeType. + + + + + This scheme follows the ISO 3166 standard, alpha 2. + Dette format følge ISO 3166 standarden, alpha 2. + + + + + This scheme follows the ISO 3166 standard, alpha 3. + Dette format følge ISO 3166 standarden, alpha 3. + + + + + This scheme follows the UN Statistics Divisions country codes. + Dette format følger FNs Statistik Kontor landekoder + + + + + This scheme follows the AuthorityCode from the Central Office of Civil Registration. + Dette format følger MyndighedsKoden fra Det Centrale Personregister + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/DKCC_PostalAddressFifthLineText.xsd b/src/main/resources/META-INF/Schemas/NemKonto/other/DKCC_PostalAddressFifthLineText.xsd new file mode 100644 index 0000000..a3b3398 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/DKCC_PostalAddressFifthLineText.xsd @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/DKCC_PostalAddressFirstLineText.xsd b/src/main/resources/META-INF/Schemas/NemKonto/other/DKCC_PostalAddressFirstLineText.xsd new file mode 100644 index 0000000..76e0e31 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/DKCC_PostalAddressFirstLineText.xsd @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/DKCC_PostalAddressFourthLineText.xsd b/src/main/resources/META-INF/Schemas/NemKonto/other/DKCC_PostalAddressFourthLineText.xsd new file mode 100644 index 0000000..d348a04 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/DKCC_PostalAddressFourthLineText.xsd @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/DKCC_PostalAddressSecondLineText.xsd b/src/main/resources/META-INF/Schemas/NemKonto/other/DKCC_PostalAddressSecondLineText.xsd new file mode 100644 index 0000000..aa90bb2 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/DKCC_PostalAddressSecondLineText.xsd @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/DKCC_PostalAddressSixthLineText.xsd b/src/main/resources/META-INF/Schemas/NemKonto/other/DKCC_PostalAddressSixthLineText.xsd new file mode 100644 index 0000000..be43fb5 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/DKCC_PostalAddressSixthLineText.xsd @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/DKCC_PostalAddressThirdLineText.xsd b/src/main/resources/META-INF/Schemas/NemKonto/other/DKCC_PostalAddressThirdLineText.xsd new file mode 100644 index 0000000..353f9ad --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/DKCC_PostalAddressThirdLineText.xsd @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/EAN_EAN13Identifier.xsd b/src/main/resources/META-INF/Schemas/NemKonto/other/EAN_EAN13Identifier.xsd new file mode 100644 index 0000000..775846e --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/EAN_EAN13Identifier.xsd @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/ITST_BankAccountIdentifier.xsd b/src/main/resources/META-INF/Schemas/NemKonto/other/ITST_BankAccountIdentifier.xsd new file mode 100644 index 0000000..b04fcce --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/ITST_BankAccountIdentifier.xsd @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/ITST_BankAccountStructure.xsd b/src/main/resources/META-INF/Schemas/NemKonto/other/ITST_BankAccountStructure.xsd new file mode 100644 index 0000000..7a63122 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/ITST_BankAccountStructure.xsd @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/ITST_BankBranchIdentifier.xsd b/src/main/resources/META-INF/Schemas/NemKonto/other/ITST_BankBranchIdentifier.xsd new file mode 100644 index 0000000..bd424b9 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/ITST_BankBranchIdentifier.xsd @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/cpr_completepostallabeltext.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/cpr_completepostallabeltext.xsd.meta.xml new file mode 100644 index 0000000..d1ec140 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/cpr_completepostallabeltext.xsd.meta.xml @@ -0,0 +1 @@ +CompletePostalLabelTextLine of text for postal labelsTekstlinie til adresselabelsLabel textLabeltekstCompletePostalLabelText1.0.0http://www.oio.dk/files/Dokumentationsguide_for_adresse.pdf8bd99501-1842-4427-b6f4-0e9d105ff3b3http://www.oio.dk/files/Dokumentationsguide_for_adresse.pdf \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/cpr_completepostallabeltext.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/cpr_completepostallabeltext.xsd.system.xml new file mode 100644 index 0000000..6417efb --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/cpr_completepostallabeltext.xsd.system.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/cpr_personcivilregistrationidentifier.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/cpr_personcivilregistrationidentifier.xsd.meta.xml new file mode 100644 index 0000000..a3013d0 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/cpr_personcivilregistrationidentifier.xsd.meta.xml @@ -0,0 +1 @@ +PersonCivilRegistrationIdentifierCivilRegistrationNumber (PNR) Description: Unique identification of a person The Civil Registration System contains: - Data on persons, who after 1968 April 2nd Danish registry of citizens. As for Greenland the corresponding date is 1972 may 1st. - Danish citizens living outside Denmark (who must pay duty and ATP) has also been given a civil registration number. - Civil registration numbers are also assigned for other administrative purposes. Value space: The civil registration number consists of two parts. The first part is the valid birthday in the form DDMMYY. The following part is a serial number of four digits. The civil registration number may also hold the value 0000000000. This value is used where the civil registration number is required but unknown. Lifecycle: The civil registration number is generated and assigned at birth, entry and change of civil registration number of for administrative reasons. The civil registration number may be assigned via hospitals. The civil registration number is not to be deleted. Remarks: 1994 June 11th the civil registration number was changed according to this description.Personnummer (PNR): Unik identifikation af en person i Det Centrale Personregister indeholder: - Data om personer, der efter 2. april 1968 har været tilmeldt dansk folkeregister – for Grønlands vedkommende dog efter 1. maj 1972. –Personer, der er bosat uden for Danmark, men som i kraft af medlemskab af ATP eller pligt til at svare skat, har fået tildelt et personnummer. Der tildeles desuden personnumre til andet administrativt behov(administrative personnumre). Værdimængde: Personnummeret består af to dele. Første del er en gyldig fødselsdato på formen DDMMÅÅ. Anden del er et serienummer på fire cifre. Personnummeret kan også have værdien 0000000000. Denne værdi bruges hvor personnummeret er påkrævet men ukendt. Livscyklus: Personnummeret genereres og tildeles ved fødsel, optagelse og ændring af personnummer af administrative grunde. Personnummeret kan tildeles via hospitaler. Personnummeret må ikke slettes. Bemærkninger: D. 11. juni 1994 blev personnummeret ændret i henhold til denne beskrivelse.Civil registration numberPersonnummermv@cpr.dkPersonCivilRegistrationIdentifier2.0.0http://rep.oio.dk/cpr.dk/xml/schemas/core/2002/06/28/CPR_CivilRegistrationNumber.xsdCivilRegistrationNumber;CivilRegistration;CivilRegistrationNumberIdentifier;personnummerd530421f-71be-4f8c-8c80-00860e5b55a7668e1d9c-51b1-4d11-8098-c612392f216c \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/cpr_personcivilregistrationidentifier.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/cpr_personcivilregistrationidentifier.xsd.system.xml new file mode 100644 index 0000000..9b84a25 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/cpr_personcivilregistrationidentifier.xsd.system.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/cpr_secondarypostallabel.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/cpr_secondarypostallabel.xsd.meta.xml new file mode 100644 index 0000000..7f5ab36 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/cpr_secondarypostallabel.xsd.meta.xml @@ -0,0 +1 @@ +SecondaryPostalLabelLabel information containing addressee and address information for print on forms, labels and letters. Standardized print of name and address data on forms og mailing items and the like based upon the standards of CPR. Intentionally to contain additional information on adresses associated with a "non-active" person. E.g where such information as contact adress, additional address, election address is needed. New in this version: only the name has been changed.Overførsel af adresseoplysninger på 6 tekstlinjer uden fast indhold eller bindinger, til brug på labels, rudekuverter og lign. Print af adresseoplysninger på formularer og brevforsendelser og lign. baseret på CPRs standarder. Skal anvendes i adressesammenhænge, hvor der ikke er tale om en aktiv person i CPR-kontekst. Dvs. i de sammenhænge, hvor der er behov for at indsætte en af følgende adresser: 1. Kontaktadresse, 2. Supplerende adresse, 3. Valgadresse, 4. Værgeadresse eller 5. Udlandsadresse. Nyt i denne version: kun navnet er ændret.Postal labelAdresselabel (Sekundær adresselabel)SecondaryPostalLabel2.0.0http://rep.oio.dk/cpr.dk/xml/schemas/core/2005/05/19/CPR_SecondaryAddressLabel.xsdhttp://www.oio.dk/files/Dokumentationsguide_for_adresse.pdfrudekuvert;AddressLabel;Sekundær adresselabel;Adresselabel;printaddress8bd99501-1842-4427-b6f4-0e9d105ff3b3http://www.oio.dk/files/Dokumentationsguide_for_adresse.pdf \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/cpr_secondarypostallabel.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/cpr_secondarypostallabel.xsd.system.xml new file mode 100644 index 0000000..44b5fdb --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/cpr_secondarypostallabel.xsd.system.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/cvr_cvrnumberidentifier.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/cvr_cvrnumberidentifier.xsd.meta.xml new file mode 100644 index 0000000..4d61f2c --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/cvr_cvrnumberidentifier.xsd.meta.xml @@ -0,0 +1 @@ +CVRnumberIdentifierUnique and generally usable identifier for all legal units included i CVR.Unik og generelt brugbar identifikator for alle juridiske enheder under CVRCVR numberCVR nummerpnj@itst.dkCVRnumberIdentifier2.0.0http://rep.oio.dk/cvr.dk/xml/schemas/2002/06/28/CVR_CVRNumber.xsdcvr;cvrnumber;cvrnummerd077d9d3-dcc1-4b7e-baf9-0b318d141ec7 \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/cvr_cvrnumberidentifier.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/cvr_cvrnumberidentifier.xsd.system.xml new file mode 100644 index 0000000..1a54254 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/cvr_cvrnumberidentifier.xsd.system.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/cvr_productionunitidentifier.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/cvr_productionunitidentifier.xsd.meta.xml new file mode 100644 index 0000000..da100b4 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/cvr_productionunitidentifier.xsd.meta.xml @@ -0,0 +1 @@ +ProductionUnitIdentifierUnique and generally usable identifier for all production units (“workplaces”).Unik og generelt brugbar identifikator for alle produktionsenheder ("arbejdspladser").Production unitProduktionsenhedpnj@itst.dkProductionUnitIdentifier2.0.0http://rep.oio.dk/cvr.dk/xml/schemas/2002/06/28/CVR_ProductionUnitNumber.xsdproduction unit;produktionsenhed;arbejdsplads;workplaced077d9d3-dcc1-4b7e-baf9-0b318d141ec7 \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/cvr_productionunitidentifier.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/cvr_productionunitidentifier.xsd.system.xml new file mode 100644 index 0000000..92d0cde --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/cvr_productionunitidentifier.xsd.system.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_countryidentificationcode.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_countryidentificationcode.xsd.meta.xml new file mode 100644 index 0000000..bd773cf --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_countryidentificationcode.xsd.meta.xml @@ -0,0 +1 @@ +CountryIdentificationCodeThe country code - 2 or 3 letters or 3 digits - as described in the ISO 3166 standards or 4 digits as described in the AuthorityCode from the Central Office of Civil Registration. E.g.'DK', 'DNK', '208' is the codes for Denmark in the ISO 3166 standards and '5100' is the code for Denmark in the AuthorityCode from the Central Office of Civil Registration.Landeidentifikations kode - 2 eller 3 karaktere eller 3 cifre - som beskrevet i ISO 3166 standarden eller 4 cifre som beskrevet i MyndighedsKode fra Det Centrale Personregister.E.k.s. 'DK', 'DNK', '208' er koderne for Danmark i ISO 3166 standarden og '5100' er koden for Danmark i MyndighedsKode fra Det Centrale Personregister.CountryIdentificationCodehttp://www.itst.dk/arkitektur-og-standarder/Standardisering/datastandardisering/om/kernekomponenter/dokumentationsguides/Dokumentationsguide%20for%20Adresse.pdfCountry8bd99501-1842-4427-b6f4-0e9d105ff3b3http://www.itst.dk/arkitektur-og-standarder/Standardisering/datastandardisering/om/kernekomponenter/dokumentationsguides/Dokumentationsguide%20for%20Adresse.pdf \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_countryidentificationcode.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_countryidentificationcode.xsd.system.xml new file mode 100644 index 0000000..30f4ad6 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_countryidentificationcode.xsd.system.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressfifthlinetext.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressfifthlinetext.xsd.meta.xml new file mode 100644 index 0000000..42911ee --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressfifthlinetext.xsd.meta.xml @@ -0,0 +1,2 @@ +PostalAddressFifthLineTextFree Address line 5. When used in CompletePostalLabel: Postal code and postal district. Line of text which contains Postal code and postal district. In the non-structured schema SecondaryPostalLabel this element can be used freely.Fri Adresselinje 5. Ved brug i CompletePostalLabel: Postnr. og postdistrikt. Tekstlinje som indeholder +postnummer og postdistrikt. I det ikke-strukturerede skema SecondaryPostalLabel kan dette element anvendes frit.Fifth address lineFemte adresse liniePostalAddressFifthLineText1.0.0http://www.oio.dk/files/Dokumentationsguide_for_adresse.pdf8bd99501-1842-4427-b6f4-0e9d105ff3b3http://www.oio.dk/files/Dokumentationsguide_for_adresse.pdf \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressfifthlinetext.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressfifthlinetext.xsd.system.xml new file mode 100644 index 0000000..b042d0e --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressfifthlinetext.xsd.system.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressfirstlinetext.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressfirstlinetext.xsd.meta.xml new file mode 100644 index 0000000..08f49f9 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressfirstlinetext.xsd.meta.xml @@ -0,0 +1,2 @@ +PostalAddressFirstLineTextFree address line 1. When used in CompletePostalLabel: C/O name, Care-of name, i.e. "living at", typically person, family, college, nursing home or the like. In the non-structured schema SecondaryPostalLabel this element can be used freely.Fri adresselinje 1. Ved brug i CompletePostalLabel: C/O-navn, Care-of navn, dvs. "boende hos", typisk person, familie, kollegie, plejehjem eller lign. I det ikke-strukturerede +skema SecondaryPostalLabel kan dette element anvendes frit.First address lineFørste adresseliniePostalAddressFirstLineText1.0.0http://www.oio.dk/files/Dokumentationsguide_for_adresse.pdf8bd99501-1842-4427-b6f4-0e9d105ff3b3http://www.oio.dk/files/Dokumentationsguide_for_adresse.pdf \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressfirstlinetext.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressfirstlinetext.xsd.system.xml new file mode 100644 index 0000000..b2d13df --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressfirstlinetext.xsd.system.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressfourthlinetext.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressfourthlinetext.xsd.meta.xml new file mode 100644 index 0000000..cf66217 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressfourthlinetext.xsd.meta.xml @@ -0,0 +1,4 @@ +PostalAddressFourthLineTextFree Address line 4. When used in CompletePostalLabel: Name of city (place name). In this schema equivalent to DistrictSubdivisionIdentifier, i.e. the city name that is specified as a part of the official address spcification for a certain street or specific parts of a street. In the non-structured schema SecondaryPostalLabel this element can be used freely.Fri Adresselinje 4. Ved brug i CompletePostalLabel: Bynavn (stednavn): +Svarer i dette skema til elementet DistrictSubdivisionIdentifier, dvs. det +bynavn der er fastsat som en del af den officielle adressebetegnelse for en +vej eller for nærmere bestemte dele af en vej. I det ikke-strukturerede skema SecondaryPostalLabel kan dette element anvendes frit.Fourth address lineFørste adresse liniePostalAddressFourthLineText1.0.0http://www.oio.dk/files/Dokumentationsguide_for_adresse.pdf8bd99501-1842-4427-b6f4-0e9d105ff3b3http://www.oio.dk/files/Dokumentationsguide_for_adresse.pdf \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressfourthlinetext.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressfourthlinetext.xsd.system.xml new file mode 100644 index 0000000..9d2c985 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressfourthlinetext.xsd.system.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdresssecondlinetext.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdresssecondlinetext.xsd.meta.xml new file mode 100644 index 0000000..b95a3dc --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdresssecondlinetext.xsd.meta.xml @@ -0,0 +1,3 @@ +PostalAddressSecondLineTextFree Address line 2. When used in CompletePostalLabel: Locality (building name): Equivalent to the element MailDeliverySublocationIdentifier, i.e. name of a farm, estate, building or dwelling, which is used as an additional postal address identifier. In the non-structured schema SecondaryPostalLabel this element can be used freely.Fri Adresselinje 2. Ved brug i CompletePostalLabel: Lokalitet (bygningsnavn): Svarer til elementet MailDeliverySublocationIdentifier, dvs. gårdnavn, navn på ejendom, bygning +eller bolig eller lign., som anvendes som supplerende postadressebetegnelse. I det ikke-strukturerede skema SecondaryPostalLabel kan dette element +anvendes frit.Second address lineAnden adresse liniePostalAddressSecondLineText1.0.0http://www.oio.dk/files/Dokumentationsguide_for_adresse.pdf8bd99501-1842-4427-b6f4-0e9d105ff3b3http://www.oio.dk/files/Dokumentationsguide_for_adresse.pdf \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdresssecondlinetext.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdresssecondlinetext.xsd.system.xml new file mode 100644 index 0000000..b8364bc --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdresssecondlinetext.xsd.system.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdresssixthlinetext.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdresssixthlinetext.xsd.meta.xml new file mode 100644 index 0000000..1e65d65 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdresssixthlinetext.xsd.meta.xml @@ -0,0 +1 @@ +PostalAddressSixthLineTextFree Address line 6. When used in CompletePostalLabel: Free line of text. CPR uses the line for customer service with a key constant. In the non-structured schema SecondaryPostalLabel this element can be used freely.Fri Adresselinje 6. Ved brug i CompletePostalLabel: (Fri tekstlinje) CPR anvender linjen til kundeservicering med en nøglekonstant. I det ikke-strukturerede skema SecondaryPostalLabel kan dette element anvendes frit.Sixth address linePostalAddressSixthLineText1.0.0http://www.oio.dk/files/Dokumentationsguide_for_adresse.pdf8bd99501-1842-4427-b6f4-0e9d105ff3b3http://www.oio.dk/files/Dokumentationsguide_for_adresse.pdf \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdresssixthlinetext.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdresssixthlinetext.xsd.system.xml new file mode 100644 index 0000000..82aa791 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdresssixthlinetext.xsd.system.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressthirdlinetext.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressthirdlinetext.xsd.meta.xml new file mode 100644 index 0000000..05da368 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressthirdlinetext.xsd.meta.xml @@ -0,0 +1,2 @@ +PostalAddressThirdLineTextFree Address line 3. When used in CompletePostalLabel: Standard street address. In this schema equivalent to an aggregation of the elements StreetNameForAddressingName, StreetBuildingIdenfier, FloorIdentifier and SuiteIdentifier, i.e. a line of text which contains street name, house number including letter, floor and door if any. In the non-structured schema SecondaryPostalLabel this element can be used freely.Fri Adresselinje 3. Ved brug i CompletePostalLabel: Standard vejadresse: Svarer i dette skema til en +aggregering af elementerne StreetNameForAddressingName, StreetBuildingIdenfier, FloorIdentifier og SuiteIdentifier, dvs. en tekstlinje som indeholder vejnavn, husnummer inkl. evt. bogstav, etage og dørbetegnelse. I det ikke-strukturerede skema SecondaryPostalLabel kan dette element anvendes frit.Third address lineTredje adresse liniePostalAddressThirdLineText1.0.0http://www.oio.dk/files/Dokumentationsguide_for_adresse.pdf8bd99501-1842-4427-b6f4-0e9d105ff3b3http://www.oio.dk/files/Dokumentationsguide_for_adresse.pdf \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressthirdlinetext.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressthirdlinetext.xsd.system.xml new file mode 100644 index 0000000..33ecdc7 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/dkcc_postaladdressthirdlinetext.xsd.system.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/ean_ean13identifier.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/ean_ean13identifier.xsd.meta.xml new file mode 100644 index 0000000..ec04391 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/ean_ean13identifier.xsd.meta.xml @@ -0,0 +1,3 @@ +EAN13IdentifierIt is expected that schemas will, contrary to general reccomendations of NDR 3.0, reuse the type rather than the element of this schema. The element is included in order to ensure full NDR 3.0 compliance. + +An EAN 13 digits identifications type algorithm is the 1st digit multiplied by 1, + the second digit multiplied by 3, + the third digit multiplied by 1, and so forth until the 12th digit, if the result of all these numbers added together does not end in a 0 then the result is rounded up, for example 56 is rounded up to 60. Then the result is subtracted from the rounded up result to give the 13 digit. in the case of the result ending in zero then the 13 digit must also end in zero.Jan Brown, IT- og TelestyrelsenEAN13Identifier1.0.0 \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/ean_ean13identifier.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/ean_ean13identifier.xsd.system.xml new file mode 100644 index 0000000..d70d34f --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/ean_ean13identifier.xsd.system.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/itst_bankaccountidentifier.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/itst_bankaccountidentifier.xsd.meta.xml new file mode 100644 index 0000000..eeb8e22 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/itst_bankaccountidentifier.xsd.meta.xml @@ -0,0 +1 @@ +BankAccountIdentifierUnique specification of an account in a given commercial bank.Entydig angivelse af en konto i et givent pengeinstitut.Bank Account NumberBankkontonummerpnj@itst.dkBankAccountIdentifier2.0.0http://rep.oio.dk/itst.dk/xml/schemas/2005/06/24/ITST_BankAccountIdentifier.xsdhttp://oio.dk/files/dokumentationsguide_for_dansk_bankkonto.pdfbankkonto;kontonr;kontonummer;bank account;account number;account;bank;http://oio.dk/files/dokumentationsguide_for_dansk_bankkonto.pdf \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/itst_bankaccountidentifier.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/itst_bankaccountidentifier.xsd.system.xml new file mode 100644 index 0000000..f8eaf03 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/itst_bankaccountidentifier.xsd.system.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/itst_bankaccountstructure.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/itst_bankaccountstructure.xsd.meta.xml new file mode 100644 index 0000000..c1e5336 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/itst_bankaccountstructure.xsd.meta.xml @@ -0,0 +1 @@ +BankAccountStructureUnique specification of a Danish bank account.Entydig angivelse af en dansk bankkonto.Bank accountBankkontopnj@itst.dkBankAccountStructure1.0.1http://rep.oio.dk/itst.dk/xml/schemas/2005/06/24/ITST_BankAccountStructure.xsdhttp://www.itst.dk/arkitektur-og-standarder/Standardisering/datastandardisering/om/kernekomponenter/dokumentationsguides/Dokumentationsguide%20for%20Dansk%20Bankkonto.pdfbankkonto;kontonr;kontonummer;bank account;account number;account;bank;http://www.itst.dk/arkitektur-og-standarder/Standardisering/datastandardisering/om/kernekomponenter/dokumentationsguides/Dokumentationsguide%20for%20Dansk%20Bankkonto.pdf \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/itst_bankaccountstructure.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/itst_bankaccountstructure.xsd.system.xml new file mode 100644 index 0000000..5627524 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/itst_bankaccountstructure.xsd.system.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/itst_bankbranchidentifier.xsd.meta.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/itst_bankbranchidentifier.xsd.meta.xml new file mode 100644 index 0000000..f878bbd --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/itst_bankbranchidentifier.xsd.meta.xml @@ -0,0 +1 @@ +BankBranchIdentifierUnique specification of a Danish commercial bank.Entydig angivelse af et dansk pengeinstitut.Branch codeRegistreringsnummerpnj@itst.dkBankBranchIdentifier2.0.0http://rep.oio.dk/itst.dk/xml/schemas/2005/06/24/ITST_BankRegistrationIdentifier.xsdhttp://oio.dk/files/dokumentationsguide_for_dansk_bankkonto.pdfBankBranch;bank branch;bank;bank account;bankkonto;registreringsnummer;bankregistreringsnummer;kontonummer;kontoaccounthttp://oio.dk/files/dokumentationsguide_for_dansk_bankkonto.pdf \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/itst_bankbranchidentifier.xsd.system.xml b/src/main/resources/META-INF/Schemas/NemKonto/other/itst_bankbranchidentifier.xsd.system.xml new file mode 100644 index 0000000..99cb392 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/itst_bankbranchidentifier.xsd.system.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/nemhandel/CVR_full.xsd b/src/main/resources/META-INF/Schemas/NemKonto/other/nemhandel/CVR_full.xsd new file mode 100644 index 0000000..b0a3c6a --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/nemhandel/CVR_full.xsd @@ -0,0 +1,7 @@ + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/nemhandel/DKCC_full.xsd b/src/main/resources/META-INF/Schemas/NemKonto/other/nemhandel/DKCC_full.xsd new file mode 100644 index 0000000..7b1dc3c --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/nemhandel/DKCC_full.xsd @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/src/main/resources/META-INF/Schemas/NemKonto/other/nemhandel/README.txt b/src/main/resources/META-INF/Schemas/NemKonto/other/nemhandel/README.txt new file mode 100644 index 0000000..fb778b7 --- /dev/null +++ b/src/main/resources/META-INF/Schemas/NemKonto/other/nemhandel/README.txt @@ -0,0 +1,9 @@ +The files located in this folder are a workaround to be able to resolve multiple imports with the same namespace. + +For example, it's problematic in NKSPU_ProductionUnitIdentificationStructure.xsd that there are two imports with the same namespace. + + + +NOTE: The files in this folder are created manually by the Nemhandel team. diff --git a/src/main/resources/META-INF/Schemas/catalog.xml b/src/main/resources/META-INF/Schemas/catalog.xml new file mode 100644 index 0000000..d979a3f --- /dev/null +++ b/src/main/resources/META-INF/Schemas/catalog.xml @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/META-INF/Schemas/transfer-delegation/transferDelegation.xsd b/src/main/resources/META-INF/Schemas/transfer-delegation/transferDelegation.xsd deleted file mode 100644 index 9df1f9e..0000000 --- a/src/main/resources/META-INF/Schemas/transfer-delegation/transferDelegation.xsd +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - Sender identification - - - - - - - - - - - - - - Receiver identification - - - - - - - - - - diff --git a/src/main/resources/META-INF/Schematron/NemKonto/NemKonto.xsl b/src/main/resources/META-INF/Schematron/NemKonto/NemKonto.xsl new file mode 100644 index 0000000000000000000000000000000000000000..629059eaeea7a20a3dc538b9e33f41ee5f073bb6 GIT binary patch literal 2810 zcmchZVQUIF{y&h*@b} z44z-YFlDs_1Y_0|?}ad796LsepA$wQl2cwWEu&X-w3vI6b2xO|jgfl$`tP9&n0rIC5*#7UI_6bn^tQQZAMTV7#-JR07s4L%!e z-U|8P=Gh-(lh`kI$~(%{)x6>J)MoAIUd4{8!I}L6ao-Lcv&62MlUKFCXU3uh(>y8@ z_EF3!LRGUOVpkQy=Vk4zIK3^hKQr$KvpsG_ei3=^3VD}RRkalP9b!cFO0}$2DcXJZ zE?RW|S*aN9hpt*Es^=B|CgZAlNA+Enl*#cs+%UGu^vdxaa(wYrxAGf9F~hg*5v;G- zv3;^5^pEU~`}TK>c|~2^F~)dBbw+KF$Ij4sO9*{!y6cAX9L>fpSh_^(TRQ~*K6`W+ z^%x!Ssu$W-=oH5>=tgizV47%vjtP1OC^M=*HLv_o|(WzFgpHT=V;K XYqaY8=J@ZqdAqfCQES=#(rf+&YRS8M literal 0 HcmV?d00001 diff --git a/src/main/resources/db/oxalis-as4-db-changelog.xml b/src/main/resources/db/oxalis-as4-db-changelog.xml index ecd3269..059db08 100644 --- a/src/main/resources/db/oxalis-as4-db-changelog.xml +++ b/src/main/resources/db/oxalis-as4-db-changelog.xml @@ -446,10 +446,7 @@ - - - - + @@ -526,4 +523,21 @@ + + + + + + + + + + + + + + diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml deleted file mode 100644 index fa2ccbe..0000000 --- a/src/main/resources/logback.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - ${catalina.base}/logs/myapp.log - - %d{yyyy-MM-dd'T'HH:mm:ss.SSS'Z'} [%c] [%level] [%thread] [%X{messageUuid}] [%X{transmissionId}] %msg%n - - - - - - - diff --git a/src/main/resources/reference.conf b/src/main/resources/reference.conf index bcc39c2..ec91b21 100644 --- a/src/main/resources/reference.conf +++ b/src/main/resources/reference.conf @@ -54,7 +54,7 @@ document.type = { schemaPath: "META-INF/Schemas/UBL_v2.1/maindoc/UBL-Reminder-2.1.xsd" identifierDiscriminators = [{ xpathExpression: "/sbd:StandardBusinessDocument/root:Reminder/cbc:CustomizationID" - xpathExpectedResult: "OIOUBL-2\\.(01|02|1)" + xpathExpectedResult: "OIOUBL-2\\.1" }] namespaces = [ { namespace: "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader", prefix: "sbd" }, @@ -101,7 +101,9 @@ document.type = { errorLocationXPath: "Xpath" } ] + preloadSchema: true preloadSchematron: true + messageLevelResponseType: "OIOUBL_APPLICATION_RESPONSE" } @@ -112,7 +114,7 @@ document.type = { schemaPath: "META-INF/Schemas/UBL_v2.1/maindoc/UBL-Invoice-2.1.xsd" identifierDiscriminators = [{ xpathExpression: "/sbd:StandardBusinessDocument/root:Invoice/cbc:CustomizationID" - xpathExpectedResult: "OIOUBL-2\\.(01|02|1)" + xpathExpectedResult: "OIOUBL-2\\.1" }] namespaces = [ { namespace: "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader", prefix: "sbd" }, @@ -131,6 +133,7 @@ document.type = { errorLocationXPath: "Xpath" } ] + preloadSchema: true preloadSchematron: true messageLevelResponseType: "OIOUBL_APPLICATION_RESPONSE" } @@ -142,7 +145,7 @@ document.type = { schemaPath: "META-INF/Schemas/UBL_v2.1/maindoc/UBL-CreditNote-2.1.xsd" identifierDiscriminators = [{ xpathExpression: "/sbd:StandardBusinessDocument/root:CreditNote/cbc:CustomizationID" - xpathExpectedResult: "OIOUBL-2\\.(01|02|1)" + xpathExpectedResult: "OIOUBL-2\\.1" }] namespaces = [ { namespace: "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader", prefix: "sbd" }, @@ -161,6 +164,7 @@ document.type = { errorLocationXPath: "Xpath" } ] + preloadSchema: true preloadSchematron: true messageLevelResponseType: "OIOUBL_APPLICATION_RESPONSE" } @@ -172,7 +176,7 @@ document.type = { schemaPath: "META-INF/Schemas/UBL_v2.1/maindoc/UBL-ApplicationResponse-2.1.xsd" identifierDiscriminators = [{ xpathExpression: "/sbd:StandardBusinessDocument/root:ApplicationResponse/cbc:CustomizationID" - xpathExpectedResult: "OIOUBL-2\\.(01|02|1)" + xpathExpectedResult: "OIOUBL-2\\.1" }] responseType: true messageLevelResponse: { @@ -198,6 +202,7 @@ document.type = { errorLocationXPath: "Xpath" } ] + preloadSchema: true preloadSchematron: true messageLevelResponseType: "OIOUBL_APPLICATION_RESPONSE" } @@ -209,7 +214,7 @@ document.type = { schemaPath: "META-INF/Schemas/UBL_v2.1/maindoc/UBL-Statement-2.1.xsd" identifierDiscriminators = [{ xpathExpression: "/sbd:StandardBusinessDocument/root:Statement/cbc:CustomizationID" - xpathExpectedResult: "OIOUBL-2\\.(01|02|1)" + xpathExpectedResult: "OIOUBL-2\\.1" }] namespaces = [ { namespace: "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader", prefix: "sbd" }, @@ -238,7 +243,7 @@ document.type = { schemaPath: "META-INF/Schemas/UBL_v2.1/maindoc/UBL-Order-2.1.xsd" identifierDiscriminators = [{ xpathExpression: "/sbd:StandardBusinessDocument/root:Order/cbc:CustomizationID" - xpathExpectedResult: "OIOUBL-2\\.(01|02|1)" + xpathExpectedResult: "OIOUBL-2\\.1" }] namespaces = [ { namespace: "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader", prefix: "sbd" }, @@ -267,7 +272,7 @@ document.type = { schemaPath: "META-INF/Schemas/UBL_v2.1/maindoc/UBL-OrderResponseSimple-2.1.xsd" identifierDiscriminators = [{ xpathExpression: "/sbd:StandardBusinessDocument/root:OrderResponseSimple/cbc:CustomizationID" - xpathExpectedResult: "OIOUBL-2\\.(01|02|1)" + xpathExpectedResult: "OIOUBL-2\\.1" }] namespaces = [ { namespace: "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader", prefix: "sbd" }, @@ -296,7 +301,7 @@ document.type = { schemaPath: "META-INF/Schemas/UBL_v2.1/maindoc/UBL-OrderResponse-2.1.xsd" identifierDiscriminators = [{ xpathExpression: "/sbd:StandardBusinessDocument/root:OrderResponse/cbc:CustomizationID" - xpathExpectedResult: "OIOUBL-2\\.(01|02|1)" + xpathExpectedResult: "OIOUBL-2\\.1" }] namespaces = [ { namespace: "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader", prefix: "sbd" }, @@ -325,7 +330,7 @@ document.type = { schemaPath: "META-INF/Schemas/UBL_v2.1/maindoc/UBL-OrderCancellation-2.1.xsd" identifierDiscriminators = [{ xpathExpression: "/sbd:StandardBusinessDocument/root:OrderCancellation/cbc:CustomizationID" - xpathExpectedResult: "OIOUBL-2\\.(01|02|1)" + xpathExpectedResult: "OIOUBL-2\\.1" }] namespaces = [ { namespace: "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader", prefix: "sbd" }, @@ -354,7 +359,7 @@ document.type = { schemaPath: "META-INF/Schemas/UBL_v2.1/maindoc/UBL-OrderChange-2.1.xsd" identifierDiscriminators = [{ xpathExpression: "/sbd:StandardBusinessDocument/root:OrderChange/cbc:CustomizationID" - xpathExpectedResult: "OIOUBL-2\\.(01|02|1)" + xpathExpectedResult: "OIOUBL-2\\.1" }] namespaces = [ { namespace: "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader", prefix: "sbd" }, @@ -383,7 +388,7 @@ document.type = { schemaPath: "META-INF/Schemas/UBL_v2.1/maindoc/UBL-Catalogue-2.1.xsd" identifierDiscriminators = [{ xpathExpression: "/sbd:StandardBusinessDocument/root:Catalogue/cbc:CustomizationID" - xpathExpectedResult: "OIOUBL-2\\.(01|02|1)" + xpathExpectedResult: "OIOUBL-2\\.1" }] namespaces = [ { namespace: "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader", prefix: "sbd" }, @@ -412,7 +417,7 @@ document.type = { schemaPath: "META-INF/Schemas/UBL_v2.1/maindoc/UBL-CatalogueDeletion-2.1.xsd" identifierDiscriminators = [{ xpathExpression: "/sbd:StandardBusinessDocument/root:CatalogueDeletion/cbc:CustomizationID" - xpathExpectedResult: "OIOUBL-2\\.(01|02|1)" + xpathExpectedResult: "OIOUBL-2\\.1" }] namespaces = [ { namespace: "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader", prefix: "sbd" }, @@ -441,7 +446,7 @@ document.type = { schemaPath: "META-INF/Schemas/UBL_v2.1/maindoc/UBL-CatalogueRequest-2.1.xsd" identifierDiscriminators = [{ xpathExpression: "/sbd:StandardBusinessDocument/root:CatalogueRequest/cbc:CustomizationID" - xpathExpectedResult: "OIOUBL-2\\.(01|02|1)" + xpathExpectedResult: "OIOUBL-2\\.1" }] namespaces = [ { namespace: "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader", prefix: "sbd" }, @@ -470,7 +475,7 @@ document.type = { schemaPath: "META-INF/Schemas/UBL_v2.1/maindoc/UBL-CatalogueItemSpecificationUpdate-2.1.xsd" identifierDiscriminators = [{ xpathExpression: "/sbd:StandardBusinessDocument/root:CatalogueItemSpecificationUpdate/cbc:CustomizationID" - xpathExpectedResult: "OIOUBL-2\\.(01|02|1)" + xpathExpectedResult: "OIOUBL-2\\.1" }] namespaces = [ { namespace: "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader", prefix: "sbd" }, @@ -499,7 +504,7 @@ document.type = { schemaPath: "META-INF/Schemas/UBL_v2.1/maindoc/UBL-CataloguePricingUpdate-2.1.xsd" identifierDiscriminators = [{ xpathExpression: "/sbd:StandardBusinessDocument/root:CataloguePricingUpdate/cbc:CustomizationID" - xpathExpectedResult: "OIOUBL-2\\.(01|02|1)" + xpathExpectedResult: "OIOUBL-2\\.1" }] namespaces = [ { namespace: "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader", prefix: "sbd" }, @@ -1159,4 +1164,258 @@ document.type = { ] messageLevelResponseType: "PEPPOL_MESSAGE_LEVEL_RESPONSE" } + + # Nemkonto document types + nemkontoNKSPayment = { + friendlyName: "NemKonto - NKSPayment" + payloadRootLocalName: "NKSPayment" + payloadRootNamespace: "http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/" + schemaPath: "META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSPayment.xsd" + identifierDiscriminators = [] + namespaces = [ + { namespace: "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader", prefix: "sbd" }, + { namespace: "http://rep.oio.dk/oes.dk/nemkonto.swift/xml/schemas/2006/05/01/", prefix: "swift" }, + { namespace: "http://rep.oio.dk/oes.dk/nemkonto.ebms/xml/schemas/2006/05/01/", prefix: "ebms" }, + { namespace: "http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/", prefix: "root"} + ] + schematronDocuments = [ + { + schematronDocumentPath: "META-INF/Schematron/NemKonto/NemKonto.xsl" + errorXPath: "/Schematron/Error" + errorMessageXPath: "Description" + errorLocationXPath: "Xpath" + } + ] + preloadSchema: false + preloadSchematron: false + messageLevelResponseType: "OIOUBL_APPLICATION_RESPONSE" + } + + nemkontoNKSReceipt0 = { + friendlyName: "NemKonto - NKSReceipt0" + payloadRootLocalName: "NKSReceipt0" + payloadRootNamespace: "http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/" + schemaPath: "META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSReceipt0.xsd" + identifierDiscriminators = [] + namespaces = [ + { namespace: "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader", prefix: "sbd" }, + { namespace: "http://rep.oio.dk/oes.dk/nemkonto.swift/xml/schemas/2006/05/01/", prefix: "swift" }, + { namespace: "http://rep.oio.dk/oes.dk/nemkonto.ebms/xml/schemas/2006/05/01/", prefix: "ebms" }, + { namespace: "http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/", prefix: "root"} + ] + schematronDocuments = [ + { + schematronDocumentPath: "META-INF/Schematron/NemKonto/NemKonto.xsl" + errorXPath: "/Schematron/Error" + errorMessageXPath: "Description" + errorLocationXPath: "Xpath" + } + ] + preloadSchema: false + preloadSchematron: false + messageLevelResponseType: "OIOUBL_APPLICATION_RESPONSE" + } + + nemkontoNKSReceipt1 = { + friendlyName: "NemKonto - NKSReceipt1" + payloadRootLocalName: "NKSReceipt1" + payloadRootNamespace: "http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/" + schemaPath: "META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSReceipt1.xsd" + identifierDiscriminators = [] + namespaces = [ + { namespace: "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader", prefix: "sbd" }, + { namespace: "http://rep.oio.dk/oes.dk/nemkonto.swift/xml/schemas/2006/05/01/", prefix: "swift" }, + { namespace: "http://rep.oio.dk/oes.dk/nemkonto.ebms/xml/schemas/2006/05/01/", prefix: "ebms" }, + { namespace: "http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/", prefix: "root"} + ] + schematronDocuments = [ + { + schematronDocumentPath: "META-INF/Schematron/NemKonto/NemKonto.xsl" + errorXPath: "/Schematron/Error" + errorMessageXPath: "Description" + errorLocationXPath: "Xpath" + } + ] + preloadSchema: false + preloadSchematron: false + messageLevelResponseType: "OIOUBL_APPLICATION_RESPONSE" + } + + nemkontoNKSResponse2 = { + friendlyName: "NemKonto - NKSResponse2" + payloadRootLocalName: "NKSResponse2" + payloadRootNamespace: "http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/" + schemaPath: "META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse2.xsd" + identifierDiscriminators = [] + namespaces = [ + { namespace: "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader", prefix: "sbd" }, + { namespace: "http://rep.oio.dk/oes.dk/nemkonto.swift/xml/schemas/2006/05/01/", prefix: "swift" }, + { namespace: "http://rep.oio.dk/oes.dk/nemkonto.ebms/xml/schemas/2006/05/01/", prefix: "ebms" }, + { namespace: "http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/", prefix: "root"} + ] + schematronDocuments = [ + { + schematronDocumentPath: "META-INF/Schematron/NemKonto/NemKonto.xsl" + errorXPath: "/Schematron/Error" + errorMessageXPath: "Description" + errorLocationXPath: "Xpath" + } + ] + preloadSchema: false + preloadSchematron: false + messageLevelResponseType: "OIOUBL_APPLICATION_RESPONSE" + } + + nemkontoNKSResponse5 = { + friendlyName: "NemKonto - NKSResponse5" + payloadRootLocalName: "NKSResponse5" + payloadRootNamespace: "http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/" + schemaPath: "META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse5.xsd" + identifierDiscriminators = [] + namespaces = [ + { namespace: "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader", prefix: "sbd" }, + { namespace: "http://rep.oio.dk/oes.dk/nemkonto.swift/xml/schemas/2006/05/01/", prefix: "swift" }, + { namespace: "http://rep.oio.dk/oes.dk/nemkonto.ebms/xml/schemas/2006/05/01/", prefix: "ebms" }, + { namespace: "http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/", prefix: "root"} + ] + schematronDocuments = [ + { + schematronDocumentPath: "META-INF/Schematron/NemKonto/NemKonto.xsl" + errorXPath: "/Schematron/Error" + errorMessageXPath: "Description" + errorLocationXPath: "Xpath" + } + ] + preloadSchema: false + preloadSchematron: false + messageLevelResponseType: "OIOUBL_APPLICATION_RESPONSE" + } + + nemkontoNKSResponse7 = { + friendlyName: "NemKonto - NKSResponse7" + payloadRootLocalName: "NKSResponse7" + payloadRootNamespace: "http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/" + schemaPath: "META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse7.xsd" + identifierDiscriminators = [] + namespaces = [ + { namespace: "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader", prefix: "sbd" }, + { namespace: "http://rep.oio.dk/oes.dk/nemkonto.swift/xml/schemas/2006/05/01/", prefix: "swift" }, + { namespace: "http://rep.oio.dk/oes.dk/nemkonto.ebms/xml/schemas/2006/05/01/", prefix: "ebms" }, + { namespace: "http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/", prefix: "root"} + ] + schematronDocuments = [ + { + schematronDocumentPath: "META-INF/Schematron/NemKonto/NemKonto.xsl" + errorXPath: "/Schematron/Error" + errorMessageXPath: "Description" + errorLocationXPath: "Xpath" + } + ] + preloadSchema: false + preloadSchematron: false + messageLevelResponseType: "OIOUBL_APPLICATION_RESPONSE" + } + + nemkontoNKSResponse8 = { + friendlyName: "NemKonto - NKSResponse8" + payloadRootLocalName: "NKSResponse8" + payloadRootNamespace: "http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/" + schemaPath: "META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse8.xsd" + identifierDiscriminators = [] + namespaces = [ + { namespace: "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader", prefix: "sbd" }, + { namespace: "http://rep.oio.dk/oes.dk/nemkonto.swift/xml/schemas/2006/05/01/", prefix: "swift" }, + { namespace: "http://rep.oio.dk/oes.dk/nemkonto.ebms/xml/schemas/2006/05/01/", prefix: "ebms" }, + { namespace: "http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/", prefix: "root"} + ] + schematronDocuments = [ + { + schematronDocumentPath: "META-INF/Schematron/NemKonto/NemKonto.xsl" + errorXPath: "/Schematron/Error" + errorMessageXPath: "Description" + errorLocationXPath: "Xpath" + } + ] + preloadSchema: false + preloadSchematron: false + messageLevelResponseType: "OIOUBL_APPLICATION_RESPONSE" + } + + nemkontoNKSResponse9 = { + friendlyName: "NemKonto - NKSResponse9" + payloadRootLocalName: "NKSResponse9" + payloadRootNamespace: "http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/" + schemaPath: "META-INF/Schemas/NemKonto/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse9.xsd" + identifierDiscriminators = [] + namespaces = [ + { namespace: "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader", prefix: "sbd" }, + { namespace: "http://rep.oio.dk/oes.dk/nemkonto.swift/xml/schemas/2006/05/01/", prefix: "swift" }, + { namespace: "http://rep.oio.dk/oes.dk/nemkonto.ebms/xml/schemas/2006/05/01/", prefix: "ebms" }, + { namespace: "http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/", prefix: "root"} + ] + schematronDocuments = [ + { + schematronDocumentPath: "META-INF/Schematron/NemKonto/NemKonto.xsl" + errorXPath: "/Schematron/Error" + errorMessageXPath: "Description" + errorLocationXPath: "Xpath" + } + ] + preloadSchema: false + preloadSchematron: false + messageLevelResponseType: "OIOUBL_APPLICATION_RESPONSE" + } + + # NemKonto PU + nemkontoPURequest = { + friendlyName: "NemKonto PU - NemkontoPrivatUdbetalerTransporterRequest" + payloadRootLocalName: "NemkontoPrivatUdbetalerTransporterRequest" + payloadRootNamespace: "http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2007/10/01/" + schemaPath: "META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoPrivatUdbetalerTransporterRequest.xsd" + identifierDiscriminators = [] + namespaces = [ + { namespace: "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader", prefix: "sbd" }, + { namespace: "http://rep.oio.dk/ean/xml/schemas/2005/01/10/", prefix: "ean" }, + { namespace: "http://rep.oio.dk/cvr.dk/xml/schemas/2005/03/22/", prefix: "cvr" }, + { namespace: "http://rep.oio.dk/cpr.dk/xml/schemas/core/2005/03/18/", prefix: "cpr" }, + { namespace: "http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2007/10/01/", prefix: "root"} + ] + schematronDocuments = [ + { + schematronDocumentPath: "META-INF/Schematron/NemKonto/NemKonto.xsl" + errorXPath: "/Schematron/Error" + errorMessageXPath: "Description" + errorLocationXPath: "Xpath" + } + ] + preloadSchema: false + preloadSchematron: false + messageLevelResponseType: "OIOUBL_APPLICATION_RESPONSE" + } + + nemkontoPUResponse = { + friendlyName: "NemKonto PU - NemkontoPrivatUdbetalerTransporterResponse" + payloadRootLocalName: "NemkontoPrivatUdbetalerTransporterResponse" + payloadRootNamespace: "http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2007/10/01/" + schemaPath: "META-INF/Schemas/NemKonto/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoPrivatUdbetalerTransporterResponse.xsd" + identifierDiscriminators = [] + namespaces = [ + { namespace: "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader", prefix: "sbd" }, + { namespace: "http://rep.oio.dk/ean/xml/schemas/2005/01/10/", prefix: "ean" }, + { namespace: "http://rep.oio.dk/cvr.dk/xml/schemas/2005/03/22/", prefix: "cvr" }, + { namespace: "http://rep.oio.dk/cpr.dk/xml/schemas/core/2005/03/18/", prefix: "cpr" }, + { namespace: "http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2007/10/01/", prefix: "root"} + ] + schematronDocuments = [ + { + schematronDocumentPath: "META-INF/Schematron/NemKonto/NemKonto.xsl" + errorXPath: "/Schematron/Error" + errorMessageXPath: "Description" + errorLocationXPath: "Xpath" + } + ] + preloadSchema: false + preloadSchematron: false + messageLevelResponseType: "OIOUBL_APPLICATION_RESPONSE" + } } diff --git a/src/test/java/dk/erst/oxalis/as4/AbstractXmlTest.java b/src/test/java/dk/erst/oxalis/as4/AbstractXmlTest.java index f5e576c..dd8dbb5 100644 --- a/src/test/java/dk/erst/oxalis/as4/AbstractXmlTest.java +++ b/src/test/java/dk/erst/oxalis/as4/AbstractXmlTest.java @@ -1,10 +1,5 @@ package dk.erst.oxalis.as4; -import dk.erst.oxalis.as4.persistence.model.Account; -import dk.erst.oxalis.as4.persistence.model.Message; -import dk.erst.oxalis.as4.persistence.model.MessageContent; -import dk.erst.oxalis.as4.persistence.model.MessageDirection; -import dk.erst.oxalis.as4.persistence.model.MessageStatus; import dk.erst.oxalis.as4.util.DocumentBuilderProvider; import dk.erst.oxalis.as4.util.DocumentUtil; import dk.erst.oxalis.as4.util.XPathUtil; @@ -22,24 +17,16 @@ import org.w3c.dom.Node; import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Base64; import java.util.Date; -import java.util.UUID; public abstract class AbstractXmlTest { @@ -52,6 +39,15 @@ public abstract class AbstractXmlTest { } } + protected Document wrapInSBDAsDocument(byte[] payload) throws Exception { + DocumentBuilder builder = new DocumentBuilderProvider().get(); + + try(ByteArrayInputStream bis = new ByteArrayInputStream(wrapInSbd(payload))) { + Document doc = builder.parse(bis); + return doc; + } + } + protected Document loadAndWrapInSBD(String xmlPath, Header header) throws Exception { DocumentBuilder builder = new DocumentBuilderProvider().get(); @@ -80,15 +76,18 @@ public abstract class AbstractXmlTest { private byte[] wrapInSbd(String xmlPath) throws Exception { try(InputStream in = this.getClass().getClassLoader().getResourceAsStream(xmlPath)) { - SbdhWrapper wrapper = new SbdhWrapper(); - DocumentBuilder builder = new DocumentBuilderProvider().get(); - byte[] xmlFile = IOUtils.toByteArray(in); - Header dummyHeader = getDummyHeader(builder, xmlFile); - try(ByteArrayInputStream wrapperInput = new ByteArrayInputStream(xmlFile)) { - return wrapper.wrap(wrapperInput, dummyHeader); - } + return wrapInSbd(xmlFile); + } + } + + protected byte[] wrapInSbd(byte[] payload) throws Exception { + DocumentBuilder builder = new DocumentBuilderProvider().get(); + Header dummyHeader = getDummyHeader(builder, payload); + SbdhWrapper wrapper = new SbdhWrapper(); + try(ByteArrayInputStream wrapperInput = new ByteArrayInputStream(payload)) { + return wrapper.wrap(wrapperInput, dummyHeader); } } @@ -183,7 +182,11 @@ public abstract class AbstractXmlTest { } } - protected void appendNemhandelSignatureScopeNode(Document doc, byte[] content) throws XPathExpressionException { + protected void appendNemhandelSignatureScopeNode(Document doc, byte[] content, EDeliverySpecification specVersion) throws XPathExpressionException { + appendNemhandelSignatureScopeNode(doc, content, specVersion != null ? specVersion.value() : null); + } + + protected void appendNemhandelSignatureScopeNode(Document doc, byte[] content, String specVersion) throws XPathExpressionException { Element scope = doc.createElementNS(Ns.SBDH, "Scope"); Element type = doc.createElementNS(Ns.SBDH, "Type"); @@ -194,6 +197,12 @@ public abstract class AbstractXmlTest { instanceIdent.setTextContent(Base64.getEncoder().encodeToString(content)); scope.appendChild(instanceIdent); + if(specVersion != null) { + Element identifier = doc.createElementNS(Ns.SBDH, "Identifier"); + identifier.setTextContent(specVersion); + scope.appendChild(identifier); + } + XPathFactory xPathFactory = XPathFactory.newInstance(); XPath xpath = xPathFactory.newXPath(); MapNamespaceContext nsCtx = new MapNamespaceContext(); diff --git a/src/test/java/dk/erst/oxalis/as4/NemhandelModuleTests.java b/src/test/java/dk/erst/oxalis/as4/NemhandelModuleTests.java new file mode 100644 index 0000000..5b136e4 --- /dev/null +++ b/src/test/java/dk/erst/oxalis/as4/NemhandelModuleTests.java @@ -0,0 +1,100 @@ +package dk.erst.oxalis.as4; + +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.util.Modules; +import dk.erst.oxalis.as4.rest.resources.AbstractJaxRsTest; +import network.oxalis.api.evidence.EvidenceFactory; +import network.oxalis.api.outbound.Transmitter; +import network.oxalis.as4.common.As4CommonModule; +import network.oxalis.as4.common.DummyHeaderParser; +import network.oxalis.commons.certvalidator.api.CrlFetcher; +import network.oxalis.commons.config.ConfigModule; +import network.oxalis.commons.filesystem.FileSystemModule; +import network.oxalis.commons.guice.GuiceModuleLoader; +import network.oxalis.commons.header.HeaderModule; +import network.oxalis.commons.header.SbdhHeaderParser; +import network.oxalis.commons.mode.ModeModule; +import network.oxalis.commons.mode.OxalisCrlFetcher; +import network.oxalis.commons.mode.OxalisOcspFetcher; +import network.oxalis.commons.security.KeyStoreConf; +import network.oxalis.commons.settings.SettingsBuilder; +import network.oxalis.commons.tracing.TracingModule; +import network.oxalis.outbound.lookup.LookupModule; +import network.oxalis.outbound.transmission.TransmissionModule; +import network.oxalis.pkix.ocsp.api.OcspFetcher; +import network.oxalis.test.dummy.DummyPkiModule; +import network.oxalis.vefa.peppol.lookup.LookupClient; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.wss4j.common.ext.WSSecurityException; +import org.hamcrest.CoreMatchers; +import org.hamcrest.MatcherAssert; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import org.xml.sax.SAXException; + +import javax.xml.bind.JAXBException; +import javax.xml.stream.XMLStreamException; +import java.io.IOException; + +public class NemhandelModuleTests { + + @Mock + private OxalisCrlFetcher mockCrlFetcher; + + @Mock + private OxalisOcspFetcher mockOcspFetcher; + + @Mock + private CloseableHttpClient mockHttpClient; + + @Mock + private Transmitter transmitterMock; + + @Mock + private LookupClient lookupClientMock; + + @Mock + private EvidenceFactory evidenceFactoryMock; + + private Injector injector; + + + @BeforeMethod + public void setup() throws IOException, XMLStreamException, JAXBException, SAXException { + MockitoAnnotations.initMocks(this); + + injector = Guice.createInjector( + Modules.override( + new GuiceModuleLoader() + ).with( + new DummyPkiModule(), + new AbstractModule() { + @Override + protected void configure() { + SettingsBuilder.with(binder(), KeyStoreConf.class); + bind(CrlFetcher.class).toInstance(mockCrlFetcher); + bind(OcspFetcher.class).toInstance(mockOcspFetcher); + bind(CloseableHttpClient.class).toInstance(mockHttpClient); + bind(Transmitter.class).toInstance(transmitterMock); + bind(LookupClient.class).toInstance(lookupClientMock); + bind(EvidenceFactory.class).toInstance(evidenceFactoryMock); + } + } + ) + ); + } + + @Test + public void testAs4SignatureErrorsCanFindWSS4jResourceBundleAfterNemhandelExtensionsAreLoaded() { + try { + throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "certpath", new Object[] {"No trusted certs found"} + ); + } catch (WSSecurityException e) { + MatcherAssert.assertThat(e.getMessage(), CoreMatchers.equalTo("Error during certificate path validation: No trusted certs found")); + } + } +} diff --git a/src/test/java/dk/erst/oxalis/as4/async/AsyncValidationRunnableImplTest.java b/src/test/java/dk/erst/oxalis/as4/async/AsyncValidationRunnableImplTest.java index 484fbde..aecf1bb 100644 --- a/src/test/java/dk/erst/oxalis/as4/async/AsyncValidationRunnableImplTest.java +++ b/src/test/java/dk/erst/oxalis/as4/async/AsyncValidationRunnableImplTest.java @@ -12,15 +12,12 @@ import dk.erst.oxalis.as4.async.response.MessageLevelResponseCreationException; import dk.erst.oxalis.as4.async.response.MessageLevelResponseFactory; import dk.erst.oxalis.as4.config.documenttype.*; import dk.erst.oxalis.as4.error.ErrorCodes; -import dk.erst.oxalis.as4.handlers.NemhandelPersister; import dk.erst.oxalis.as4.handlers.OutboundException; import dk.erst.oxalis.as4.handlers.SendSbdHandler; import dk.erst.oxalis.as4.handlers.dto.ValidationMessage; import dk.erst.oxalis.as4.handlers.dto.ValidationResult; import dk.erst.oxalis.as4.jdbc.JdbcModule; -import dk.erst.oxalis.as4.mode.Mode; import dk.erst.oxalis.as4.persistence.model.*; -import dk.erst.oxalis.as4.signature.TransferDelegationException; import dk.erst.oxalis.as4.util.*; import dk.erst.oxalis.as4.validation.MessageValidator; import dk.erst.oxalis.as4.validation.ValidationException; @@ -39,14 +36,11 @@ import org.mockito.MockitoAnnotations; import org.testng.Assert; import org.testng.annotations.*; import org.w3c.dom.Document; -import org.xml.sax.SAXException; import javax.persistence.EntityManager; import javax.persistence.EntityTransaction; -import javax.xml.bind.JAXBException; import javax.xml.transform.TransformerException; import javax.xml.xpath.XPath; -import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -67,12 +61,9 @@ public class AsyncValidationRunnableImplTest extends AbstractXmlTest { private Provider entityManagerProviderMock; private MessageValidator messageValidatorMock; - private DocumentSender documentSenderMock; - private NemhandelPersister nemhandelPersisterMock; private ExecutorService executorService; private Key executorServiceBindingKey = Key.get(ExecutorService.class, NemhandelExecutorService.class); private Account account; - private Mode modeMock; private MessageLevelResponseFactory messageLevelResponseFactoryMock; private MessageLevelResponseConverter messageLevelResponseConverterMock; private DocumentTypeConfigResolver documentTypeConfigResolverMock; @@ -82,26 +73,20 @@ public class AsyncValidationRunnableImplTest extends AbstractXmlTest { private DocumentTypeConfig dummyConfig; @BeforeClass - public void setupBeforeClass() throws OutboundException, MessageLevelResponseCreationException, XPathExpressionException, IOException, TransformerException, SAXException, JAXBException, TransferDelegationException, MessageLevelResponseConverterException, DocumentTypeConfigResolverException { + public void setupBeforeClass() { Security.addProvider(new BouncyCastleProvider()); MockitoAnnotations.initMocks(this); messageValidatorMock = Mockito.mock(MessageValidator.class); - documentSenderMock = Mockito.mock(DocumentSender.class); - nemhandelPersisterMock = Mockito.mock(NemhandelPersister.class); messageLevelResponseFactoryMock = Mockito.mock(MessageLevelResponseFactory.class); sendSbdHandlerMock = Mockito.mock(SendSbdHandler.class); messageLevelResponseConverterMock = Mockito.mock(MessageLevelResponseConverter.class); - modeMock = Mockito.mock(Mode.class); documentTypeConfigResolverMock = Mockito.mock(DocumentTypeConfigResolver.class); injector = createInjector(new AbstractModule() { @Override protected void configure() { bind(MessageValidator.class).toInstance(messageValidatorMock); - bind(DocumentSender.class).toInstance(documentSenderMock); - bind(NemhandelPersister.class).toInstance(nemhandelPersisterMock); - bind(Mode.class).toInstance(modeMock); bind(MessageLevelResponseFactory.class).toInstance(messageLevelResponseFactoryMock); bind(SendSbdHandler.class).toInstance(sendSbdHandlerMock); bind(MessageLevelResponseConverter.class).toInstance(messageLevelResponseConverterMock); @@ -134,9 +119,6 @@ public class AsyncValidationRunnableImplTest extends AbstractXmlTest { em.persist(account); }); - doReturn("87654321").when(nemhandelPersisterMock).getReceiverCvrNumber(any()); - doReturn("signed content".getBytes(StandardCharsets.UTF_8)).when(documentSenderMock).signMessage(any(), any(), any(), any()); - when(modeMock.isNemhandelEdeliveryMode()).thenReturn(true); dummyMLR = load("sbd-examples/SBD_MessageLevelResponse.xml"); doReturn(dummyMLR).when(messageLevelResponseFactoryMock).createMessageLevelResponse(any()); dummyOIOUBLApplicationResponse = load("sbd-examples/SBD_OIOUBL_ApplicationResponse.xml"); @@ -166,12 +148,9 @@ public class AsyncValidationRunnableImplTest extends AbstractXmlTest { Mockito.reset(entityManagerProviderMock); Mockito.reset(messageValidatorMock); - Mockito.reset(documentSenderMock); - Mockito.reset(nemhandelPersisterMock); Mockito.reset(messageLevelResponseFactoryMock); Mockito.reset(sendSbdHandlerMock); Mockito.reset(messageLevelResponseConverterMock); - Mockito.reset(modeMock); Mockito.reset(documentTypeConfigResolverMock); } @@ -211,96 +190,6 @@ public class AsyncValidationRunnableImplTest extends AbstractXmlTest { verify(unitOfWorkMock).end(); } - @Test - public void testSignsMessage() throws ValidationException, InterruptedException, ExecutionException, TimeoutException { - when(modeMock.isNemhandelEdeliveryMode()).thenReturn(true); - final String messageUuid = UUID.randomUUID().toString(); - doInTx(em -> { - Message message = new Message( - account, - MessageDirection.IN, - "iso6523-actorid-upis::sender", - "iso6523-actorid-upis::receiver", - "channel", - messageUuid, - "busdox-docid-qns::urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##OIOUBL-2.1::2.1", - new MessageContent("".getBytes(StandardCharsets.UTF_8))); - message.setPeppolProcessId("cenbii-procid-ubl::proces"); - message.setStatus(MessageStatus.CREATED); - em.persist(account); - em.persist(message); - }); - - ValidationResult result = new ValidationResult(); - List empty = new LinkedList<>(); - doReturn(new DataPairs.Tuple2<>(empty, result)).when(messageValidatorMock).validate(argThat(msg -> messageUuid.equals(msg.getMessageUuid())), eq(ValidationType.ASYNCHRONOUS), any()); - - Runnable runnable = new AsyncProcessingRunnable(injector, messageUuid); - Future future = executorService.submit(runnable); - future.get(60, TimeUnit.SECONDS); // wait for async processing to complete - - doInTx(em -> { - em.clear(); // clear the entitymanager to force reload of entities which may have been changed by a separate thread - Message message = em.createQuery("from Message m where m.messageUuid = :messageUuid", Message.class) - .setParameter("messageUuid", messageUuid) - .getSingleResult(); - MatcherAssert.assertThat(message, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(message.getStatus(), CoreMatchers.equalTo(MessageStatus.RECEIVED)); - MatcherAssert.assertThat(message.getMessageContent().getData(), CoreMatchers.equalTo("signed content".getBytes(StandardCharsets.UTF_8))); - - List logs = em.createQuery("from MessageLog l where l.message = :message order by l.id") - .setParameter("message", message) - .getResultList(); - MatcherAssert.assertThat(logs, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(logs.size(), CoreMatchers.equalTo(4)); // Signing init, overdragelses forhold init, overdragelses forhold done, signing done - }); - } - - @Test - public void testDoesNotSignMessage() throws ValidationException, InterruptedException, ExecutionException, TimeoutException { - when(modeMock.isNemhandelEdeliveryMode()).thenReturn(false); - final String messageUuid = UUID.randomUUID().toString(); - doInTx(em -> { - Message message = new Message( - account, - MessageDirection.IN, - "iso6523-actorid-upis::sender", - "iso6523-actorid-upis::receiver", - "channel", - messageUuid, - "busdox-docid-qns::urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##OIOUBL-2.1::2.1", - new MessageContent("".getBytes(StandardCharsets.UTF_8))); - message.setPeppolProcessId("cenbii-procid-ubl::proces"); - message.setStatus(MessageStatus.CREATED); - em.persist(account); - em.persist(message); - }); - - ValidationResult result = new ValidationResult(); - List empty = new LinkedList<>(); - doReturn(new DataPairs.Tuple2<>(empty, result)).when(messageValidatorMock).validate(argThat(msg -> messageUuid.equals(msg.getMessageUuid())), eq(ValidationType.ASYNCHRONOUS), any()); - - Runnable runnable = new AsyncProcessingRunnable(injector, messageUuid); - Future future = executorService.submit(runnable); - future.get(60, TimeUnit.SECONDS); // wait for async processing to complete - - doInTx(em -> { - em.clear(); // clear the entitymanager to force reload of entities which may have been changed by a separate thread - Message message = em.createQuery("from Message m where m.messageUuid = :messageUuid", Message.class) - .setParameter("messageUuid", messageUuid) - .getSingleResult(); - MatcherAssert.assertThat(message, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(message.getStatus(), CoreMatchers.equalTo(MessageStatus.RECEIVED)); - MatcherAssert.assertThat(message.getMessageContent().getData(), CoreMatchers.equalTo("".getBytes(StandardCharsets.UTF_8))); - - List logs = em.createQuery("from MessageLog l where l.message = :message order by l.id") - .setParameter("message", message) - .getResultList(); - MatcherAssert.assertThat(logs, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(logs.size(), CoreMatchers.equalTo(0)); - }); - } - @Test public void testSavesValidationResultForMessage() throws ValidationException, ExecutionException, InterruptedException, TimeoutException { final String messageUuid = UUID.randomUUID().toString(); @@ -452,7 +341,7 @@ public class AsyncValidationRunnableImplTest extends AbstractXmlTest { } @Test - private void testHandlesUnknownMessageIds() throws ValidationException { + public void testHandlesUnknownMessageIds() throws ValidationException { final String messageUuid = UUID.randomUUID().toString(); doReturn(null).when(messageValidatorMock).validate(eq(null), eq(ValidationType.ASYNCHRONOUS), any()); @@ -467,7 +356,7 @@ public class AsyncValidationRunnableImplTest extends AbstractXmlTest { } @Test - private void testDoesNotProcessOutboundMessages() throws ValidationException, ExecutionException, InterruptedException, TimeoutException { + public void testDoesNotProcessOutboundMessages() throws ValidationException, ExecutionException, InterruptedException, TimeoutException { final String messageUuid = UUID.randomUUID().toString(); doInTx(em -> { Message message = new Message( @@ -502,7 +391,7 @@ public class AsyncValidationRunnableImplTest extends AbstractXmlTest { } @Test - public void testGeneratesMessageLevelResponseForValidationErrors() throws ValidationException, ExecutionException, InterruptedException, TimeoutException, MessageLevelResponseCreationException, JAXBException, TransferDelegationException, IOException, TransformerException, XPathExpressionException, SAXException, OutboundException, MessageLevelResponseConverterException { + public void testGeneratesMessageLevelResponseForValidationErrors() throws ValidationException, ExecutionException, InterruptedException, TimeoutException, MessageLevelResponseCreationException, IOException, TransformerException, OutboundException, MessageLevelResponseConverterException { final String messageUuid = UUID.randomUUID().toString(); doInTx(em -> { Message message = new Message( @@ -610,7 +499,7 @@ public class AsyncValidationRunnableImplTest extends AbstractXmlTest { } @Test - public void testConvertsMessageLevelResponseToOIOUBLApplicationResponseIfOriginalDocumentIsOIOUBL() throws Exception, TransferDelegationException { + public void testConvertsMessageLevelResponseToOIOUBLApplicationResponseIfOriginalDocumentIsOIOUBL() throws Exception { final String messageUuid = UUID.randomUUID().toString(); final byte[] originalDocumentContent = loadAndWrapInSBDAsByteArray("ubl-examples/OIOUBL_Invoice_v2p2.xml"); doInTx(em -> { @@ -1055,6 +944,101 @@ public class AsyncValidationRunnableImplTest extends AbstractXmlTest { }); } + @Test + public void testSavesStacktraceToMessageIfProcessingThrowsException() throws ValidationException, ExecutionException, InterruptedException, TimeoutException { + final String messageUuid = UUID.randomUUID().toString(); + doInTx(em -> { + Message message = new Message( + account, + MessageDirection.IN, + "iso6523-actorid-upis::sender", + "iso6523-actorid-upis::receiver", + "channel", + messageUuid, + "busdox-docid-qns::urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##OIOUBL-2.1::2.1", + new MessageContent("".getBytes(StandardCharsets.UTF_8))); + message.setPeppolProcessId("cenbii-procid-ubl::proces"); + message.setStatus(MessageStatus.CREATED); + em.persist(account); + em.persist(message); + }); + + doThrow(ValidationException.class).when(messageValidatorMock).validate(argThat(msg -> messageUuid.equals(msg.getMessageUuid())), eq(ValidationType.ASYNCHRONOUS), any()); + + Runnable runnable = new AsyncProcessingRunnable(injector, messageUuid); + Future future = executorService.submit(runnable); + future.get(60, TimeUnit.SECONDS); // wait for async processing to complete + + doInTx(em -> { + em.clear(); // clear the entitymanager to force reload of entities which may have been changed by a separate thread + Message message = em.createQuery("from Message m where m.messageUuid = :messageUuid", Message.class) + .setParameter("messageUuid", messageUuid) + .getSingleResult(); + MatcherAssert.assertThat(message, CoreMatchers.notNullValue()); + MatcherAssert.assertThat(message.getStacktrace(), CoreMatchers.notNullValue()); + MatcherAssert.assertThat(message.getStacktrace(), CoreMatchers.containsString(ValidationException.class.getName())); + }); + } + + @Test + public void testSetsReferenceToOriginalDocument() throws ValidationException, ExecutionException, InterruptedException, TimeoutException { + final byte[] xml = loadAsByteArray("sbd-examples/SBD_OIOUBL_ApplicationResponse_BusinessReject.xml"); + final String messageUuid = UUID.randomUUID().toString(); + final Message[] messages = new Message[1]; + doInTx(em -> { + messages[0] = new Message( + account, + MessageDirection.IN, + "iso6523-actorid-upis::sender", + "iso6523-actorid-upis::receiver", + "channel", + messageUuid, + "busdox-docid-qns::urn:oasis:names:specification:ubl:schema:xsd:ApplicationResponse-2::ApplicationResponse##OIOUBL-2.1::2.1", + new MessageContent(xml)); + messages[0].setPeppolProcessId("urn:fdc:peppol.eu:poacc:bis:mlr:3"); + messages[0].setStatus(MessageStatus.CREATED); + em.persist(account); + em.persist(messages[0]); + + Message original = new Message( + account, + MessageDirection.OUT, + "iso6523-actorid-upis::sender", + "iso6523-actorid-upis::receiver", + "channel", + "44abcdc4-0258-4c99-a229-601ca8e79759", // original UUID from sbd-examples/SBD_MessageLevelResponse.xml + "busdox-docid-qns::urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##OIOUBL-2.1::2.1", + new MessageContent("".getBytes(StandardCharsets.UTF_8)) + ); + original.setStatus(MessageStatus.SENT); + em.persist(original); + }); + + doReturn(emptyValidationResult()).when(messageValidatorMock).validate(argThat(msg -> messageUuid.equals(msg.getMessageUuid())), eq(ValidationType.ASYNCHRONOUS), any()); + + // make sure conversion is needed + dummyConfig.setResponseType(true); + dummyConfig.setMessageLevelResponse(oioublARConfig()); + dummyConfig.setNamespaces(mlrNamespaces()); + + Runnable runnable = new AsyncProcessingRunnable(injector, messageUuid); + Future future = executorService.submit(runnable); + future.get(60, TimeUnit.SECONDS); + + doInTx(em -> { + em.clear(); + Message mlr = em.createQuery("from Message where messageUuid = :messageUuid", Message.class) + .setParameter("messageUuid", messageUuid) + .getResultStream() + .findFirst() + .orElse(null); + + MatcherAssert.assertThat(mlr, CoreMatchers.notNullValue()); + MatcherAssert.assertThat(mlr.getOriginalMessage(), CoreMatchers.notNullValue()); + MatcherAssert.assertThat(mlr.getOriginalMessage().getMessageUuid(), CoreMatchers.equalTo("44abcdc4-0258-4c99-a229-601ca8e79759")); + }); + } + private void doInTx(Consumer action){ Provider provider = injector.getProvider(EntityManager.class); TxUtil.doInTx(provider, action); diff --git a/src/test/java/dk/erst/oxalis/as4/async/response/MessageLevelResponseFactoryTest.java b/src/test/java/dk/erst/oxalis/as4/async/response/MessageLevelResponseFactoryTest.java index 0e8ecf2..82d4efe 100644 --- a/src/test/java/dk/erst/oxalis/as4/async/response/MessageLevelResponseFactoryTest.java +++ b/src/test/java/dk/erst/oxalis/as4/async/response/MessageLevelResponseFactoryTest.java @@ -113,13 +113,13 @@ public class MessageLevelResponseFactoryTest extends AbstractXmlTest { Document doc = loadAndWrapInSBD("ubl-examples/OIOUBL_Invoice_v2p2.xml"); byte[] xml = toByteArray(doc); Message message = createTestInvoiceMessage(xml, MessageStatus.ERROR); - MessageLog error1 = NemhandelLog.createMessageLog(message, new ValidationMessage(ValidationMessageType.ERROR, "err-1", "error message 1", "line-1")); + MessageLog error1 = NemhandelLog.createMessageLog(message, new ValidationMessage(ValidationMessageType.ERROR, "err-1", "err-1: error message 1", "line-1")); message.addDocumentLog(error1); - MessageLog error2 = NemhandelLog.createMessageLog(message, new ValidationMessage(ValidationMessageType.ERROR, "err-2", "error message 2", "line-2")); + MessageLog error2 = NemhandelLog.createMessageLog(message, new ValidationMessage(ValidationMessageType.ERROR, "err-2", "err-2: error message 2", "line-2")); message.addDocumentLog(error2); - MessageLog warning1 = NemhandelLog.createMessageLog(message, new ValidationMessage(ValidationMessageType.WARNING, "warn-1", "warning message 1", "line-3")); + MessageLog warning1 = NemhandelLog.createMessageLog(message, new ValidationMessage(ValidationMessageType.WARNING, "warn-1", "warn-1: warning message 1", "line-3")); message.addDocumentLog(warning1); - MessageLog warning2 = NemhandelLog.createMessageLog(message, new ValidationMessage(ValidationMessageType.WARNING, "warn-2", "warning message 2", "line-4")); + MessageLog warning2 = NemhandelLog.createMessageLog(message, new ValidationMessage(ValidationMessageType.WARNING, "warn-2", "warn-2: warning message 2", "line-4")); message.addDocumentLog(warning2); Document mlr = factory.createMessageLevelResponse(message); @@ -240,6 +240,7 @@ public class MessageLevelResponseFactoryTest extends AbstractXmlTest { MatcherAssert.assertThat(mlr, hasXpath(lineResponseXpath + "/cac:LineReference/cbc:LineID", CoreMatchers.equalTo(resultLog.getValidationLineReference()))); MatcherAssert.assertThat(mlr, hasXpath(lineResponseXpath + "/cac:Response/cbc:ResponseCode", CoreMatchers.equalTo(MessageLevelResponseFactory.RESPONSE_CODE_REJECTED))); MatcherAssert.assertThat(mlr, hasXpath(lineResponseXpath + "/cac:Response/cac:Status/cbc:StatusReasonCode", CoreMatchers.equalTo(ValidationMessageType.WARNING.equals(resultLog.getType()) ? MessageLevelResponseFactory.RESPONSE_STATUS_BUSINESS_VIOLATION_WARNING : MessageLevelResponseFactory.RESPONSE_STATUS_BUSINESS_VIOLATION_FATAL))); + MatcherAssert.assertThat(mlr, hasXpath(lineResponseXpath + "/cac:Response/cbc:Description", CoreMatchers.equalTo(resultLog.getDescription()))); } private Matcher hasXpath(String xpath, Matcher valueMatcher) { diff --git a/src/test/java/dk/erst/oxalis/as4/config/documenttype/DocumentTypeModuleTest.java b/src/test/java/dk/erst/oxalis/as4/config/documenttype/DocumentTypeModuleTest.java index 4acb52b..be25d6d 100644 --- a/src/test/java/dk/erst/oxalis/as4/config/documenttype/DocumentTypeModuleTest.java +++ b/src/test/java/dk/erst/oxalis/as4/config/documenttype/DocumentTypeModuleTest.java @@ -23,7 +23,9 @@ import java.util.Set; public class DocumentTypeModuleTest { private static final String UBL_PATH_PREFIX = "META-INF/Schemas/UBL_v2.1/maindoc"; + private static final String NEMKONTO_XSD_PATH_PREFIX = "META-INF/Schemas/NemKonto"; private static final String SCHEMATRON_UBL_PATH_PREFIX = "META-INF/Schematron/OIOUBL_v2.1"; + private static final String SCHEMATRON_NEMKONTO_PATH_PREFIX = "META-INF/Schematron/NemKonto"; private static final String SCHEMATRON_PEPPOL_PATH_PREFIX = "META-INF/Schematron/PEPPOL"; private static final String SCHEMATRON_ERROR_XPATH = "/Schematron/Error"; private static final String SCHEMATRON_ERROR_DESCRIPTION_XPATH = "Description"; @@ -69,7 +71,7 @@ public class DocumentTypeModuleTest { config.setPayloadRootLocalName("Reminder"); config.setPayloadRootNamespace("urn:oasis:names:specification:ubl:schema:xsd:Reminder-2"); config.setSchemaPath(UBL_PATH_PREFIX + "/UBL-Reminder-2.1.xsd"); - config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:Reminder/cbc:CustomizationID", "OIOUBL-2\\.(01|02|1)"))); + config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:Reminder/cbc:CustomizationID", "OIOUBL-2\\.1"))); config.setNamespaces(Arrays.asList( standardBusinessDocumentNamespace(), commonAggregateComponentsNamespace(), @@ -119,7 +121,7 @@ public class DocumentTypeModuleTest { config.setPayloadRootLocalName("Invoice"); config.setPayloadRootNamespace("urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"); config.setSchemaPath(UBL_PATH_PREFIX + "/UBL-Invoice-2.1.xsd"); - config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:Invoice/cbc:CustomizationID", "OIOUBL-2\\.(01|02|1)"))); + config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:Invoice/cbc:CustomizationID", "OIOUBL-2\\.1"))); config.setNamespaces(Arrays.asList( standardBusinessDocumentNamespace(), commonAggregateComponentsNamespace(), @@ -144,7 +146,7 @@ public class DocumentTypeModuleTest { config.setPayloadRootLocalName("CreditNote"); config.setPayloadRootNamespace("urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2"); config.setSchemaPath(UBL_PATH_PREFIX + "/UBL-CreditNote-2.1.xsd"); - config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:CreditNote/cbc:CustomizationID", "OIOUBL-2\\.(01|02|1)"))); + config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:CreditNote/cbc:CustomizationID", "OIOUBL-2\\.1"))); config.setNamespaces(Arrays.asList( standardBusinessDocumentNamespace(), commonAggregateComponentsNamespace(), @@ -169,7 +171,7 @@ public class DocumentTypeModuleTest { config.setPayloadRootLocalName("ApplicationResponse"); config.setPayloadRootNamespace("urn:oasis:names:specification:ubl:schema:xsd:ApplicationResponse-2"); config.setSchemaPath(UBL_PATH_PREFIX + "/UBL-ApplicationResponse-2.1.xsd"); - config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:ApplicationResponse/cbc:CustomizationID", "OIOUBL-2\\.(01|02|1)"))); + config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:ApplicationResponse/cbc:CustomizationID", "OIOUBL-2\\.1"))); config.setNamespaces(Arrays.asList( standardBusinessDocumentNamespace(), commonAggregateComponentsNamespace(), @@ -200,7 +202,7 @@ public class DocumentTypeModuleTest { config.setPayloadRootLocalName("Statement"); config.setPayloadRootNamespace("urn:oasis:names:specification:ubl:schema:xsd:Statement-2"); config.setSchemaPath(UBL_PATH_PREFIX + "/UBL-Statement-2.1.xsd"); - config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:Statement/cbc:CustomizationID", "OIOUBL-2\\.(01|02|1)"))); + config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:Statement/cbc:CustomizationID", "OIOUBL-2\\.1"))); config.setNamespaces(Arrays.asList( standardBusinessDocumentNamespace(), commonAggregateComponentsNamespace(), @@ -225,7 +227,7 @@ public class DocumentTypeModuleTest { config.setPayloadRootLocalName("Order"); config.setPayloadRootNamespace("urn:oasis:names:specification:ubl:schema:xsd:Order-2"); config.setSchemaPath(UBL_PATH_PREFIX + "/UBL-Order-2.1.xsd"); - config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:Order/cbc:CustomizationID", "OIOUBL-2\\.(01|02|1)"))); + config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:Order/cbc:CustomizationID", "OIOUBL-2\\.1"))); config.setNamespaces(Arrays.asList( standardBusinessDocumentNamespace(), commonAggregateComponentsNamespace(), @@ -250,7 +252,7 @@ public class DocumentTypeModuleTest { config.setPayloadRootLocalName("OrderResponseSimple"); config.setPayloadRootNamespace("urn:oasis:names:specification:ubl:schema:xsd:OrderResponseSimple-2"); config.setSchemaPath(UBL_PATH_PREFIX + "/UBL-OrderResponseSimple-2.1.xsd"); - config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:OrderResponseSimple/cbc:CustomizationID", "OIOUBL-2\\.(01|02|1)"))); + config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:OrderResponseSimple/cbc:CustomizationID", "OIOUBL-2\\.1"))); config.setNamespaces(Arrays.asList( standardBusinessDocumentNamespace(), commonAggregateComponentsNamespace(), @@ -275,7 +277,7 @@ public class DocumentTypeModuleTest { config.setPayloadRootLocalName("OrderResponse"); config.setPayloadRootNamespace("urn:oasis:names:specification:ubl:schema:xsd:OrderResponse-2"); config.setSchemaPath(UBL_PATH_PREFIX + "/UBL-OrderResponse-2.1.xsd"); - config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:OrderResponse/cbc:CustomizationID", "OIOUBL-2\\.(01|02|1)"))); + config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:OrderResponse/cbc:CustomizationID", "OIOUBL-2\\.1"))); config.setNamespaces(Arrays.asList( standardBusinessDocumentNamespace(), commonAggregateComponentsNamespace(), @@ -300,7 +302,7 @@ public class DocumentTypeModuleTest { config.setPayloadRootLocalName("OrderCancellation"); config.setPayloadRootNamespace("urn:oasis:names:specification:ubl:schema:xsd:OrderCancellation-2"); config.setSchemaPath(UBL_PATH_PREFIX + "/UBL-OrderCancellation-2.1.xsd"); - config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:OrderCancellation/cbc:CustomizationID", "OIOUBL-2\\.(01|02|1)"))); + config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:OrderCancellation/cbc:CustomizationID", "OIOUBL-2\\.1"))); config.setNamespaces(Arrays.asList( standardBusinessDocumentNamespace(), commonAggregateComponentsNamespace(), @@ -325,7 +327,7 @@ public class DocumentTypeModuleTest { config.setPayloadRootLocalName("OrderChange"); config.setPayloadRootNamespace("urn:oasis:names:specification:ubl:schema:xsd:OrderChange-2"); config.setSchemaPath(UBL_PATH_PREFIX + "/UBL-OrderChange-2.1.xsd"); - config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:OrderChange/cbc:CustomizationID", "OIOUBL-2\\.(01|02|1)"))); + config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:OrderChange/cbc:CustomizationID", "OIOUBL-2\\.1"))); config.setNamespaces(Arrays.asList( standardBusinessDocumentNamespace(), commonAggregateComponentsNamespace(), @@ -350,7 +352,7 @@ public class DocumentTypeModuleTest { config.setPayloadRootLocalName("Catalogue"); config.setPayloadRootNamespace("urn:oasis:names:specification:ubl:schema:xsd:Catalogue-2"); config.setSchemaPath(UBL_PATH_PREFIX + "/UBL-Catalogue-2.1.xsd"); - config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:Catalogue/cbc:CustomizationID", "OIOUBL-2\\.(01|02|1)"))); + config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:Catalogue/cbc:CustomizationID", "OIOUBL-2\\.1"))); config.setNamespaces(Arrays.asList( standardBusinessDocumentNamespace(), commonAggregateComponentsNamespace(), @@ -375,7 +377,7 @@ public class DocumentTypeModuleTest { config.setPayloadRootLocalName("CatalogueDeletion"); config.setPayloadRootNamespace("urn:oasis:names:specification:ubl:schema:xsd:CatalogueDeletion-2"); config.setSchemaPath(UBL_PATH_PREFIX + "/UBL-CatalogueDeletion-2.1.xsd"); - config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:CatalogueDeletion/cbc:CustomizationID", "OIOUBL-2\\.(01|02|1)"))); + config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:CatalogueDeletion/cbc:CustomizationID", "OIOUBL-2\\.1"))); config.setNamespaces(Arrays.asList( standardBusinessDocumentNamespace(), commonAggregateComponentsNamespace(), @@ -400,7 +402,7 @@ public class DocumentTypeModuleTest { config.setPayloadRootLocalName("CatalogueRequest"); config.setPayloadRootNamespace("urn:oasis:names:specification:ubl:schema:xsd:CatalogueRequest-2"); config.setSchemaPath(UBL_PATH_PREFIX + "/UBL-CatalogueRequest-2.1.xsd"); - config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:CatalogueRequest/cbc:CustomizationID", "OIOUBL-2\\.(01|02|1)"))); + config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:CatalogueRequest/cbc:CustomizationID", "OIOUBL-2\\.1"))); config.setNamespaces(Arrays.asList( standardBusinessDocumentNamespace(), commonAggregateComponentsNamespace(), @@ -425,7 +427,7 @@ public class DocumentTypeModuleTest { config.setPayloadRootLocalName("CatalogueItemSpecificationUpdate"); config.setPayloadRootNamespace("urn:oasis:names:specification:ubl:schema:xsd:CatalogueItemSpecificationUpdate-2"); config.setSchemaPath(UBL_PATH_PREFIX + "/UBL-CatalogueItemSpecificationUpdate-2.1.xsd"); - config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:CatalogueItemSpecificationUpdate/cbc:CustomizationID", "OIOUBL-2\\.(01|02|1)"))); + config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:CatalogueItemSpecificationUpdate/cbc:CustomizationID", "OIOUBL-2\\.1"))); config.setNamespaces(Arrays.asList( standardBusinessDocumentNamespace(), commonAggregateComponentsNamespace(), @@ -450,7 +452,7 @@ public class DocumentTypeModuleTest { config.setPayloadRootLocalName("CataloguePricingUpdate"); config.setPayloadRootNamespace("urn:oasis:names:specification:ubl:schema:xsd:CataloguePricingUpdate-2"); config.setSchemaPath(UBL_PATH_PREFIX + "/UBL-CataloguePricingUpdate-2.1.xsd"); - config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:CataloguePricingUpdate/cbc:CustomizationID", "OIOUBL-2\\.(01|02|1)"))); + config.setIdentifierDiscriminators(Collections.singletonList(new XpathDiscriminatorConfig("/sbd:StandardBusinessDocument/root:CataloguePricingUpdate/cbc:CustomizationID", "OIOUBL-2\\.1"))); config.setNamespaces(Arrays.asList( standardBusinessDocumentNamespace(), commonAggregateComponentsNamespace(), @@ -978,6 +980,7 @@ public class DocumentTypeModuleTest { new SchematronValidationConfig(SCHEMATRON_UBL_PATH_PREFIX + "/OIOUBL_Invoice_Schematron.xsl", SCHEMATRON_ERROR_XPATH, SCHEMATRON_ERROR_DESCRIPTION_XPATH, SCHEMATRON_ERROR_LOCATION_XPATH) )); additionalDocumentType.setPreloadSchematron(false); + additionalDocumentType.setPreloadSchema(false); additionalDocumentType.setMessageLevelResponseType(MessageLevelResponseType.OIOUBL_APPLICATION_RESPONSE); testDocumentTypeContainsCorrectValues(additionalDocumentType); @@ -998,6 +1001,248 @@ public class DocumentTypeModuleTest { }); } + @Test + public void testProvidesNemkontoNKSPaymentDocumentTypeConfig() { + DocumentTypeConfig config = new DocumentTypeConfig(); + config.setFriendlyName("NemKonto - NKSPayment"); + config.setPayloadRootLocalName("NKSPayment"); + config.setPayloadRootNamespace("http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/"); + config.setSchemaPath(NEMKONTO_XSD_PATH_PREFIX + "/nemkonto/xml/schemas/2006/05/01/NKS_NKSPayment.xsd"); + config.setIdentifierDiscriminators(Collections.emptyList()); + config.setNamespaces(Arrays.asList( + standardBusinessDocumentNamespace(), + nemKontoSwiftNamespace(), + nemKontoEbmsNamespace(), + new PrefixedNamespace("http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/", "root") + )); + config.setSchematronDocuments(Collections.singletonList( + new SchematronValidationConfig(SCHEMATRON_NEMKONTO_PATH_PREFIX + "/NemKonto.xsl", SCHEMATRON_ERROR_XPATH, SCHEMATRON_ERROR_DESCRIPTION_XPATH, SCHEMATRON_ERROR_LOCATION_XPATH) + )); + config.setMessageLevelResponseType(MessageLevelResponseType.OIOUBL_APPLICATION_RESPONSE); + + testDocumentTypeContainsCorrectValues(config); + } + + @Test + public void testProvidesNemkontoNKSReceipt0DocumentTypeConfig() { + DocumentTypeConfig config = new DocumentTypeConfig(); + config.setFriendlyName("NemKonto - NKSReceipt0"); + config.setPayloadRootLocalName("NKSReceipt0"); + config.setPayloadRootNamespace("http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/"); + config.setSchemaPath(NEMKONTO_XSD_PATH_PREFIX + "/nemkonto/xml/schemas/2006/05/01/NKS_NKSReceipt0.xsd"); + config.setIdentifierDiscriminators(Collections.emptyList()); + config.setNamespaces(Arrays.asList( + standardBusinessDocumentNamespace(), + nemKontoSwiftNamespace(), + nemKontoEbmsNamespace(), + new PrefixedNamespace("http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/", "root") + )); + config.setSchematronDocuments(Collections.singletonList( + new SchematronValidationConfig(SCHEMATRON_NEMKONTO_PATH_PREFIX + "/NemKonto.xsl", SCHEMATRON_ERROR_XPATH, SCHEMATRON_ERROR_DESCRIPTION_XPATH, SCHEMATRON_ERROR_LOCATION_XPATH) + )); + config.setMessageLevelResponseType(MessageLevelResponseType.OIOUBL_APPLICATION_RESPONSE); + + testDocumentTypeContainsCorrectValues(config); + } + + @Test + public void testProvidesNemkontoNKSReceipt1DocumentTypeConfig() { + DocumentTypeConfig config = new DocumentTypeConfig(); + config.setFriendlyName("NemKonto - NKSReceipt1"); + config.setPayloadRootLocalName("NKSReceipt1"); + config.setPayloadRootNamespace("http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/"); + config.setSchemaPath(NEMKONTO_XSD_PATH_PREFIX + "/nemkonto/xml/schemas/2006/05/01/NKS_NKSReceipt1.xsd"); + config.setIdentifierDiscriminators(Collections.emptyList()); + config.setNamespaces(Arrays.asList( + standardBusinessDocumentNamespace(), + nemKontoSwiftNamespace(), + nemKontoEbmsNamespace(), + new PrefixedNamespace("http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/", "root") + )); + config.setSchematronDocuments(Collections.singletonList( + new SchematronValidationConfig(SCHEMATRON_NEMKONTO_PATH_PREFIX + "/NemKonto.xsl", SCHEMATRON_ERROR_XPATH, SCHEMATRON_ERROR_DESCRIPTION_XPATH, SCHEMATRON_ERROR_LOCATION_XPATH) + )); + config.setMessageLevelResponseType(MessageLevelResponseType.OIOUBL_APPLICATION_RESPONSE); + + testDocumentTypeContainsCorrectValues(config); + } + + @Test + public void testProvidesNemkontoNKSResponse2DocumentTypeConfig() { + DocumentTypeConfig config = new DocumentTypeConfig(); + config.setFriendlyName("NemKonto - NKSResponse2"); + config.setPayloadRootLocalName("NKSResponse2"); + config.setPayloadRootNamespace("http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/"); + config.setSchemaPath(NEMKONTO_XSD_PATH_PREFIX + "/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse2.xsd"); + config.setIdentifierDiscriminators(Collections.emptyList()); + config.setNamespaces(Arrays.asList( + standardBusinessDocumentNamespace(), + nemKontoSwiftNamespace(), + nemKontoEbmsNamespace(), + new PrefixedNamespace("http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/", "root") + )); + config.setSchematronDocuments(Collections.singletonList( + new SchematronValidationConfig(SCHEMATRON_NEMKONTO_PATH_PREFIX + "/NemKonto.xsl", SCHEMATRON_ERROR_XPATH, SCHEMATRON_ERROR_DESCRIPTION_XPATH, SCHEMATRON_ERROR_LOCATION_XPATH) + )); + config.setMessageLevelResponseType(MessageLevelResponseType.OIOUBL_APPLICATION_RESPONSE); + + testDocumentTypeContainsCorrectValues(config); + } + + @Test + public void testProvidesNemkontoNKSResponse5DocumentTypeConfig() { + DocumentTypeConfig config = new DocumentTypeConfig(); + config.setFriendlyName("NemKonto - NKSResponse5"); + config.setPayloadRootLocalName("NKSResponse5"); + config.setPayloadRootNamespace("http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/"); + config.setSchemaPath(NEMKONTO_XSD_PATH_PREFIX + "/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse5.xsd"); + config.setIdentifierDiscriminators(Collections.emptyList()); + config.setNamespaces(Arrays.asList( + standardBusinessDocumentNamespace(), + nemKontoSwiftNamespace(), + nemKontoEbmsNamespace(), + new PrefixedNamespace("http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/", "root") + )); + config.setSchematronDocuments(Collections.singletonList( + new SchematronValidationConfig(SCHEMATRON_NEMKONTO_PATH_PREFIX + "/NemKonto.xsl", SCHEMATRON_ERROR_XPATH, SCHEMATRON_ERROR_DESCRIPTION_XPATH, SCHEMATRON_ERROR_LOCATION_XPATH) + )); + config.setMessageLevelResponseType(MessageLevelResponseType.OIOUBL_APPLICATION_RESPONSE); + + testDocumentTypeContainsCorrectValues(config); + } + + @Test + public void testProvidesNemkontoNKSResponse7DocumentTypeConfig() { + DocumentTypeConfig config = new DocumentTypeConfig(); + config.setFriendlyName("NemKonto - NKSResponse7"); + config.setPayloadRootLocalName("NKSResponse7"); + config.setPayloadRootNamespace("http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/"); + config.setSchemaPath(NEMKONTO_XSD_PATH_PREFIX + "/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse7.xsd"); + config.setIdentifierDiscriminators(Collections.emptyList()); + config.setNamespaces(Arrays.asList( + standardBusinessDocumentNamespace(), + nemKontoSwiftNamespace(), + nemKontoEbmsNamespace(), + new PrefixedNamespace("http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/", "root") + )); + config.setSchematronDocuments(Collections.singletonList( + new SchematronValidationConfig(SCHEMATRON_NEMKONTO_PATH_PREFIX + "/NemKonto.xsl", SCHEMATRON_ERROR_XPATH, SCHEMATRON_ERROR_DESCRIPTION_XPATH, SCHEMATRON_ERROR_LOCATION_XPATH) + )); + config.setMessageLevelResponseType(MessageLevelResponseType.OIOUBL_APPLICATION_RESPONSE); + + testDocumentTypeContainsCorrectValues(config); + } + + @Test + public void testProvidesNemkontoNKSResponse8DocumentTypeConfig() { + DocumentTypeConfig config = new DocumentTypeConfig(); + config.setFriendlyName("NemKonto - NKSResponse8"); + config.setPayloadRootLocalName("NKSResponse8"); + config.setPayloadRootNamespace("http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/"); + config.setSchemaPath(NEMKONTO_XSD_PATH_PREFIX + "/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse8.xsd"); + config.setIdentifierDiscriminators(Collections.emptyList()); + config.setNamespaces(Arrays.asList( + standardBusinessDocumentNamespace(), + nemKontoSwiftNamespace(), + nemKontoEbmsNamespace(), + new PrefixedNamespace("http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/", "root") + )); + config.setSchematronDocuments(Collections.singletonList( + new SchematronValidationConfig(SCHEMATRON_NEMKONTO_PATH_PREFIX + "/NemKonto.xsl", SCHEMATRON_ERROR_XPATH, SCHEMATRON_ERROR_DESCRIPTION_XPATH, SCHEMATRON_ERROR_LOCATION_XPATH) + )); + config.setMessageLevelResponseType(MessageLevelResponseType.OIOUBL_APPLICATION_RESPONSE); + + testDocumentTypeContainsCorrectValues(config); + } + + @Test + public void testProvidesNemkontoNKSResponse9DocumentTypeConfig() { + DocumentTypeConfig config = new DocumentTypeConfig(); + config.setFriendlyName("NemKonto - NKSResponse9"); + config.setPayloadRootLocalName("NKSResponse9"); + config.setPayloadRootNamespace("http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/"); + config.setSchemaPath(NEMKONTO_XSD_PATH_PREFIX + "/nemkonto/xml/schemas/2006/05/01/NKS_NKSResponse9.xsd"); + config.setIdentifierDiscriminators(Collections.emptyList()); + config.setNamespaces(Arrays.asList( + standardBusinessDocumentNamespace(), + nemKontoSwiftNamespace(), + nemKontoEbmsNamespace(), + new PrefixedNamespace("http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2006/05/01/", "root") + )); + config.setSchematronDocuments(Collections.singletonList( + new SchematronValidationConfig(SCHEMATRON_NEMKONTO_PATH_PREFIX + "/NemKonto.xsl", SCHEMATRON_ERROR_XPATH, SCHEMATRON_ERROR_DESCRIPTION_XPATH, SCHEMATRON_ERROR_LOCATION_XPATH) + )); + config.setMessageLevelResponseType(MessageLevelResponseType.OIOUBL_APPLICATION_RESPONSE); + + testDocumentTypeContainsCorrectValues(config); + } + + @Test + public void testProvidesNemkontPrivatUdbetalerTransporterRequestDocumentTypeConfig() { + DocumentTypeConfig config = new DocumentTypeConfig(); + config.setFriendlyName("NemKonto PU - NemkontoPrivatUdbetalerTransporterRequest"); + config.setPayloadRootLocalName("NemkontoPrivatUdbetalerTransporterRequest"); + config.setPayloadRootNamespace("http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2007/10/01/"); + config.setSchemaPath(NEMKONTO_XSD_PATH_PREFIX + "/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoPrivatUdbetalerTransporterRequest.xsd"); + config.setIdentifierDiscriminators(Collections.emptyList()); + config.setNamespaces(Arrays.asList( + standardBusinessDocumentNamespace(), + nemkontoEANNamespace(), + nemkontoCVRNamespace(), + nemkontoCPRNamespace(), + new PrefixedNamespace("http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2007/10/01/", "root") + )); + config.setSchematronDocuments(Collections.singletonList( + new SchematronValidationConfig(SCHEMATRON_NEMKONTO_PATH_PREFIX + "/NemKonto.xsl", SCHEMATRON_ERROR_XPATH, SCHEMATRON_ERROR_DESCRIPTION_XPATH, SCHEMATRON_ERROR_LOCATION_XPATH) + )); + config.setMessageLevelResponseType(MessageLevelResponseType.OIOUBL_APPLICATION_RESPONSE); + + testDocumentTypeContainsCorrectValues(config); + } + + private static PrefixedNamespace nemkontoCPRNamespace() { + return new PrefixedNamespace("http://rep.oio.dk/cpr.dk/xml/schemas/core/2005/03/18/", "cpr"); + } + + private static PrefixedNamespace nemkontoCVRNamespace() { + return new PrefixedNamespace("http://rep.oio.dk/cvr.dk/xml/schemas/2005/03/22/", "cvr"); + } + + private static PrefixedNamespace nemkontoEANNamespace() { + return new PrefixedNamespace("http://rep.oio.dk/ean/xml/schemas/2005/01/10/", "ean"); + } + + @Test + public void testProvidesNemkontPrivatUdbetalerTransporterResponseDocumentTypeConfig() { + DocumentTypeConfig config = new DocumentTypeConfig(); + config.setFriendlyName("NemKonto PU - NemkontoPrivatUdbetalerTransporterResponse"); + config.setPayloadRootLocalName("NemkontoPrivatUdbetalerTransporterResponse"); + config.setPayloadRootNamespace("http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2007/10/01/"); + config.setSchemaPath(NEMKONTO_XSD_PATH_PREFIX + "/nemkonto-pu/xml/schemas/2007/10/01/NKSPU_NemkontoPrivatUdbetalerTransporterResponse.xsd"); + config.setIdentifierDiscriminators(Collections.emptyList()); + config.setNamespaces(Arrays.asList( + standardBusinessDocumentNamespace(), + nemkontoEANNamespace(), + nemkontoCVRNamespace(), + nemkontoCPRNamespace(), + new PrefixedNamespace("http://rep.oio.dk/oes.dk/nemkonto/xml/schemas/2007/10/01/", "root") + )); + config.setSchematronDocuments(Collections.singletonList( + new SchematronValidationConfig(SCHEMATRON_NEMKONTO_PATH_PREFIX + "/NemKonto.xsl", SCHEMATRON_ERROR_XPATH, SCHEMATRON_ERROR_DESCRIPTION_XPATH, SCHEMATRON_ERROR_LOCATION_XPATH) + )); + config.setMessageLevelResponseType(MessageLevelResponseType.OIOUBL_APPLICATION_RESPONSE); + + testDocumentTypeContainsCorrectValues(config); + } + + private static PrefixedNamespace nemKontoEbmsNamespace() { + return new PrefixedNamespace("http://rep.oio.dk/oes.dk/nemkonto.ebms/xml/schemas/2006/05/01/", "ebms"); + } + + private static PrefixedNamespace nemKontoSwiftNamespace() { + return new PrefixedNamespace("http://rep.oio.dk/oes.dk/nemkonto.swift/xml/schemas/2006/05/01/", "swift"); + } + private static PrefixedNamespace unqualifiedDatatypesNamespace() { return new PrefixedNamespace("urn:un:unece:uncefact:data:specification:UnqualifiedDataTypesSchemaModule:2", "udt"); } diff --git a/src/test/java/dk/erst/oxalis/as4/error/ErrorCodesTest.java b/src/test/java/dk/erst/oxalis/as4/error/ErrorCodesTest.java index 2f4aab2..013b2b7 100644 --- a/src/test/java/dk/erst/oxalis/as4/error/ErrorCodesTest.java +++ b/src/test/java/dk/erst/oxalis/as4/error/ErrorCodesTest.java @@ -19,19 +19,19 @@ public class ErrorCodesTest { @Test public void testFormatsErrorCodeCorrectly() { String error = ErrorCodes.SENDER_EDELIVERY_SIGNATURE_MISSING.getErrorCode(); - MatcherAssert.assertThat(error, CoreMatchers.equalTo("E-APS21001")); + MatcherAssert.assertThat(error, CoreMatchers.equalTo("E-APS21020")); } @Test public void testGetErrorCodeByCodeCorrectly() { ErrorCodes error = ErrorCodes.getByCode(ErrorCodes.SENDER_EDELIVERY_SIGNATURE_MISSING.getErrorCode()); - MatcherAssert.assertThat(error.getErrorCode(), CoreMatchers.equalTo("E-APS21001")); + MatcherAssert.assertThat(error.getErrorCode(), CoreMatchers.equalTo("E-APS21020")); } @Test public void testCanFormatErrorMessageWithParameters() { - String error = ErrorCodes.SENDER_EDELIVERY_SIGNATURE_INVALID.formatErrorMessage(1, "foo"); - MatcherAssert.assertThat(error, CoreMatchers.equalTo("E-APS21002: Invalid Nemhandel e-Delivery document signature at level 1: foo")); + String error = ErrorCodes.SENDER_EDELIVERY_SIGNATURE_INVALID.formatErrorMessage("foo"); + MatcherAssert.assertThat(error, CoreMatchers.equalTo("E-APS21021: Invalid Nemhandel e-Delivery document signature: foo")); } @Test diff --git a/src/test/java/dk/erst/oxalis/as4/handlers/GetOutboxMessageHandlerTest.java b/src/test/java/dk/erst/oxalis/as4/handlers/GetOutboxMessageHandlerTest.java index 879ba2c..02b4a39 100644 --- a/src/test/java/dk/erst/oxalis/as4/handlers/GetOutboxMessageHandlerTest.java +++ b/src/test/java/dk/erst/oxalis/as4/handlers/GetOutboxMessageHandlerTest.java @@ -5,6 +5,7 @@ import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.persist.PersistService; import com.google.inject.util.Modules; +import dk.erst.oxalis.as4.handlers.dto.MessageAndResponses; import dk.erst.oxalis.as4.handlers.dto.MessageModel; import dk.erst.oxalis.as4.jdbc.JdbcModule; import dk.erst.oxalis.as4.persistence.model.Account; @@ -17,7 +18,10 @@ import dk.erst.oxalis.as4.util.TxUtil; import dk.erst.oxalis.as4.util.UtilityModule; import network.oxalis.commons.config.ConfigModule; import network.oxalis.commons.filesystem.FileSystemModule; +import org.apache.commons.io.IOUtils; import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.hamcrest.CoreMatchers; +import org.hamcrest.MatcherAssert; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; @@ -26,9 +30,13 @@ import org.testng.annotations.Test; import javax.persistence.EntityManager; import javax.ws.rs.NotFoundException; import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.security.Security; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; +import java.util.UUID; import java.util.function.Consumer; import static org.testng.Assert.assertEquals; @@ -39,6 +47,7 @@ public class GetOutboxMessageHandlerTest { private Injector injector; private Account account; + private Account account2; @BeforeClass public void setupBeforeClass() { @@ -62,8 +71,10 @@ public class GetOutboxMessageHandlerTest { persistService.start(); account = new Account("testuser", new PasswordHasher().hashPassword("testpassword"), "DK"); + account2 = new Account("testuser-2", new PasswordHasher().hashPassword("testpassword"), "DK"); doInTx(em -> { em.persist(account); + em.persist(account2); }); } @@ -138,6 +149,87 @@ public class GetOutboxMessageHandlerTest { handler.getDocumentStatus("abc", account); } + @Test + public void testGetMessageAndAllResponsesOnlyReturnsResponsesTiedToUsersAccount() { + + + String originalUuid = UUID.randomUUID().toString(); + String response1Uuid = UUID.randomUUID().toString(); + String response2Uuid = UUID.randomUUID().toString(); + + doInTx(em -> { + Message m = createDummyMessage(account, originalUuid, "test"); + em.persist(m); + Message resp1 = createDummyResponseMessage(account, response1Uuid, "response 1", m); + em.persist(resp1); + + // response with different account + Message resp2 = createDummyResponseMessage(account2, response2Uuid, "response 2", m); + em.persist(resp2); + }); + + GetOutboxMessagesHandler handler = injector.getInstance(GetOutboxMessagesHandler.class); + MessageAndResponses responses = handler.getMessageAndAllResponses(account, originalUuid); + MatcherAssert.assertThat(responses, CoreMatchers.notNullValue()); + MatcherAssert.assertThat(responses.getOriginalDocument(), CoreMatchers.notNullValue()); + MatcherAssert.assertThat(responses.getOriginalDocument().getMessageUuid(), CoreMatchers.equalTo(originalUuid)); + + MatcherAssert.assertThat(responses.getResponses(), CoreMatchers.notNullValue()); + MatcherAssert.assertThat(responses.getResponses().size(), CoreMatchers.equalTo(1)); + MatcherAssert.assertThat(responses.getResponses().iterator().next().getMessageUuid(), CoreMatchers.equalTo(response1Uuid)); + } + + @Test + void testGetMessageContentReturnsTheExpectedContent() throws IOException { + GetOutboxMessagesHandler handler = injector.getInstance(GetOutboxMessagesHandler.class); + + Message dummyMessage1 = createDummyMessage(account, "abc1", "test"); + dummyMessage1.setReceived(LocalDateTime.now()); + dummyMessage1.setStatus(MessageStatus.RECEIVED); + + doInTx(em -> { + em.persist(dummyMessage1); + }); + try(InputStream is = handler.getMessageContent("abc1", account)) { + MatcherAssert.assertThat(IOUtils.toString(is, StandardCharsets.UTF_8), CoreMatchers.equalTo("test")); + } + } + + @Test(expectedExceptions = NotFoundException.class) + void testGetMessageContentThrowsNotFoundExceptionForUnknownMessage() throws IOException { + GetOutboxMessagesHandler handler = injector.getInstance(GetOutboxMessagesHandler.class); + handler.getMessageContent(UUID.randomUUID().toString(), account); + } + + @Test(expectedExceptions = NotFoundException.class) + void testGetMessageContentThrowsNotFoundExceptionForMessageTiedToAnotherAccount() throws IOException { + Message dummyMessage1 = createDummyMessage(account2, UUID.randomUUID().toString(), "test"); + dummyMessage1.setReceived(LocalDateTime.now()); + dummyMessage1.setStatus(MessageStatus.RECEIVED); + + doInTx(em -> { + em.persist(dummyMessage1); + }); + + GetOutboxMessagesHandler handler = injector.getInstance(GetOutboxMessagesHandler.class); + handler.getMessageContent(dummyMessage1.getMessageUuid(), account); + } + + @Test(expectedExceptions = NotFoundException.class) + void testGetMessageContentDoesNotLookInInbox() throws IOException { + Message dummyMessage1 = createDummyMessage(account, UUID.randomUUID().toString(), "test"); + dummyMessage1.setReceived(LocalDateTime.now()); + dummyMessage1.setStatus(MessageStatus.RECEIVED); + dummyMessage1.setDirection(MessageDirection.IN); // inbox + + doInTx(em -> { + em.persist(dummyMessage1); + }); + + GetOutboxMessagesHandler handler = injector.getInstance(GetOutboxMessagesHandler.class); + handler.getMessageContent(dummyMessage1.getMessageUuid(), account); + } + private Message createDummyMessage(Account account, String uuid, String content) { Message message = new Message(); message.setAccount(account); @@ -151,4 +243,19 @@ public class GetOutboxMessageHandlerTest { message.setMessageContent(new MessageContent(content.getBytes())); return message; } + + private Message createDummyResponseMessage(Account account, String uuid, String content, Message originalMessage) { + Message message = new Message(); + message.setAccount(account); + message.setSender("sender"); + message.setReceiver("receiver"); + message.setMessageUuid(uuid); + message.setDirection(MessageDirection.IN); + message.setDocumentTypeId("urn:oasis:names:specification:ubl:schema:xsd:ApplicationResponse-2::ApplicationResponse##OIOUBL-2.1::2.1"); + message.setChannel("as4"); + message.setStatus(MessageStatus.RECEIVED); + message.setMessageContent(new MessageContent(content.getBytes())); + message.setOriginalMessage(originalMessage); + return message; + } } diff --git a/src/test/java/dk/erst/oxalis/as4/handlers/NemhandelPersisterHandlerTest.java b/src/test/java/dk/erst/oxalis/as4/handlers/NemhandelPersisterHandlerTest.java index 3ecda0a..7858011 100644 --- a/src/test/java/dk/erst/oxalis/as4/handlers/NemhandelPersisterHandlerTest.java +++ b/src/test/java/dk/erst/oxalis/as4/handlers/NemhandelPersisterHandlerTest.java @@ -165,16 +165,51 @@ public class NemhandelPersisterHandlerTest { assertThat(message.getAccount(), equalTo(account)); assertThat(message.getSender(), equalTo("iso6523-actorid-upis::urn:oasis:names:tc:ebcore:partyid-type:unregistered:c1")); assertThat(message.getReceiver(), equalTo("iso6523-actorid-upis::urn:oasis:names:tc:ebcore:partyid-type:unregistered:c4")); - assertThat(message.getDocumentTypeId(), equalTo("submitMessage")); + assertThat(message.getDocumentTypeId(), equalTo("busdox-docid-qns::submitMessage")); assertThat(message.getDirection(), equalTo(MessageDirection.IN)); - assertThat(message.getPeppolProcessId(), equalTo("http://ec.europa.eu/edelivery/services/connectivity-service")); + assertThat(message.getPeppolProcessId(), equalTo("e-delivery::http://ec.europa.eu/edelivery/services/connectivity-service")); assertThat(message.getStatus(), equalTo(MessageStatus.PROCESSING)); assertThat(message.getMessageContent().getDataAsString(), equalTo(xml)); + assertThat(message.getTransmissionId(), equalTo("1")); + }); + } + + @Test + public void testSavesDocumentToDbWithIdentifiers() throws IOException { + String xml = load("/cef-sbd-with-identifiers.xml"); + persistDocument(xml); + + doInTx(em -> { + Message message = em.createQuery("from Message", Message.class) + .getSingleResult(); + + //should use e-delivery prefix from sbdh + assertThat(message.getDocumentTypeId(), equalTo("e-delivery::submitMessage")); + //should use e-delivery prefix from sbdh + assertThat(message.getPeppolProcessId(), equalTo("e-delivery::http://ec.europa.eu/edelivery/services/connectivity-service")); + + }); + } + + @Test + public void testSavesDocumentToDbWithDefaultIdentifiers() throws IOException { + String xml = load("/cef-sbd-without-identifiers.xml"); + persistDocument(xml); + + doInTx(em -> { + Message message = em.createQuery("from Message", Message.class) + .getSingleResult(); + + //should use default prefix + assertThat(message.getDocumentTypeId(), equalTo("busdox-docid-qns::submitMessage")); + //should use default prefix + assertThat(message.getPeppolProcessId(), equalTo("cenbii-procid-ubl::http://ec.europa.eu/edelivery/services/connectivity-service")); + }); } private void persistDocument(String xml) throws IOException { - accountReceiver = new AccountReceiver(account, "urn:oasis:names:tc:ebcore:partyid-type:unregistered:C4", "87654321"); + accountReceiver = new AccountReceiver(account, "urn:oasis:names:tc:ebcore:partyid-type:unregistered:C4"); // add some accountreceiver entry: doInTx(em -> { em.persist(accountReceiver); @@ -196,7 +231,7 @@ public class NemhandelPersisterHandlerTest { expectedExceptionsMessageRegExp = "E-APR24123: Receiver urn:oasis:names:tc:ebcore:partyid-type:unregistered:C4 is not recognized by the receiving endpoint. The document was rejected by the receiving endpoint" ) public void testExceptionOnNonMatchingAccountReceiver() throws IOException { - accountReceiver = new AccountReceiver(account, "NonMatchingParticipantID", "00000000"); + accountReceiver = new AccountReceiver(account, "NonMatchingParticipantID"); // add some accountreceiver entry: doInTx(em -> { em.persist(accountReceiver); @@ -219,7 +254,7 @@ public class NemhandelPersisterHandlerTest { expectedExceptionsMessageRegExp = "Xml is not a StandardBusinessDocument, or is missing" ) public void testThrowsOnMissingSender() throws IOException { - accountReceiver = new AccountReceiver(account, "urn:oasis:names:tc:ebcore:partyid-type:unregistered:C4", "00000000"); + accountReceiver = new AccountReceiver(account, "urn:oasis:names:tc:ebcore:partyid-type:unregistered:C4"); // add some accountreceiver entry: doInTx(em -> { em.persist(accountReceiver); @@ -244,7 +279,7 @@ public class NemhandelPersisterHandlerTest { expectedExceptionsMessageRegExp = "Xml is not a StandardBusinessDocument, or is missing" ) public void testThrowsOnMissingReceiver() throws IOException { - accountReceiver = new AccountReceiver(account, "urn:oasis:names:tc:ebcore:partyid-type:unregistered:C4", "00000000"); + accountReceiver = new AccountReceiver(account, "urn:oasis:names:tc:ebcore:partyid-type:unregistered:C4"); // add some accountreceiver entry: doInTx(em -> { em.persist(accountReceiver); @@ -268,7 +303,7 @@ public class NemhandelPersisterHandlerTest { expectedExceptionsMessageRegExp = "Xml is not a StandardBusinessDocument, or a with DOCUMENTID is missing" ) public void testThrowsOnMissingDocumentTypeId() throws IOException { - accountReceiver = new AccountReceiver(account, "urn:oasis:names:tc:ebcore:partyid-type:unregistered:C4", "00000000"); + accountReceiver = new AccountReceiver(account, "urn:oasis:names:tc:ebcore:partyid-type:unregistered:C4"); // add some accountreceiver entry: doInTx(em -> { em.persist(accountReceiver); @@ -464,7 +499,7 @@ public class NemhandelPersisterHandlerTest { Assert.fail("Should throw"); }catch(IOException e) { assertThat(e.getMessage(), Matchers.startsWith("Error while performing validation")); - assertThat(e.getMessage(), Matchers.containsString("E-APS21001:")); + assertThat(e.getMessage(), Matchers.containsString("E-APS21020:")); verify(executorServiceMock, never()).execute(any(AsyncProcessingRunnable.class)); } @@ -488,7 +523,7 @@ public class NemhandelPersisterHandlerTest { @Test public void testCreatesMLRMessageLogsForMLRWhereNoKnownOriginalMessage() throws IOException { String xml = load("/sbd-examples/SBD_MessageLevelResponse.xml"); - accountReceiver = new AccountReceiver(account, "0184:12345678", "87654321"); + accountReceiver = new AccountReceiver(account, "0184:12345678"); doInTx(em -> { em.persist(accountReceiver); }); @@ -518,7 +553,7 @@ public class NemhandelPersisterHandlerTest { mesg.setReceiver("0184:12345678"); mesg.setSender("0184:12345678"); - accountReceiver = new AccountReceiver(account, "0184:12345678", "87654321"); + accountReceiver = new AccountReceiver(account, "0184:12345678"); String xml = load("/sbd-examples/SBD_MessageLevelResponse.xml"); doInTx(em -> { @@ -537,6 +572,7 @@ public class NemhandelPersisterHandlerTest { MatcherAssert.assertThat(logs.stream().anyMatch(m -> ErrorCodes.C2_RECEIVING_OF_MLR_DONE.getErrorCode().equals(m.getCode())), CoreMatchers.is(true)); }); } + @Test public void testCreatesARMessageLogsForOriginalMessage() throws IOException { @@ -550,7 +586,7 @@ public class NemhandelPersisterHandlerTest { mesg.setReceiver("0184:12345678"); mesg.setSender("0184:12345678"); - accountReceiver = new AccountReceiver(account, "0184:12345678", "87654321"); + accountReceiver = new AccountReceiver(account, "0184:12345678"); String xml = load("/sbd-examples/SBD_OIOUBL_ApplicationResponse.xml"); doInTx(em -> { diff --git a/src/test/java/dk/erst/oxalis/as4/handlers/SendSbdHandlerTest.java b/src/test/java/dk/erst/oxalis/as4/handlers/SendSbdHandlerTest.java index d733ee3..e1ed409 100644 --- a/src/test/java/dk/erst/oxalis/as4/handlers/SendSbdHandlerTest.java +++ b/src/test/java/dk/erst/oxalis/as4/handlers/SendSbdHandlerTest.java @@ -556,10 +556,10 @@ public class SendSbdHandlerTest { assertThat(message.getStatus(), equalTo(MessageStatus.ERROR)); assertThat(messageLog.size(), equalTo(4)); - assertThat(messageLog.get(0).getDescription(), equalTo("E-APS21001: No Nemhandel e-Delivery document signature found at level 0")); + assertThat(messageLog.get(0).getDescription(), equalTo("E-APS21020: No Nemhandel e-Delivery document signature found")); assertThat(messageLog.get(0).getMessageStatus(), equalTo(MessageStatus.ERROR)); assertThat(messageLog.get(0).getType(), equalTo(ValidationMessageType.ERROR)); - assertThat(messageLog.get(0).getCode(), equalTo("E-APS21001")); + assertThat(messageLog.get(0).getCode(), equalTo("E-APS21020")); assertThat(messageLog.get(0).getValidationLineReference(), equalTo("line-1")); assertThat(messageLog.get(1).getDescription(), equalTo("E-REF23001: Standard Business Document is null or empty")); assertThat(messageLog.get(1).getMessageStatus(), equalTo(MessageStatus.ERROR)); @@ -579,6 +579,33 @@ public class SendSbdHandlerTest { } } + @Test + public void testUpdatesStatusToErrorWhenOutboundValidationExceptionFromDocumentSender() throws OutboundException { + String xml = load("/cef-sbd.xml"); + ValidationResult result = new ValidationResult(); + result.addError(new ValidationMessage(ValidationMessageType.ERROR, "error-1", "dummy error", "/foo")); + + doThrow(new OutboundValidationException(result, null)).when(mockSender).send(Mockito.any(Message.class), Mockito.any(SBDMessageContext.class)); + + SendSbdHandler handler = injector.getInstance(SendSbdHandler.class); + try { + handler.handle(account, xml, "test", new SBDMessageContext()); + } catch(Exception e) { + MatcherAssert.assertThat(e, CoreMatchers.instanceOf(OutboundValidationException.class)); + + doInTx(em -> { + Message m = em.createQuery("from Message where messageUuid = :uuid", Message.class) + .setParameter("uuid", "555bcb4c-940b-4694-9b90-d9b0ae1e937b") // message id from cef-sdb.xml + .getResultStream() + .findFirst() + .orElse(null); + + MatcherAssert.assertThat(m, CoreMatchers.notNullValue()); + MatcherAssert.assertThat(m.getStatus(), CoreMatchers.equalTo(MessageStatus.ERROR)); + }); + } + } + private static As4TransmissionResponse fakeResponse() throws URISyntaxException { return fakeResponse(null); } diff --git a/src/test/java/dk/erst/oxalis/as4/handlers/dto/ValidationResultTest.java b/src/test/java/dk/erst/oxalis/as4/handlers/dto/ValidationResultTest.java new file mode 100644 index 0000000..1822262 --- /dev/null +++ b/src/test/java/dk/erst/oxalis/as4/handlers/dto/ValidationResultTest.java @@ -0,0 +1,56 @@ +package dk.erst.oxalis.as4.handlers.dto; + +import dk.erst.oxalis.as4.validation.ValidationType; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +public class ValidationResultTest { + + @Test + void validationResultFormatTest() { + ValidationResult result = new ValidationResult(); + result.setValidationType(ValidationType.ASYNCHRONOUS); + result.addError("E-APS00001","dummy error"); + result.addError("E-APS00002","dummy error 2"); + result.addWarning("W-APS00001","some warning"); + String formatted = result.toString(); + assertEquals(formatted, "Validation result (validation type: ASYNCHRONOUS): 2 errors: [E-APS00001: dummy error, E-APS00002: dummy error 2], 1 warnings: [W-APS00001: some warning]"); + } + + @Test + void validationResultFormatTestOnlyError() { + ValidationResult result = new ValidationResult(); + result.setValidationType(ValidationType.ASYNCHRONOUS); + result.addError("E-APS00001","dummy error"); + result.addError("E-APS00002","dummy error 2"); + String formatted = result.toString(); + assertEquals(formatted, "Validation result (validation type: ASYNCHRONOUS): 2 errors: [E-APS00001: dummy error, E-APS00002: dummy error 2], 0 warnings"); + } + + @Test + void validationResultFormatTestOnlyWarnings() { + ValidationResult result = new ValidationResult(); + result.setValidationType(ValidationType.ASYNCHRONOUS); + result.addWarning("W-APS00001","some warning"); + result.addWarning("W-APS00002","some warning 2"); + String formatted = result.toString(); + assertEquals(formatted, "Validation result (validation type: ASYNCHRONOUS): 0 errors, 2 warnings: [W-APS00001: some warning, W-APS00002: some warning 2]"); + } + + @Test + void validationResultFormatEmpty() { + ValidationResult result = new ValidationResult(); + result.setValidationType(ValidationType.ASYNCHRONOUS); + String formatted = result.toString(); + assertEquals(formatted, "Validation result (validation type: ASYNCHRONOUS): 0 errors, 0 warnings"); + } + + + @Test + void validationResultFormatEmptyWithoutType() { + ValidationResult result = new ValidationResult(); + String formatted = result.toString(); + assertEquals(formatted, "Validation result: 0 errors, 0 warnings"); + } +} diff --git a/src/test/java/dk/erst/oxalis/as4/jdbc/LiquibaseTest.java b/src/test/java/dk/erst/oxalis/as4/jdbc/LiquibaseTest.java new file mode 100644 index 0000000..169f705 --- /dev/null +++ b/src/test/java/dk/erst/oxalis/as4/jdbc/LiquibaseTest.java @@ -0,0 +1,59 @@ +package dk.erst.oxalis.as4.jdbc; + +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.persist.PersistService; +import com.typesafe.config.ConfigFactory; +import dk.erst.oxalis.as4.util.UtilityModule; +import network.oxalis.commons.config.ConfigModule; +import network.oxalis.commons.filesystem.FileSystemModule; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.testng.annotations.Test; + +import javax.persistence.EntityManager; +import java.math.BigInteger; + +public class LiquibaseTest { + @Test + public void testCanConfigureLiquibaseChangelogFile(){ + try { + System.setProperty("oxalis.path.conf", "configure-liquibase-changelog-conf"); + // invalidate cached system properties to force reload! + ConfigFactory.invalidateCaches(); + + Injector injector = Guice.createInjector( + new FileSystemModule(), + new ConfigModule(), + new UtilityModule(), + new JdbcModule() + ); + + // Must manually start Jpa here + PersistService persistService = injector.getInstance(PersistService.class); + persistService.start(); + + + EntityManager em = injector.getInstance(EntityManager.class); + + // Make sure liquibase has run + BigInteger n = (BigInteger)em.createNativeQuery("SELECT count(1) from databasechangelog") + .getSingleResult(); + MatcherAssert.assertThat(n.longValue(), Matchers.greaterThanOrEqualTo(1L)); + + // Make sure liquibase has the included changesets from the real changelog (which is included in dummy-db-changelog.xml) + n = (BigInteger)em.createNativeQuery("SELECT count(1) from Account") + .getSingleResult(); + MatcherAssert.assertThat(n.longValue(), Matchers.greaterThanOrEqualTo(0L)); + + // make sure test changeset from configured changelog file has run and created dummy table + n = (BigInteger)em.createNativeQuery("SELECT count(1) from Dummy_Table") + .getSingleResult(); + MatcherAssert.assertThat(n.longValue(), Matchers.greaterThanOrEqualTo(0L)); + + } finally { + System.clearProperty("oxalis.path.conf"); + ConfigFactory.invalidateCaches(); + } + } +} diff --git a/src/test/java/dk/erst/oxalis/as4/rest/resources/AbstractJaxRsTest.java b/src/test/java/dk/erst/oxalis/as4/rest/resources/AbstractJaxRsTest.java index eeec956..03e91b9 100644 --- a/src/test/java/dk/erst/oxalis/as4/rest/resources/AbstractJaxRsTest.java +++ b/src/test/java/dk/erst/oxalis/as4/rest/resources/AbstractJaxRsTest.java @@ -123,11 +123,6 @@ public class AbstractJaxRsTest extends AbstractJettyServerTest { protected static class DummyDocumentSender implements DocumentSender { - @Override - public byte[] signMessage(byte[] sbd, byte[] originalDocument, String receiverCVR, SBDMessageContext messageContext) throws OutboundException { - return "hello".getBytes(StandardCharsets.UTF_8); - } - @Override public TransmissionResponse send(Message message, SBDMessageContext messageContext) throws OutboundException { URI uri = null; diff --git a/src/test/java/dk/erst/oxalis/as4/rest/resources/ForwardedHeaderFilterTest.java b/src/test/java/dk/erst/oxalis/as4/rest/resources/ForwardedHeaderFilterTest.java index 81c2648..c8b9c15 100644 --- a/src/test/java/dk/erst/oxalis/as4/rest/resources/ForwardedHeaderFilterTest.java +++ b/src/test/java/dk/erst/oxalis/as4/rest/resources/ForwardedHeaderFilterTest.java @@ -22,7 +22,6 @@ import network.oxalis.pkix.ocsp.api.OcspFetcher; import network.oxalis.test.dummy.DummyPkiModule; import org.apache.http.impl.client.CloseableHttpClient; import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.jetbrains.annotations.NotNull; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; @@ -284,7 +283,6 @@ public class ForwardedHeaderFilterTest extends AbstractJaxRsTest { } - @NotNull private static String getUrlFromRequest(HttpServletRequest modifiedRequest) { String scheme = modifiedRequest.getScheme(); String host = modifiedRequest.getServerName(); diff --git a/src/test/java/dk/erst/oxalis/as4/rest/resources/InboxResourceTest.java b/src/test/java/dk/erst/oxalis/as4/rest/resources/InboxResourceTest.java index fdef433..8f2b164 100644 --- a/src/test/java/dk/erst/oxalis/as4/rest/resources/InboxResourceTest.java +++ b/src/test/java/dk/erst/oxalis/as4/rest/resources/InboxResourceTest.java @@ -1,12 +1,7 @@ package dk.erst.oxalis.as4.rest.resources; import dk.erst.oxalis.as4.error.ErrorCodes; -import dk.erst.oxalis.as4.handlers.dto.ErrorResponse; -import dk.erst.oxalis.as4.handlers.dto.MessageModel; -import dk.erst.oxalis.as4.handlers.dto.MessageModelResponse; -import dk.erst.oxalis.as4.handlers.dto.UpdateStatusRequest; -import dk.erst.oxalis.as4.handlers.dto.ValidationMessageType; -import dk.erst.oxalis.as4.handlers.dto.MessageLogResponse; +import dk.erst.oxalis.as4.handlers.dto.*; import dk.erst.oxalis.as4.persistence.model.*; import dk.erst.oxalis.as4.util.PasswordHasher; import org.apache.http.HttpStatus; @@ -25,9 +20,11 @@ import javax.ws.rs.core.Response; import java.nio.charset.StandardCharsets; import java.security.Security; import java.sql.SQLException; +import java.util.UUID; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; import static org.testng.Assert.assertEquals; public class InboxResourceTest extends AbstractJaxRsTest { @@ -136,11 +133,35 @@ public class InboxResourceTest extends AbstractJaxRsTest { MatcherAssert.assertThat(resp.getEntries().get(0).getSender(), CoreMatchers.equalTo("sender1")); } + @Test + public void getStatusAndResponsesForMessageWithUUIDWrongUser() { + doInTx(em -> { + Message m1 = new Message(account, MessageDirection.IN, "sender1", "receiver1", "test", "messageUUID2", + "documentType", new MessageContent("foo".getBytes(StandardCharsets.UTF_8))); + m1.setStatus(MessageStatus.PROCESSING); + em.persist(m1); + + Message response1 = new Message(account, MessageDirection.IN, "sender1", "receiver1", "test", "messageUUID", + "documentType", new MessageContent("foo".getBytes(StandardCharsets.UTF_8))); + response1.setOriginalMessage(m1); + response1.setStatus(MessageStatus.ERROR); + em.persist(response1); + }); + Client client = ResteasyClientBuilder.newClient(); + Response response = client.target("http://localhost:8080/api/inbox/messageUUID/status") + .register(new BasicAuthentication("wronguser", "testpassword")) + .request(MediaType.APPLICATION_XML_TYPE) + .get(); + assertThat(response, notNullValue()); + assertThat(response.getStatus(), equalTo(401)); + } + @Test public void testGetInboxMessageStatusFromMessageUUID() { doInTx(em -> { Message m1 = new Message(account, MessageDirection.IN, "sender1", "receiver1", "test", "messageUUID", "documentType", new MessageContent("foo".getBytes(StandardCharsets.UTF_8))); + m1.setMessageLevelResponseUuid("mlrMessageUUID"); em.persist(m1); }); @@ -165,8 +186,10 @@ public class InboxResourceTest extends AbstractJaxRsTest { MatcherAssert.assertThat(m.getChannel(), CoreMatchers.equalTo("test")); MatcherAssert.assertThat(m.getMessageUuid(), CoreMatchers.equalTo("messageUUID")); MatcherAssert.assertThat(m.getDocumentTypeId(), CoreMatchers.equalTo("documentType")); + MatcherAssert.assertThat(m.getMessageLevelResponseUuid(), CoreMatchers.equalTo("mlrMessageUUID")); } + @Test public void testGetInboxMessageLog400BadRequestOnWrongMessageUUID() { doInTx(em -> { diff --git a/src/test/java/dk/erst/oxalis/as4/rest/resources/OutboxResourceTest.java b/src/test/java/dk/erst/oxalis/as4/rest/resources/OutboxResourceTest.java index 88e5d21..684bbfa 100644 --- a/src/test/java/dk/erst/oxalis/as4/rest/resources/OutboxResourceTest.java +++ b/src/test/java/dk/erst/oxalis/as4/rest/resources/OutboxResourceTest.java @@ -32,18 +32,22 @@ import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; import java.security.Security; import java.sql.SQLException; +import java.time.LocalDateTime; import java.util.UUID; import java.util.regex.Pattern; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.doReturn; +import static org.testng.Assert.assertEquals; public class OutboxResourceTest extends AbstractJaxRsTest { private Account account; private EntityManager entityManager; + private static final String OUTBOX_URL = "http://localhost:8080/api/outbox"; @BeforeClass public void setup() { @@ -350,6 +354,99 @@ public class OutboxResourceTest extends AbstractJaxRsTest { assertThat(response.getStatus(), equalTo(HttpStatus.SC_UNAUTHORIZED)); } + + @Test + public void testGetSimpleStatusForDocumentUnauthorized() { + doInTx(em -> { + Message m1 = new Message(account, MessageDirection.OUT, "sender1", "receiver1", "test", "messageUUID", + "documentType", new MessageContent("".getBytes())); + m1.setStatus(MessageStatus.CREATED); + + em.persist(m1); + Message resp = new Message(account, MessageDirection.IN, "receiver1", "sender1", "test", "messageUUID2", + "documentType", new MessageContent("".getBytes())); + resp.setStatus(MessageStatus.ERROR); + resp.setOriginalMessage(m1); + em.persist(resp); + }); + + Client client = ResteasyClientBuilder.newClient(); + Response response = client.target("http://localhost:8080/api/outbox/messageUUID") + .register(new BasicAuthentication("wronguser", "testpassword")) + .request().get(); + assertThat(response, notNullValue()); + assertThat(response.getStatus(), equalTo(401)); + } + @Test + public void testGetMessageAndAllResponses() { + doInTx(em -> { + Message m1 = new Message(account, MessageDirection.OUT, "sender1", "receiver1", "test", "messageUUID", + "documentType", new MessageContent("".getBytes())); + m1.setStatus(MessageStatus.CREATED); + + em.persist(m1); + Message resp = new Message(account, MessageDirection.IN, "receiver1", "sender1", "test", "messageUUID2", + "documentType", new MessageContent("".getBytes())); + resp.setStatus(MessageStatus.ERROR); + resp.setOriginalMessage(m1); + em.persist(resp); + }); + + Client client = ResteasyClientBuilder.newClient(); + Response response = client.target("http://localhost:8080/api/outbox/messageUUID/with-responses") + .register(new BasicAuthentication("testuser", "testpassword")) + .request().get(); + assertThat(response, notNullValue()); + assertThat(response.getStatus(), equalTo(200)); + MessageAndResponses resp = response.readEntity(MessageAndResponses.class); + + assertThat(resp, notNullValue()); + assertThat(resp.getOriginalDocument().getMessageUuid(), equalTo("messageUUID")); + + assertThat(resp.getResponses().stream().anyMatch(x -> x.getMessageUuid().equals("messageUUID2")), CoreMatchers.equalTo(true)); + } + @Test + public void testGetMessageAndAllResponsesMultipleResponses() { + doInTx(em -> { + Message m1 = new Message(account, MessageDirection.OUT, "sender1", "receiver1", "test", "messageUUID", + "documentType", new MessageContent("".getBytes())); + m1.setStatus(MessageStatus.CREATED); + + em.persist(m1); + Message resp1 = new Message(account, MessageDirection.IN, "receiver1", "sender1", "test", "messageUUID2", + "documentType", new MessageContent("".getBytes())); + resp1.setStatus(MessageStatus.ERROR); + resp1.setOriginalMessage(m1); + em.persist(resp1); + Message resp2 = new Message(account, MessageDirection.IN, "receiver1", "sender1", "test", "messageUUID3", + "documentType", new MessageContent("".getBytes())); + resp2.setStatus(MessageStatus.ERROR); + resp2.setOriginalMessage(m1); + em.persist(resp2); + Message resp3 = new Message(account, MessageDirection.IN, "receiver1", "sender1", "test", "messageUUID4", + "documentType", new MessageContent("".getBytes())); + resp3.setStatus(MessageStatus.ERROR); + resp3.setOriginalMessage(m1); + em.persist(resp3); + }); + + Client client = ResteasyClientBuilder.newClient(); + Response response = client.target("http://localhost:8080/api/outbox/messageUUID/with-responses") + .register(new BasicAuthentication("testuser", "testpassword")) + .request().get(); + assertThat(response, notNullValue()); + assertThat(response.getStatus(), equalTo(200)); + MessageAndResponses resp = response.readEntity(MessageAndResponses.class); + + assertThat(resp, notNullValue()); + assertThat(resp.getOriginalDocument().getMessageUuid(), equalTo("messageUUID")); + + assertThat(resp.getResponses().stream().anyMatch(x -> x.getMessageUuid().equals("messageUUID2")), CoreMatchers.equalTo(true)); + assertThat(resp.getResponses().stream().anyMatch(x -> x.getMessageUuid().equals("messageUUID3")), CoreMatchers.equalTo(true)); + assertThat(resp.getResponses().stream().anyMatch(x -> x.getMessageUuid().equals("messageUUID4")), CoreMatchers.equalTo(true)); + assertThat(resp.getResponses().size(), equalTo(3)); + } + @Test public void testDocumentSignatureIsOptionalForC2() throws OutboundException { SendSbdHandler sbdHandlerMock = Mockito.mock(SendSbdHandler.class); @@ -367,6 +464,44 @@ public class OutboxResourceTest extends AbstractJaxRsTest { } } + @Test + public void testGetMessageContentReturnsCorrectResult() { + doInTx(em -> { + Message m1 = new Message(account, MessageDirection.OUT, "sender1", "receiver1", "test", "uuid-1", + "documentType", new MessageContent("".getBytes())); + m1.setStatus(MessageStatus.CREATED); + em.persist(m1); + }); + + Client client = ResteasyClientBuilder.newClient(); + Response response = client.target(OUTBOX_URL + "/uuid-1") + .register(new BasicAuthentication(account.getUsername(), "testpassword")) + .request().get(); + + MatcherAssert.assertThat(response.getStatus(), CoreMatchers.equalTo(HttpStatus.SC_OK)); + MatcherAssert.assertThat(response.readEntity(String.class), CoreMatchers.equalTo("")); + } + + @Test + public void testGetMessageContentReturns404NotFoundIfMessageIsNotFound() { + Client client = ResteasyClientBuilder.newClient(); + Response response = client.target(OUTBOX_URL + "/" + UUID.randomUUID().toString()) + .register(new BasicAuthentication(account.getUsername(), "testpassword")) + .request().get(); + + MatcherAssert.assertThat(response.getStatus(), CoreMatchers.equalTo(HttpStatus.SC_NOT_FOUND)); + } + + @Test + public void testGetMessageContentReturns401UnauthorizedIfWrongAccount() { + Client client = ResteasyClientBuilder.newClient(); + Response response = client.target(OUTBOX_URL + "/" + UUID.randomUUID().toString()) + .register(new BasicAuthentication("wrongaccount", "wrongpassword")) + .request().get(); + + MatcherAssert.assertThat(response.getStatus(), CoreMatchers.equalTo(HttpStatus.SC_UNAUTHORIZED)); + } + private String load(String path) { try (InputStream in = SendSbdHandlerTest.class.getResourceAsStream(path)) { return IOUtils.toString(in, StandardCharsets.UTF_8); diff --git a/src/test/java/dk/erst/oxalis/as4/util/OxalisDocumentSenderTest.java b/src/test/java/dk/erst/oxalis/as4/util/OxalisDocumentSenderTest.java index 7504cec..ea49414 100644 --- a/src/test/java/dk/erst/oxalis/as4/util/OxalisDocumentSenderTest.java +++ b/src/test/java/dk/erst/oxalis/as4/util/OxalisDocumentSenderTest.java @@ -3,21 +3,18 @@ package dk.erst.oxalis.as4.util; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; -import com.google.inject.Provider; import com.google.inject.persist.PersistService; import com.google.inject.util.Modules; -import com.typesafe.config.Config; -import com.typesafe.config.ConfigFactory; import dk.erst.oxalis.as4.AbstractXmlTest; +import dk.erst.oxalis.as4.EDeliverySpecification; import dk.erst.oxalis.as4.error.ErrorCodes; import dk.erst.oxalis.as4.handlers.OutboundException; import dk.erst.oxalis.as4.handlers.OutboundValidationException; import dk.erst.oxalis.as4.jdbc.JdbcModule; import dk.erst.oxalis.as4.mode.Mode; import dk.erst.oxalis.as4.persistence.model.*; +import dk.erst.oxalis.as4.signature.SignatureException; import dk.erst.oxalis.as4.signature.SignatureFactory; -import dk.erst.oxalis.as4.signature.TransferDelegationException; -import dk.erst.oxalis.as4.signature.TransferDelegationFactoryImpl; import dk.erst.oxalis.as4.validation.MessageValidator; import dk.erst.oxalis.as4.validation.ValidationException; import dk.erst.oxalis.as4.handlers.dto.ValidationResult; @@ -25,7 +22,6 @@ import dk.erst.oxalis.as4.validation.ValidationType; import io.opentracing.Span; import network.oxalis.api.lang.OxalisContentException; import network.oxalis.api.lang.OxalisTransmissionException; -import network.oxalis.api.lookup.LookupService; import network.oxalis.api.model.Direction; import network.oxalis.api.model.TransmissionIdentifier; import network.oxalis.api.outbound.TransmissionMessage; @@ -33,74 +29,49 @@ import network.oxalis.api.outbound.TransmissionRequest; import network.oxalis.api.outbound.TransmissionResponse; import network.oxalis.api.outbound.Transmitter; import network.oxalis.api.timestamp.Timestamp; -import network.oxalis.as4.lang.OxalisAs4Exception; import network.oxalis.as4.lang.OxalisAs4TransmissionException; import network.oxalis.as4.outbound.As4TransmissionResponse; import network.oxalis.commons.config.ConfigModule; import network.oxalis.commons.filesystem.FileSystemModule; -import network.oxalis.commons.header.SbdhHeaderParser; import network.oxalis.outbound.transmission.TransmissionRequestBuilder; -import network.oxalis.sniffer.PeppolStandardBusinessHeader; import network.oxalis.vefa.peppol.common.code.DigestMethod; import network.oxalis.vefa.peppol.common.lang.PeppolParsingException; import network.oxalis.vefa.peppol.common.model.*; import network.oxalis.vefa.peppol.lookup.api.LookupException; import network.oxalis.vefa.peppol.lookup.api.NotFoundException; import org.apache.commons.io.IOUtils; -import org.apache.cxf.helpers.MapNamespaceContext; -import org.apache.xml.security.exceptions.XMLSecurityException; -import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; import org.mockito.ArgumentCaptor; -import org.mockito.Mock; import org.mockito.Mockito; import org.testng.Assert; import org.testng.annotations.*; import org.w3c.dom.Document; -import org.w3c.dom.Node; -import org.xml.sax.InputSource; import org.xml.sax.SAXException; import javax.persistence.EntityManager; -import javax.xml.bind.JAXBException; import javax.xml.parsers.DocumentBuilder; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; -import javax.xml.xpath.XPath; import javax.xml.xpath.XPathExpressionException; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; -import java.net.URL; import java.nio.charset.StandardCharsets; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.Security; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; import java.util.*; import java.util.function.Consumer; import java.util.regex.Pattern; import java.util.stream.Collectors; -import static dk.erst.oxalis.as4.util.TxUtil.doInTx; -import static dk.erst.oxalis.as4.util.XPathUtil.evaluateXPath; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; -import static org.hamcrest.Matchers.nullValue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; -import static org.testng.Assert.assertEquals; public class OxalisDocumentSenderTest extends AbstractXmlTest { private Injector injector; @@ -110,15 +81,11 @@ public class OxalisDocumentSenderTest extends AbstractXmlTest { private TransmissionRequest transmissionRequestMock; private MessageValidator messageValidatorMock; private SignatureFactory signatureFactoryMock; - private TransferDelegationFactoryImpl transferDelegationFactoryMock; - private LookupService lookupServiceMock; - private X509Certificate accessPointCertificate; - private SbdhHeaderParser headerParserMock; private Mode modeMock; - private DocumentBuilderProvider documentBuilderProviderMock; private DocumentBuilder documentBuilderMock; private Account account; private EntityManager entityManager; + private Document exampleSBD; @BeforeClass public void setupBeforeClass() { @@ -162,42 +129,14 @@ public class OxalisDocumentSenderTest extends AbstractXmlTest { transmissionRequestMock = Mockito.mock(TransmissionRequest.class); messageValidatorMock = Mockito.mock(MessageValidator.class); signatureFactoryMock = Mockito.mock(SignatureFactory.class); - transferDelegationFactoryMock = Mockito.mock(TransferDelegationFactoryImpl.class); - lookupServiceMock = Mockito.mock(LookupService.class); - headerParserMock = Mockito.mock(SbdhHeaderParser.class); modeMock = Mockito.mock(Mode.class); - documentBuilderProviderMock = Mockito.mock(DocumentBuilderProvider.class); + DocumentBuilderProvider documentBuilderProviderMock = Mockito.mock(DocumentBuilderProvider.class); documentBuilderMock = Mockito.mock(DocumentBuilder.class); - try(InputStream is = this.getClass().getResourceAsStream("/oxalis_home/Test_systemcertifikat.p12")) { - URL url = this.getClass().getResource("/oxalis_home/nemhandel-e-delivery-oxalis.conf"); - Path confPath = Paths.get(url.toURI()); - - Config cfg = ConfigFactory.parseFile(confPath.toFile()).resolve(); - - KeyStore ks = KeyStore.getInstance("PKCS12"); - ks.load(is, cfg.getString("oxalis.keystore.password").toCharArray()); - - accessPointCertificate = (X509Certificate) ks.getCertificate(cfg.getString("oxalis.keystore.key.alias")); - } - Endpoint endpoint = Mockito.mock(Endpoint.class); - doReturn(accessPointCertificate).when(endpoint).getCertificate(); - doReturn(endpoint).when(lookupServiceMock).lookup(any(Header.class)); when(modeMock.isNemhandelEdeliveryMode()).thenReturn(true); byte[] signedDoc = "SignedDocument".getBytes(StandardCharsets.UTF_8); - doReturn(signedDoc).when(signatureFactoryMock).signDocument(any(), any(), any(), any()); - - PeppolStandardBusinessHeader psbh = Mockito.mock(PeppolStandardBusinessHeader.class); - Header head = Header.of( - ParticipantIdentifier.of("0184:12345678"), - ParticipantIdentifier.of("0184:12345678"), - ProcessIdentifier.of("urn:www.nesubl.eu:profiles:profile5:ver2.0"), - DocumentTypeIdentifier.of("urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##OIOUBL-2.1::2.1"), - InstanceIdentifier.of("foo"), - InstanceType.of("foo", "InstanceType", "1.0"), - new Date()); - doReturn(head).when(headerParserMock).parse(any(InputStream.class)); + doReturn(signedDoc).when(signatureFactoryMock).signDocument(any(), any()); when(requestBuilderMock.sender(any(ParticipantIdentifier.class))).thenReturn(requestBuilderMock); when(requestBuilderMock.receiver(any(ParticipantIdentifier.class))).thenReturn(requestBuilderMock); @@ -215,14 +154,13 @@ public class OxalisDocumentSenderTest extends AbstractXmlTest { when(documentBuilderProviderMock.get()).thenReturn(documentBuilderMock); - Document doc = loadDocument("sbd-examples/SBD_OIOUBL_Invoice.xml"); - when(documentBuilderMock.parse(any(InputStream.class))).thenReturn(doc); + exampleSBD = loadDocument("sbd-examples/SBD_OIOUBL_Invoice.xml"); + when(documentBuilderMock.parse(any(InputStream.class))).thenReturn(exampleSBD); sender = new OxalisDocumentSender( () -> requestBuilderMock, transmitterMock, messageValidatorMock, signatureFactoryMock, - transferDelegationFactoryMock, lookupServiceMock, accessPointCertificate, headerParserMock, modeMock, documentBuilderProviderMock, () -> entityManager); } @@ -380,6 +318,33 @@ public class OxalisDocumentSenderTest extends AbstractXmlTest { } } + @Test + public void testDocumentTypeNotSupportByReceiver() throws OxalisTransmissionException { + when(transmitterMock.transmit(any(TransmissionMessage.class))).thenThrow(new OxalisTransmissionException("test", new LookupException("Combination of receiver (iso6523-actorid-upis::0184:31674255) and document type identifier (busdox-docid-qns::urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##OIOUBL-2.1::2.1) is not supported."))); + String messageUUID = UUID.randomUUID().toString(); + Message message = new Message( + account, + MessageDirection.OUT, + "iso6523-actorid-upis::sender", + "iso6523-actorid-upis::receiver", + "channel", + messageUUID, + "busdox-docid-qns::urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##OIOUBL-2.1::2.1", + new MessageContent("test".getBytes(StandardCharsets.UTF_8))); + doInTx(em -> { + em.persist(message); + }); + try { + sender.send(message, new SBDMessageContext()); + Assert.fail("should throw"); + }catch(OutboundException e){ + assertThat(e.getCause(), instanceOf(OxalisTransmissionException.class)); + MatcherAssert.assertThat(e.getErrorCode(), CoreMatchers.equalTo(ErrorCodes.OUTBOUND_RECEIVER_DOES_NOT_EXIST_ERROR.getErrorCode())); + MatcherAssert.assertThat(e.getMessage(), CoreMatchers.equalTo("E-APS24006: The receiver 'iso6523-actorid-upis::0184:31674255' does not support document type 'busdox-docid-qns::urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##OIOUBL-2.1::2.1' according to the SMP lookup.")); + MatcherAssert.assertThat(e.getMessageUuid(), CoreMatchers.equalTo(messageUUID)); + } + } + @Test public void testNotFoundExceptionErrorDuringSMP() throws OxalisTransmissionException { when(transmitterMock.transmit(any(TransmissionMessage.class))).thenThrow(new OxalisTransmissionException("test", new NotFoundException(""))); @@ -524,33 +489,12 @@ public class OxalisDocumentSenderTest extends AbstractXmlTest { } @Test - public void testCreatesTransferDelegation() throws URISyntaxException, OxalisTransmissionException, OutboundException, IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, JAXBException, TransferDelegationException { + public void testDoesNotSignDocumentWhenNotInNemhandelMode() throws URISyntaxException, OxalisTransmissionException, OutboundException, SignatureException, IOException, TransformerException { TransmissionResponse fakeResponse = fakeResponse(); when(transmitterMock.transmit(any(TransmissionMessage.class))).thenReturn(fakeResponse); - when(modeMock.isNemhandelEdeliveryMode()).thenReturn(true); - - Message message = new Message( - account, - MessageDirection.OUT, - "iso6523-actorid-upis::sender", - "iso6523-actorid-upis::receiver", - "channel", - UUID.randomUUID().toString(), - "busdox-docid-qns::urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##OIOUBL-2.1::2.1", - new MessageContent("test".getBytes(StandardCharsets.UTF_8))); - message.setPeppolProcessId("cenbii-procid-ubl::proces::oioubl-procid-ubl::urn:www.nesubl.eu:profiles:profile5:ver2.0"); - doInTx(em -> { - em.persist(message); - }); - sender.send(message, new SBDMessageContext()); - verify(transferDelegationFactoryMock).createTransferDelegation("97281536", "97281536"); - } - - @Test - public void testLooksUpEndpointFromSMPWithCorrectInput() throws URISyntaxException, OxalisTransmissionException, OutboundException, IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, JAXBException, PeppolParsingException, XPathExpressionException, TransformerException, SAXException { - TransmissionResponse fakeResponse = fakeResponse(); - when(transmitterMock.transmit(any(TransmissionMessage.class))).thenReturn(fakeResponse); - + when(modeMock.isNemhandelEdeliveryMode()).thenReturn(false); + byte[] testContent = "test".getBytes(StandardCharsets.UTF_8); + // Account account = new Account("test", "password"); Message message = new Message( account, MessageDirection.OUT, @@ -559,34 +503,23 @@ public class OxalisDocumentSenderTest extends AbstractXmlTest { "channel", UUID.randomUUID().toString(), "busdox-docid-qns::urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##OIOUBL-2.1::2.1", - new MessageContent("test".getBytes(StandardCharsets.UTF_8))); + new MessageContent(testContent)); message.setPeppolProcessId("cenbii-procid-ubl::proces::oioubl-procid-ubl::urn:www.nesubl.eu:profiles:profile5:ver2.0"); doInTx(em -> { em.persist(message); }); sender.send(message, new SBDMessageContext()); - ArgumentCaptor
headerArgumentCaptor = ArgumentCaptor.forClass(Header.class); - verify(lookupServiceMock).lookup(headerArgumentCaptor.capture()); - - Header header = headerArgumentCaptor.getValue(); - MatcherAssert.assertThat(header, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(header.getSender(), CoreMatchers.equalTo(ParticipantIdentifier.parse(message.getSender()))); - MatcherAssert.assertThat(header.getReceiver(), CoreMatchers.equalTo(ParticipantIdentifier.parse(message.getReceiver()))); - MatcherAssert.assertThat(header.getDocumentType(), CoreMatchers.equalTo(DocumentTypeIdentifier.parse(message.getDocumentTypeId()))); - MatcherAssert.assertThat(header.getProcess(), CoreMatchers.equalTo(ProcessIdentifier.parse(message.getPeppolProcessId()))); + MatcherAssert.assertThat(message.getMessageContent().getData(), equalTo(DocumentUtil.toByteArray(exampleSBD))); + verify(signatureFactoryMock, never()).signDocument(any(), any()); } @Test - public void testSignsDocument() throws URISyntaxException, OxalisTransmissionException, OutboundException, IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, JAXBException, SAXException, TransformerException, XPathExpressionException, TransferDelegationException, XMLSecurityException { + public void testIfMessageUUIDIsSetItStays() throws Exception { + Pattern UUID_REGEX = Pattern.compile("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"); TransmissionResponse fakeResponse = fakeResponse(); when(transmitterMock.transmit(any(TransmissionMessage.class))).thenReturn(fakeResponse); - when(modeMock.isNemhandelEdeliveryMode()).thenReturn(true); - - byte[] transferDelegation = "foo".getBytes(StandardCharsets.UTF_8); - doReturn(transferDelegation).when(transferDelegationFactoryMock).createTransferDelegation(anyString(), anyString()); - - byte[] signedDoc = "ThisIsASignedDocument".getBytes(StandardCharsets.UTF_8); - doReturn(signedDoc).when(signatureFactoryMock).signDocument(any(), any(), any(), any()); + String messageUUID = UUID.randomUUID().toString(); + Document doc = loadDocument("sbd-examples/SBD_OIOUBL_Invoice-NoMessageUUID.xml"); Message message = new Message( account, @@ -594,49 +527,25 @@ public class OxalisDocumentSenderTest extends AbstractXmlTest { "iso6523-actorid-upis::sender", "iso6523-actorid-upis::receiver", "channel", - UUID.randomUUID().toString(), + messageUUID, "busdox-docid-qns::urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##OIOUBL-2.1::2.1", - new MessageContent("test".getBytes(StandardCharsets.UTF_8))); + new MessageContent(toByteArray(doc))); + message.setPeppolProcessId("cenbii-procid-ubl::proces"); doInTx(em -> { em.persist(message); }); - message.setPeppolProcessId("cenbii-procid-ubl::proces::oioubl-procid-ubl::urn:www.nesubl.eu:profiles:profile5:ver2.0"); - - sender.send(message, new SBDMessageContext()); - MatcherAssert.assertThat(message.getMessageContent().getData(), equalTo(signedDoc)); - } - - @Test - public void testDoesNotSignDocument() throws URISyntaxException, OxalisTransmissionException, OutboundException, IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, JAXBException, SAXException, TransformerException, XPathExpressionException, TransferDelegationException { - TransmissionResponse fakeResponse = fakeResponse(); - when(transmitterMock.transmit(any(TransmissionMessage.class))).thenReturn(fakeResponse); - when(modeMock.isNemhandelEdeliveryMode()).thenReturn(false); - byte[] testContent = "test".getBytes(StandardCharsets.UTF_8); + TransmissionResponse response = sender.send(message, new SBDMessageContext()); - // Account account = new Account("test", "password"); - Message message = new Message( - account, - MessageDirection.OUT, - "iso6523-actorid-upis::sender", - "iso6523-actorid-upis::receiver", - "channel", - UUID.randomUUID().toString(), - "busdox-docid-qns::urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##OIOUBL-2.1::2.1", - new MessageContent(testContent)); - message.setPeppolProcessId("cenbii-procid-ubl::proces::oioubl-procid-ubl::urn:www.nesubl.eu:profiles:profile5:ver2.0"); - doInTx(em -> { - em.persist(message); - }); - sender.send(message, new SBDMessageContext()); - MatcherAssert.assertThat(message.getMessageContent().getData(), equalTo(testContent)); + assertThat(message.getMessageUuid(), CoreMatchers.equalTo(messageUUID)); + assertThat(UUID_REGEX.matcher(message.getMessageUuid()).matches(), equalTo(Boolean.TRUE)); } @Test - public void testSendsResignedSbd() throws URISyntaxException, OxalisTransmissionException, JAXBException, OutboundException, IOException, SAXException, TransformerException, XPathExpressionException, XMLSecurityException { + public void testHandlesTransactionsCorrectlyForInternalMessageLevelResponses() throws URISyntaxException, OxalisTransmissionException, OutboundException, SignatureException { TransmissionResponse fakeResponse = fakeResponse(); when(transmitterMock.transmit(any(TransmissionMessage.class))).thenReturn(fakeResponse); byte[] signedDoc = "SignedDocument".getBytes(StandardCharsets.UTF_8); - doReturn(signedDoc).when(signatureFactoryMock).signDocument(any(), any(), any(), any()); + doReturn(signedDoc).when(signatureFactoryMock).signDocument(any(), any()); when(modeMock.isNemhandelEdeliveryMode()).thenReturn(true); Message message = new Message( @@ -653,22 +562,21 @@ public class OxalisDocumentSenderTest extends AbstractXmlTest { }); message.setPeppolProcessId("cenbii-procid-ubl::proces::oioubl-procid-ubl::urn:www.nesubl.eu:profiles:profile5:ver2.0"); - sender.send(message, new SBDMessageContext()); + EntityManager em = injector.getInstance(EntityManager.class); + TxUtil.doInTxThrows(() -> em, entityManager -> { + // will fail if OxalisDocumentSender attempts to start new transaction + sender.send(message, new SBDMessageContext(true, Direction.OUT)); + }); ArgumentCaptor inputStreamArgumentCaptor = ArgumentCaptor.forClass(InputStream.class); verify(requestBuilderMock).payLoad(inputStreamArgumentCaptor.capture()); - byte[] bytes = IOUtils.toByteArray(inputStreamArgumentCaptor.getValue()); - MatcherAssert.assertThat(bytes, equalTo(signedDoc)); } @Test - public void testIfMessageUUIDIsSetItStays() throws Exception { - Pattern UUID_REGEX = Pattern.compile("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"); + public void testCorrectlyFormatsDocumentLogs() throws OutboundException, OxalisContentException, OxalisTransmissionException, URISyntaxException, PeppolParsingException, XPathExpressionException, IOException, TransformerException, SAXException { TransmissionResponse fakeResponse = fakeResponse(); when(transmitterMock.transmit(any(TransmissionMessage.class))).thenReturn(fakeResponse); - String messageUUID = UUID.randomUUID().toString(); - Document doc = loadDocument("sbd-examples/SBD_OIOUBL_Invoice-NoMessageUUID.xml"); Message message = new Message( account, @@ -676,25 +584,33 @@ public class OxalisDocumentSenderTest extends AbstractXmlTest { "iso6523-actorid-upis::sender", "iso6523-actorid-upis::receiver", "channel", - messageUUID, + UUID.randomUUID().toString(), "busdox-docid-qns::urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##OIOUBL-2.1::2.1", - new MessageContent(toByteArray(doc))); + new MessageContent("test".getBytes(StandardCharsets.UTF_8))); + message.setPeppolProcessId("cenbii-procid-ubl::proces"); doInTx(em -> { em.persist(message); }); - TransmissionResponse response = sender.send(message, new SBDMessageContext()); + sender.send(message, new SBDMessageContext()); - assertThat(message.getMessageUuid(), CoreMatchers.equalTo(messageUUID)); - assertThat(UUID_REGEX.matcher(message.getMessageUuid()).matches(), equalTo(Boolean.TRUE)); + doInTx(em -> { + List logs = em.createQuery("from MessageLog l where l.message = :message", MessageLog.class) + .setParameter("message", message) + .getResultStream() + .filter(l -> l.getDescription() != null && l.getDescription().contains("%")) + .collect(Collectors.toList()); + + MatcherAssert.assertThat(logs.size(), CoreMatchers.equalTo(0)); + }); } @Test - public void testHandlesTransactionsCorrectlyForInternalMessageLevelResponses() throws URISyntaxException, OxalisTransmissionException, JAXBException, OutboundException, IOException, SAXException, TransformerException, XPathExpressionException, XMLSecurityException { + public void testUnsignedDocumentIsSignedBeforeSending() throws URISyntaxException, OxalisTransmissionException, OutboundException, IOException, SignatureException { TransmissionResponse fakeResponse = fakeResponse(); when(transmitterMock.transmit(any(TransmissionMessage.class))).thenReturn(fakeResponse); byte[] signedDoc = "SignedDocument".getBytes(StandardCharsets.UTF_8); - doReturn(signedDoc).when(signatureFactoryMock).signDocument(any(), any(), any(), any()); + doReturn(signedDoc).when(signatureFactoryMock).signDocument(any(), any()); when(modeMock.isNemhandelEdeliveryMode()).thenReturn(true); Message message = new Message( @@ -711,28 +627,28 @@ public class OxalisDocumentSenderTest extends AbstractXmlTest { }); message.setPeppolProcessId("cenbii-procid-ubl::proces::oioubl-procid-ubl::urn:www.nesubl.eu:profiles:profile5:ver2.0"); - EntityManager em = injector.getInstance(EntityManager.class); - TxUtil.doInTxThrows(() -> em, entityManager -> { - // will fail if OxalisDocumentSender attempts to start new transaction - sender.send(message, new SBDMessageContext(true, Direction.OUT)); - }); + sender.send(message, new SBDMessageContext()); ArgumentCaptor inputStreamArgumentCaptor = ArgumentCaptor.forClass(InputStream.class); verify(requestBuilderMock).payLoad(inputStreamArgumentCaptor.capture()); + byte[] bytes = IOUtils.toByteArray(inputStreamArgumentCaptor.getValue()); + MatcherAssert.assertThat(bytes, equalTo(signedDoc)); } @Test - public void testDoesNotIncludeOriginalStandardBusinessDocumentWhenSigningInternalMessageLevelResponse() throws URISyntaxException, OxalisTransmissionException, JAXBException, OutboundException, IOException, SAXException, TransformerException, XPathExpressionException, TransferDelegationException, XMLSecurityException { + public void testDoesNotResignDocumentBeforeSendingIfDocumentIsAlreadySigned() throws Exception { TransmissionResponse fakeResponse = fakeResponse(); when(transmitterMock.transmit(any(TransmissionMessage.class))).thenReturn(fakeResponse); + byte[] signedDoc = "SignedDocument".getBytes(StandardCharsets.UTF_8); + doReturn(signedDoc).when(signatureFactoryMock).signDocument(any(), any()); when(modeMock.isNemhandelEdeliveryMode()).thenReturn(true); - byte[] transferDelegation = "foo".getBytes(StandardCharsets.UTF_8); - doReturn(transferDelegation).when(transferDelegationFactoryMock).createTransferDelegation(anyString(), anyString()); - - byte[] signedDoc = "ThisIsASignedDocument".getBytes(StandardCharsets.UTF_8); - doReturn(signedDoc).when(signatureFactoryMock).signDocument(any(), any(), any(), any()); + Document doc = loadAndWrapInSBD("ubl-examples/OIOUBL_Invoice_v2p2.xml"); + // add dummy signature + appendNemhandelSignatureScopeNode(doc, "dummy-signature".getBytes(StandardCharsets.UTF_8), EDeliverySpecification.NEMHANDEL_EDELIVERY_1_2); + doReturn(doc).when(documentBuilderMock).parse(any(InputStream.class)); + final byte[] original = DocumentUtil.toByteArray(doc); Message message = new Message( account, @@ -742,54 +658,25 @@ public class OxalisDocumentSenderTest extends AbstractXmlTest { "channel", UUID.randomUUID().toString(), "busdox-docid-qns::urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##OIOUBL-2.1::2.1", - new MessageContent("test".getBytes(StandardCharsets.UTF_8))); - message.setPeppolProcessId("cenbii-procid-ubl::proces::oioubl-procid-ubl::urn:www.nesubl.eu:profiles:profile5:ver2.0"); + new MessageContent(original)); doInTx(em -> { em.persist(message); }); - sender.send(message, new SBDMessageContext(true, Direction.OUT)); - - verify(signatureFactoryMock).signDocument(any(), Mockito.isNull(), eq(transferDelegation), argThat(ctx -> ctx.isInternalMessageLevelResponse())); - - } - - @Test - public void testSignatureDoesNotIncludeOriginalSBDIfOriginalSBDIsUnsigned() throws Exception, TransferDelegationException { - Document sbd = loadAndWrapInSBD("ubl-examples/OIOUBL_Invoice_v2p2.xml"); - appendNemhandelSignatureScopeNode(sbd, "asic-container".getBytes(StandardCharsets.UTF_8)); - byte[] xml = DocumentUtil.toByteArray(sbd); - - TransmissionResponse fakeResponse = fakeResponse(); - when(transmitterMock.transmit(any(TransmissionMessage.class))).thenReturn(fakeResponse); - when(modeMock.isNemhandelEdeliveryMode()).thenReturn(true); + message.setPeppolProcessId("cenbii-procid-ubl::proces::oioubl-procid-ubl::urn:www.nesubl.eu:profiles:profile5:ver2.0"); - byte[] transferDelegation = "foo".getBytes(StandardCharsets.UTF_8); - doReturn(transferDelegation).when(transferDelegationFactoryMock).createTransferDelegation(anyString(), anyString()); + sender.send(message, new SBDMessageContext()); - byte[] signedDoc = "ThisIsASignedDocument".getBytes(StandardCharsets.UTF_8); - doReturn(signedDoc).when(signatureFactoryMock).signDocument(any(), any(), any(), any()); + ArgumentCaptor inputStreamArgumentCaptor = ArgumentCaptor.forClass(InputStream.class); + verify(requestBuilderMock).payLoad(inputStreamArgumentCaptor.capture()); - Message message = new Message( - account, - MessageDirection.OUT, - "iso6523-actorid-upis::sender", - "iso6523-actorid-upis::receiver", - "channel", - UUID.randomUUID().toString(), - "busdox-docid-qns::urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##OIOUBL-2.1::2.1", - new MessageContent(xml)); - message.setPeppolProcessId("cenbii-procid-ubl::proces::oioubl-procid-ubl::urn:www.nesubl.eu:profiles:profile5:ver2.0"); - doInTx(em -> { - em.persist(message); - }); - sender.send(message, new SBDMessageContext(false, Direction.OUT, false)); - verify(signatureFactoryMock).signDocument(any(), Mockito.isNull(), eq(transferDelegation), any()); + byte[] bytes = IOUtils.toByteArray(inputStreamArgumentCaptor.getValue()); + MatcherAssert.assertThat(bytes, equalTo(original)); + verify(signatureFactoryMock, never()).signDocument(any(), any()); } @Test - public void testCorrectlyFormatsDocumentLogs() throws OutboundException, OxalisContentException, OxalisTransmissionException, URISyntaxException, PeppolParsingException, XPathExpressionException, IOException, TransformerException, SAXException { - TransmissionResponse fakeResponse = fakeResponse(); - when(transmitterMock.transmit(any(TransmissionMessage.class))).thenReturn(fakeResponse); + public void testHandleSignatureExceptionWhenSigningFails() throws SignatureException { + doThrow(SignatureException.class).when(signatureFactoryMock).signDocument(any(), any()); Message message = new Message( account, @@ -801,21 +688,14 @@ public class OxalisDocumentSenderTest extends AbstractXmlTest { "busdox-docid-qns::urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##OIOUBL-2.1::2.1", new MessageContent("test".getBytes(StandardCharsets.UTF_8))); - message.setPeppolProcessId("cenbii-procid-ubl::proces"); - doInTx(em -> { - em.persist(message); - }); - sender.send(message, new SBDMessageContext()); - - doInTx(em -> { - List logs = em.createQuery("from MessageLog l where l.message = :message", MessageLog.class) - .setParameter("message", message) - .getResultStream() - .filter(l -> l.getDescription() != null && l.getDescription().contains("%")) - .collect(Collectors.toList()); - - MatcherAssert.assertThat(logs.size(), CoreMatchers.equalTo(0)); - }); + try { + sender.send(message, new SBDMessageContext()); + Assert.fail("should throw"); + } catch (OutboundException e) { + assertThat(e.getCause(), instanceOf(SignatureException.class)); + assertThat(e.getErrorCode(), CoreMatchers.equalTo(ErrorCodes.OUTBOUND_EXCEPTION.getErrorCode())); + assertThat(e.getMessage(), CoreMatchers.equalTo("Error while signing document")); + } } private Document loadDocument(String xmlPath) throws Exception { diff --git a/src/test/java/dk/erst/oxalis/as4/validation/MessageValidatorTest.java b/src/test/java/dk/erst/oxalis/as4/validation/MessageValidatorTest.java index 6a9336a..18dabcc 100644 --- a/src/test/java/dk/erst/oxalis/as4/validation/MessageValidatorTest.java +++ b/src/test/java/dk/erst/oxalis/as4/validation/MessageValidatorTest.java @@ -16,6 +16,7 @@ import dk.erst.oxalis.as4.persistence.model.MessageLog; import dk.erst.oxalis.as4.util.DataPairs; import dk.erst.oxalis.as4.util.SBDMessageContext; import dk.erst.oxalis.as4.util.UtilityModule; +import dk.erst.oxalis.as4.validation.registration.RegistrationValidator; import dk.erst.oxalis.as4.validation.schema.SchemaValidator; import dk.erst.oxalis.as4.validation.schema.SchemaValidationException; import dk.erst.oxalis.as4.validation.schematron.SchematronValidator; @@ -69,6 +70,9 @@ public class MessageValidatorTest extends AbstractXmlTest { @Mock private VersionValidator versionValidatorMock; + @Mock + private RegistrationValidator registrationValidatorMock; + private Set documentTypeConfigs; @BeforeMethod @@ -90,7 +94,8 @@ public class MessageValidatorTest extends AbstractXmlTest { schematronValidatorMock, documentBuilderProvider, signatureValidatorMock, - versionValidatorMock); + versionValidatorMock, + registrationValidatorMock); } @Test diff --git a/src/test/java/dk/erst/oxalis/as4/validation/ValidationModuleTest.java b/src/test/java/dk/erst/oxalis/as4/validation/ValidationModuleTest.java index 75d843c..86453fe 100644 --- a/src/test/java/dk/erst/oxalis/as4/validation/ValidationModuleTest.java +++ b/src/test/java/dk/erst/oxalis/as4/validation/ValidationModuleTest.java @@ -2,17 +2,22 @@ package dk.erst.oxalis.as4.validation; import com.google.common.cache.LoadingCache; import com.google.inject.*; +import com.google.inject.name.Names; import com.typesafe.config.ConfigFactory; import dk.erst.oxalis.as4.config.documenttype.DocumentTypeConfig; import dk.erst.oxalis.as4.config.documenttype.DocumentTypeModule; import dk.erst.oxalis.as4.util.UtilityModule; +import dk.erst.oxalis.as4.validation.schema.SchemaCache; +import dk.erst.oxalis.as4.validation.schema.SchemaCacheLoader; import dk.erst.oxalis.as4.validation.schema.SchemaResolver; import dk.erst.oxalis.as4.validation.schema.SchemaValidator; import dk.erst.oxalis.as4.validation.schematron.XSLTCache; import dk.erst.oxalis.as4.validation.schematron.SchematronTransformerFactory; import dk.erst.oxalis.as4.validation.schematron.SchematronValidator; +import dk.erst.oxalis.as4.validation.schematron.XSLTCacheLoader; import dk.erst.oxalis.as4.validation.signature.SignatureValidator; import dk.erst.oxalis.as4.validation.version.VersionValidator; +import network.oxalis.api.lookup.LookupService; import network.oxalis.commons.config.ConfigModule; import network.oxalis.commons.filesystem.FileSystemModule; import network.oxalis.vefa.peppol.lookup.LookupClient; @@ -25,6 +30,7 @@ import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.xml.transform.Templates; +import javax.xml.validation.Schema; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -42,12 +48,11 @@ public class ValidationModuleTest { @Mock private LookupClient lookupClientMock; + @Mock + private LookupService lookupServiceMock; + @BeforeMethod public void setup() { - System.setProperty("oxalis.path.conf", "schematron-preload-conf"); - // invalidate cached system properties to force reload! - ConfigFactory.invalidateCaches(); - MockitoAnnotations.initMocks(this); injector = Guice.createInjector( new FileSystemModule(), @@ -60,18 +65,12 @@ public class ValidationModuleTest { protected void configure() { bind(SignatureValidator.class).toInstance(signatureValidatorMock); bind(LookupClient.class).toInstance(lookupClientMock); + bind(LookupService.class).toInstance(lookupServiceMock); } } ); } - @AfterMethod - public void cleanup() { - System.clearProperty("oxalis.path.conf"); - // invalidate cached system properties to force reload! - ConfigFactory.invalidateCaches(); - } - @Test public void testModuleProvidesExpectedSingletons() { testProvidesSingleton(SchemaResolver.class); @@ -80,6 +79,10 @@ public class ValidationModuleTest { testProvidesSingleton(SchematronValidator.class); testProvidesSingleton(MessageValidator.class); testProvidesSingleton(VersionValidator.class); + testProvidesSingleton(SchemaCacheLoader.class); + testProvidesSingleton(XSLTCacheLoader.class); + testProvidesSingleton(Key.get(new TypeLiteral>() {}, SchemaCache.class)); + testProvidesSingleton(Key.get(new TypeLiteral>() {}, XSLTCache.class)); } private void testProvidesSingleton(Class clazz) { @@ -90,6 +93,14 @@ public class ValidationModuleTest { MatcherAssert.assertThat(singleton2, CoreMatchers.sameInstance(singleton)); } + private void testProvidesSingleton(Key key) { + T singleton = injector.getInstance(key); + MatcherAssert.assertThat(singleton, CoreMatchers.notNullValue()); + + T singleton2 = injector.getInstance(key); + MatcherAssert.assertThat(singleton2, CoreMatchers.sameInstance(singleton)); + } + @Test public void testInitializesSchematronCacheAndPreloadsSpecificSchematron() { @@ -140,4 +151,54 @@ public class ValidationModuleTest { } } + @Test + public void testInitializesSchemaCacheAndPreloadsSpecificXSDSchemas() { + + try { + System.setProperty("oxalis.path.conf", "schematron-preload-conf"); + // invalidate cached system properties to force reload! + ConfigFactory.invalidateCaches(); + + MockitoAnnotations.initMocks(this); + injector = Guice.createInjector( + new FileSystemModule(), + new ConfigModule(), + new UtilityModule(), + new DocumentTypeModule(), + new ValidationModule(), + new AbstractModule() { + @Override + protected void configure() { + bind(SignatureValidator.class).toInstance(signatureValidatorMock); + bind(LookupClient.class).toInstance(lookupClientMock); + } + } + ); + + LoadingCache schemaCache = injector.getInstance(Key.get(new TypeLiteral>() {}, SchemaCache.class)); + MatcherAssert.assertThat(schemaCache, CoreMatchers.notNullValue()); + + Set configs = injector.getInstance(Key.get(new TypeLiteral>() {})); + + List preloaded = configs.stream().filter(DocumentTypeConfig::isPreloadSchema).collect(Collectors.toList()); + MatcherAssert.assertThat(preloaded.size(), CoreMatchers.equalTo(2)); + MatcherAssert.assertThat(preloaded.stream().anyMatch(p -> p.getFriendlyName().equals("Faktura")), CoreMatchers.is(true)); + MatcherAssert.assertThat(preloaded.stream().anyMatch(p -> p.getFriendlyName().equals("Applikationsmeddelse")), CoreMatchers.is(true)); + preloaded.stream().forEach(s -> { + MatcherAssert.assertThat("XSD schema " + s.getSchemaPath() + " should be preloaded", schemaCache.getIfPresent(s.getSchemaPath()), CoreMatchers.notNullValue()); + }); + + List notPreloaded = configs.stream() + .filter(c -> !c.isPreloadSchema()) + .filter(c -> preloaded.stream().noneMatch(d -> d.getSchemaPath().equals(c.getSchemaPath()))) // some document types use the same XSD so we have to exclude all these if one of them has been preloaded + .collect(Collectors.toList()); + notPreloaded.stream().forEach(s -> { + MatcherAssert.assertThat("XSD schema " + s.getSchemaPath() + " should not be preloaded", schemaCache.getIfPresent(s.getSchemaPath()), CoreMatchers.nullValue()); + }); + } finally { + System.clearProperty("oxalis.path.conf"); + ConfigFactory.invalidateCaches(); + } + } + } diff --git a/src/test/java/dk/erst/oxalis/as4/validation/registration/RegistrationValidatorTest.java b/src/test/java/dk/erst/oxalis/as4/validation/registration/RegistrationValidatorTest.java new file mode 100644 index 0000000..0c85bff --- /dev/null +++ b/src/test/java/dk/erst/oxalis/as4/validation/registration/RegistrationValidatorTest.java @@ -0,0 +1,127 @@ +package dk.erst.oxalis.as4.validation.registration; + +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.name.Names; +import dk.erst.oxalis.as4.AbstractXmlTest; +import dk.erst.oxalis.as4.config.documenttype.DocumentTypeConfig; +import dk.erst.oxalis.as4.config.documenttype.MessageLevelResponseType; +import dk.erst.oxalis.as4.handlers.dto.ValidationResult; +import dk.erst.oxalis.as4.persistence.model.Message; +import network.oxalis.api.lang.OxalisTransmissionException; +import network.oxalis.api.lookup.LookupService; +import network.oxalis.as4.lang.OxalisAs4TransmissionException; +import network.oxalis.vefa.peppol.common.lang.EndpointNotFoundException; +import network.oxalis.vefa.peppol.common.model.Endpoint; +import network.oxalis.vefa.peppol.lookup.api.LookupException; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import static dk.erst.oxalis.as4.error.ErrorCodes.OUTBOUND_SENDER_DOES_NOT_EXIST_ERROR; +import static dk.erst.oxalis.as4.error.ErrorCodes.OUTBOUND_SMP_ERROR; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; + +public class RegistrationValidatorTest extends AbstractXmlTest{ + + @Mock + private LookupService mockLookupService; + private Injector injector; + private RegistrationValidator registrationValidator; + + @BeforeMethod + public void setup() throws Exception { + MockitoAnnotations.initMocks(this); + injector = Guice.createInjector( + new AbstractModule() { + @Override + protected void configure() { + bind(LookupService.class).toInstance(mockLookupService); + } + } + ); + registrationValidator = new RegistrationValidatorImpl(injector); + } + + @AfterMethod + public void cleanup() { + } + + @Test + public void testLookupOIOUBL() throws Exception { + Message message = new Message(); + message.setSender("iso6523-actorid-upis::0088:5798912345672"); + DocumentTypeConfig documentTypeConfig = new DocumentTypeConfig(); + documentTypeConfig.setMessageLevelResponseType(MessageLevelResponseType.OIOUBL_APPLICATION_RESPONSE); + doReturn(new Endpoint(null, null, null, null)) + .when(mockLookupService).lookup(any()); + ValidationResult result = registrationValidator.validate(message, documentTypeConfig); + Assert.assertEquals(result.getErrors().size(), 0); + + doThrow(new OxalisAs4TransmissionException("test", new LookupException("test"))) + .when(mockLookupService).lookup(any()); + result = registrationValidator.validate(message, documentTypeConfig); + Assert.assertEquals(result.getErrors().size(), 1); + Assert.assertEquals(result.getErrors().get(0).getCode(), OUTBOUND_SENDER_DOES_NOT_EXIST_ERROR.getErrorCode()); + + doThrow(new OxalisAs4TransmissionException("test", new EndpointNotFoundException("test"))) + .when(mockLookupService).lookup(any()); + result = registrationValidator.validate(message, documentTypeConfig); + Assert.assertEquals(result.getErrors().size(), 1); + Assert.assertEquals(result.getErrors().get(0).getCode(), OUTBOUND_SENDER_DOES_NOT_EXIST_ERROR.getErrorCode()); + + doThrow(new OxalisAs4TransmissionException("test")).when(mockLookupService).lookup(any()); + try { + registrationValidator.validate(message, documentTypeConfig); + Assert.fail(); + } + catch(Exception e) { + Assert.assertTrue(e instanceof OxalisTransmissionException); + } + } + + + + @Test + public void testLookupPEPPOL() throws Exception { + Message message = new Message(); + message.setSender("iso6523-actorid-upis::0088:5798912345672"); + DocumentTypeConfig documentTypeConfig = new DocumentTypeConfig(); + documentTypeConfig.setMessageLevelResponseType(MessageLevelResponseType.PEPPOL_MESSAGE_LEVEL_RESPONSE); + doReturn(new Endpoint(null, null, null, null)) + .when(mockLookupService).lookup(any()); + ValidationResult result = registrationValidator.validate(message, documentTypeConfig); + Assert.assertEquals(result.getErrors().size(), 0); + + doThrow(new OxalisAs4TransmissionException("test", new LookupException("test"))) + .when(mockLookupService).lookup(any()); + result = registrationValidator.validate(message, documentTypeConfig); + Assert.assertEquals(result.getErrors().size(), 1); + Assert.assertEquals(result.getErrors().get(0).getCode(), OUTBOUND_SENDER_DOES_NOT_EXIST_ERROR.getErrorCode()); + + doThrow(new OxalisAs4TransmissionException("test", new EndpointNotFoundException("test"))) + .when(mockLookupService).lookup(any()); + result = registrationValidator.validate(message, documentTypeConfig); + Assert.assertEquals(result.getErrors().size(), 1); + Assert.assertEquals(result.getErrors().get(0).getCode(), OUTBOUND_SENDER_DOES_NOT_EXIST_ERROR.getErrorCode()); + + doThrow(new OxalisAs4TransmissionException("test")).when(mockLookupService).lookup(any()); + try { + registrationValidator.validate(message, documentTypeConfig); + Assert.fail(); + } + catch(Exception e) { + Assert.assertTrue(e instanceof OxalisTransmissionException); + } + } + + + + +} diff --git a/src/test/java/dk/erst/oxalis/as4/validation/schema/SchemaCacheLoaderTest.java b/src/test/java/dk/erst/oxalis/as4/validation/schema/SchemaCacheLoaderTest.java new file mode 100644 index 0000000..fdd9d84 --- /dev/null +++ b/src/test/java/dk/erst/oxalis/as4/validation/schema/SchemaCacheLoaderTest.java @@ -0,0 +1,69 @@ +package dk.erst.oxalis.as4.validation.schema; + +import org.hamcrest.CoreMatchers; +import org.hamcrest.MatcherAssert; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import javax.xml.validation.Schema; +import java.io.File; +import java.io.FileNotFoundException; +import java.net.URL; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Map; + +public class SchemaCacheLoaderTest { + + private SchemaCacheLoader cacheLoader; + + @BeforeMethod + public void setup() { + cacheLoader = new SchemaCacheLoader(); + } + + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testThrowsExceptionIfXsltPathIsNull() throws Exception { + cacheLoader.load(null); + } + + @Test(expectedExceptions = FileNotFoundException.class) + public void testThrowsExceptionIfXSLTDoesNotExist() throws Exception { + String xsdPath = "foo.xsd"; + cacheLoader.load(xsdPath); + } + + @Test + public void testCanLoadFromResources() throws Exception { + Schema schema = cacheLoader.load("META-INF/Schemas/UBL_v2.1/maindoc/UBL-Invoice-2.1.xsd"); + MatcherAssert.assertThat(schema, CoreMatchers.notNullValue()); + } + + @Test + public void testCanCreateSchemaUsingAbsolutePath() throws Exception { + URL res = this.getClass().getClassLoader().getResource("META-INF/Schemas/UBL_v2.1/maindoc/UBL-Invoice-2.1.xsd"); + File file = Paths.get(res.toURI()).toFile(); + + Schema schema = cacheLoader.load(file.getAbsolutePath()); + MatcherAssert.assertThat(schema, CoreMatchers.notNullValue()); + } + + @Test + public void testBulkLoadReturnsEmptyCollectionIfNoSchemaProvided() throws Exception { + Map schemaMap = cacheLoader.loadAll(null); + MatcherAssert.assertThat(schemaMap, CoreMatchers.notNullValue()); + MatcherAssert.assertThat(schemaMap.size(), CoreMatchers.equalTo(0)); + } + + @Test + public void testCanBulkLoadSchemas() throws Exception { + String invoicePath = "META-INF/Schemas/UBL_v2.1/maindoc/UBL-Invoice-2.1.xsd"; + String arPath = "META-INF/Schemas/UBL_v2.1/maindoc/UBL-ApplicationResponse-2.1.xsd"; + Map schemaMap = cacheLoader.loadAll(Arrays.asList(invoicePath, arPath)); + MatcherAssert.assertThat(schemaMap, CoreMatchers.notNullValue()); + MatcherAssert.assertThat(schemaMap.size(), CoreMatchers.equalTo(2)); + MatcherAssert.assertThat(schemaMap.get(invoicePath), CoreMatchers.notNullValue()); + MatcherAssert.assertThat(schemaMap.get(arPath), CoreMatchers.notNullValue()); + } +} diff --git a/src/test/java/dk/erst/oxalis/as4/validation/schema/SchemaResolverTest.java b/src/test/java/dk/erst/oxalis/as4/validation/schema/SchemaResolverTest.java index 2afcf61..6334dd7 100644 --- a/src/test/java/dk/erst/oxalis/as4/validation/schema/SchemaResolverTest.java +++ b/src/test/java/dk/erst/oxalis/as4/validation/schema/SchemaResolverTest.java @@ -1,8 +1,8 @@ package dk.erst.oxalis.as4.validation.schema; -import com.google.inject.AbstractModule; -import com.google.inject.Guice; -import com.google.inject.Injector; +import com.google.common.cache.LoadingCache; +import com.google.inject.*; +import com.google.inject.util.Modules; import dk.erst.oxalis.as4.AbstractXmlTest; import dk.erst.oxalis.as4.config.documenttype.DocumentTypeConfig; import dk.erst.oxalis.as4.config.documenttype.DocumentTypeConfigResolver; @@ -13,12 +13,15 @@ import dk.erst.oxalis.as4.util.DocumentBuilderProvider; import dk.erst.oxalis.as4.util.UtilityModule; import dk.erst.oxalis.as4.validation.ValidationException; import dk.erst.oxalis.as4.validation.ValidationModule; +import dk.erst.oxalis.as4.validation.schematron.SchematronTransformerFactory; +import dk.erst.oxalis.as4.validation.schematron.XSLTCache; import dk.erst.oxalis.as4.validation.signature.SignatureValidator; import network.oxalis.commons.config.ConfigModule; import network.oxalis.commons.filesystem.FileSystemModule; import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -28,8 +31,12 @@ import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Templates; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; import javax.xml.validation.Schema; import java.io.IOException; +import java.util.concurrent.ExecutionException; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; @@ -42,6 +49,11 @@ public class SchemaResolverTest extends AbstractXmlTest { @Mock private SignatureValidator signatureValidatorMock; + private LoadingCache schemaCache; + + @Mock + private LoadingCache cacheMock; + @BeforeMethod public void setup() throws ValidationException { MockitoAnnotations.initMocks(this); @@ -62,17 +74,12 @@ public class SchemaResolverTest extends AbstractXmlTest { schemaResolver = injector.getInstance(SchemaResolver.class); documentTypeConfigResolver = injector.getInstance(DocumentTypeConfigResolver.class); + schemaCache = injector.getInstance(Key.get(new TypeLiteral>() {}, SchemaCache.class)); } @Test - public void testReturnsNullIfDocumentIsNull() throws Exception { - MatcherAssert.assertThat(schemaResolver.resolveSchema(null, new DocumentTypeConfig()), CoreMatchers.nullValue()); - } - - @Test - public void testReturnsNullIfDocumentTypeConfigIsNull() throws Exception { - Document doc = new DocumentBuilderProvider().get().newDocument(); - MatcherAssert.assertThat(schemaResolver.resolveSchema(doc, null), CoreMatchers.nullValue()); + public void testReturnsNullIfSchemaPathIsNull() throws Exception { + MatcherAssert.assertThat(schemaResolver.resolveSchema(null), CoreMatchers.nullValue()); } @Test @@ -207,8 +214,47 @@ public class SchemaResolverTest extends AbstractXmlTest { private void assertCanLocateSchema(String xmlPath) throws Exception { Document doc = loadAndWrapInSBD(xmlPath); DocumentTypeConfig config = documentTypeConfigResolver.getDocumentTypeConfig(doc); - Schema schema = schemaResolver.resolveSchema(doc, config); + Schema schema = schemaResolver.resolveSchema(config.getSchemaPath()); MatcherAssert.assertThat(schema, CoreMatchers.notNullValue()); } + @Test + public void testLoadsCompiledSchemaThroughCache() throws Exception { + Injector injector = Guice.createInjector(Modules.override( + new FileSystemModule(), + new ConfigModule(), + new UtilityModule(), + new DocumentTypeModule(), + new ValidationModule()) + .with( + new AbstractModule() { + @Override + protected void configure() { + bind(SignatureValidator.class).toInstance(signatureValidatorMock); + bind(Key.get(new TypeLiteral>() {}, SchemaCache.class)).toInstance(cacheMock); + } + }) + ); + + Schema schemaMock = Mockito.mock(Schema.class); + doReturn(schemaMock).when(cacheMock).get(any()); + + SchemaResolver schemaResolver = injector.getInstance(SchemaResolver.class); + Schema t = schemaResolver.resolveSchema("META-INF/Schemas/UBL_v2.1/maindoc/UBL-Invoice-2.1.xsd"); + + MatcherAssert.assertThat(t, CoreMatchers.equalTo(schemaMock)); + } + + @Test + public void testCachesCompiledSchemaWhichIsNotAlreadyCached() throws TransformerConfigurationException, IOException, ExecutionException { + String schemaPath = "META-INF/Schemas/UBL_v2.1/common/UBL-CommonAggregateComponents-2.1.xsd"; + + MatcherAssert.assertThat(schemaCache.getIfPresent(schemaPath), CoreMatchers.nullValue()); + Schema t = schemaResolver.resolveSchema(schemaPath); + MatcherAssert.assertThat(t, CoreMatchers.notNullValue()); + + // should be cached + MatcherAssert.assertThat(schemaCache.getIfPresent(schemaPath), CoreMatchers.notNullValue()); + } + } diff --git a/src/test/java/dk/erst/oxalis/as4/validation/schema/SchemaValidatorTest.java b/src/test/java/dk/erst/oxalis/as4/validation/schema/SchemaValidatorTest.java index b629b88..b9e0de5 100644 --- a/src/test/java/dk/erst/oxalis/as4/validation/schema/SchemaValidatorTest.java +++ b/src/test/java/dk/erst/oxalis/as4/validation/schema/SchemaValidatorTest.java @@ -5,7 +5,6 @@ import com.google.inject.Guice; import com.google.inject.Injector; import dk.erst.oxalis.as4.config.documenttype.DocumentTypeConfig; import dk.erst.oxalis.as4.config.documenttype.DocumentTypeConfigResolver; -import dk.erst.oxalis.as4.config.documenttype.DocumentTypeConfigResolverException; import dk.erst.oxalis.as4.config.documenttype.DocumentTypeModule; import dk.erst.oxalis.as4.AbstractXmlTest; import dk.erst.oxalis.as4.error.ErrorCodes; @@ -14,7 +13,6 @@ import dk.erst.oxalis.as4.util.SBDMessageContext; import dk.erst.oxalis.as4.util.UtilityModule; import dk.erst.oxalis.as4.validation.ValidationModule; import dk.erst.oxalis.as4.handlers.dto.ValidationResult; -import dk.erst.oxalis.as4.validation.schematron.SchematronValidationException; import dk.erst.oxalis.as4.validation.signature.SignatureValidator; import network.oxalis.api.model.Direction; import network.oxalis.commons.config.ConfigModule; @@ -28,17 +26,10 @@ import org.mockito.MockitoAnnotations; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.w3c.dom.Document; -import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; public class SchemaValidatorTest extends AbstractXmlTest { private Injector injector; @@ -309,6 +300,56 @@ public class SchemaValidatorTest extends AbstractXmlTest { } } + @Test + public void testCanValidateNemkontoNKSPayment() throws Exception { + assertValidateValidXml("nemkonto-examples/Payment_GLN_5798009811578.xml"); + } + + @Test + public void testCanValidateNemkontoNKSReceipt0() throws Exception { + assertValidateValidXml("nemkonto-examples/Kvittering 0.xml"); + } + + @Test + public void testCanValidateNemkontoNKSReceipt1() throws Exception { + assertValidateValidXml("nemkonto-examples/Kvittering 1.xml"); + } + + @Test + public void testCanValidateNemkontoNKSResponse2() throws Exception { + assertValidateValidXml("nemkonto-examples/Retursvar 2 med ACPT.xml"); + } + + @Test + public void testCanValidateNemkontoNKSResponse5() throws Exception { + assertValidateValidXml("nemkonto-examples/Retursvar 5 med PART.xml"); + } + + @Test + public void testCanValidateNemkontoNKSResponse7() throws Exception { + assertValidateValidXml("nemkonto-examples/Retursvar 7.xml"); + } + + @Test + public void testCanValidateNemkontoNKSResponse8() throws Exception { + assertValidateValidXml("nemkonto-examples/Retursvar 8.xml"); + } + + @Test + public void testCanValidateNemkontoNKSResponse9() throws Exception { + assertValidateValidXml("nemkonto-examples/Retursvar 9.xml"); + } + + @Test + public void testCanValidateNemkontoNemkontoPrivatUdbetalerTransporterRequest() throws Exception { + assertValidateValidXml("nemkonto-examples/Request - CVR-nr SE-nr P-nr.xml"); + } + + @Test + public void testCanValidateNemkontoNemkontoPrivatUdbetalerTransporterResponse() throws Exception { + assertValidateValidXml("nemkonto-examples/Reply - CPR-nr - DK konto og ingen kontonummer.XML"); + } + private void assertValidateValidXml(String xmlPath) throws Exception { Document doc = loadAndWrapInSBD(xmlPath); diff --git a/src/test/java/dk/erst/oxalis/as4/validation/schematron/SchematronValidatorTest.java b/src/test/java/dk/erst/oxalis/as4/validation/schematron/SchematronValidatorTest.java index 310708e..c872116 100644 --- a/src/test/java/dk/erst/oxalis/as4/validation/schematron/SchematronValidatorTest.java +++ b/src/test/java/dk/erst/oxalis/as4/validation/schematron/SchematronValidatorTest.java @@ -291,6 +291,56 @@ public class SchematronValidatorTest extends AbstractXmlTest { MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.equalTo("/*:CreditNote[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2'][1]/*:LegalMonetaryTotal[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2'][1]")); } + @Test + public void testCanValidateNemkontoNKSPayment() throws Exception { + assertValidateSchematronOk("nemkonto-examples/Payment_GLN_5798009811578.xml"); + } + + @Test + public void testCanValidateNemkontoNKSReceipt0() throws Exception { + assertValidateSchematronOk("nemkonto-examples/Kvittering 0.xml"); + } + + @Test + public void testCanValidateNemkontoNKSReceipt1() throws Exception { + assertValidateSchematronOk("nemkonto-examples/Kvittering 1.xml"); + } + + @Test + public void testCanValidateNemkontoNKSResponse2() throws Exception { + assertValidateSchematronOk("nemkonto-examples/Retursvar 2 med ACPT.xml"); + } + + @Test + public void testCanValidateNemkontoNKSResponse5() throws Exception { + assertValidateSchematronOk("nemkonto-examples/Retursvar 5 med PART.xml"); + } + + @Test + public void testCanValidateNemkontoNKSResponse7() throws Exception { + assertValidateSchematronOk("nemkonto-examples/Retursvar 7.xml"); + } + + @Test + public void testCanValidateNemkontoNKSResponse8() throws Exception { + assertValidateSchematronOk("nemkonto-examples/Retursvar 8.xml"); + } + + @Test + public void testCanValidateNemkontoNKSResponse9() throws Exception { + assertValidateSchematronOk("nemkonto-examples/Retursvar 9.xml"); + } + + @Test + public void testCanValidateNemkontoNemkontoPrivatUdbetalerTransporterRequest() throws Exception { + assertValidateSchematronOk("nemkonto-examples/Request - CVR-nr SE-nr P-nr.xml"); + } + + @Test + public void testCanValidateNemkontoNemkontoPrivatUdbetalerTransporterResponse() throws Exception { + assertValidateSchematronOk("nemkonto-examples/Reply - CPR-nr - DK konto og ingen kontonummer.XML"); + } + private void assertValidateSchematronOk(String xmlPath) throws Exception { Document doc = loadAndWrapInSBD(xmlPath); diff --git a/src/test/java/dk/erst/oxalis/as4/validation/signature/DSSModuleTest.java b/src/test/java/dk/erst/oxalis/as4/validation/signature/DSSModuleTest.java index 89a4d4a..ba2177f 100644 --- a/src/test/java/dk/erst/oxalis/as4/validation/signature/DSSModuleTest.java +++ b/src/test/java/dk/erst/oxalis/as4/validation/signature/DSSModuleTest.java @@ -6,11 +6,12 @@ import com.google.inject.util.Modules; import dk.erst.oxalis.as4.config.documenttype.DocumentTypeModule; import dk.erst.oxalis.as4.util.UtilityModule; import dk.erst.oxalis.as4.validation.ValidationModule; -import eu.europa.esig.dss.asic.xades.signature.ASiCWithXAdESService; import eu.europa.esig.dss.policy.ValidationPolicy; import eu.europa.esig.dss.spi.x509.CertificateSource; import eu.europa.esig.dss.spi.x509.revocation.crl.CRLSource; import eu.europa.esig.dss.validation.CertificateVerifier; +import eu.europa.esig.dss.xades.XAdESSignatureParameters; +import eu.europa.esig.dss.xades.signature.XAdESService; import network.oxalis.api.settings.Settings; import network.oxalis.commons.certvalidator.api.CrlFetcher; import network.oxalis.commons.config.ConfigModule; @@ -102,8 +103,9 @@ public class DSSModuleTest { } @Test - public void testProvidesAsicWithXAdESService() { - testProvidesSingleton(ASiCWithXAdESService.class); + public void testProvidesXAdESService() { + Provider prov = injector.getProvider(XAdESService.class); + MatcherAssert.assertThat(prov, CoreMatchers.notNullValue()); } @Test @@ -131,4 +133,10 @@ public class DSSModuleTest { T singleton2 = injector.getInstance(clazz); MatcherAssert.assertThat(singleton2, CoreMatchers.sameInstance(singleton)); } + + @Test + public void testProvidesXAdESSignatureParameters() { + Provider prov = injector.getProvider(XAdESSignatureParameters.class); + MatcherAssert.assertThat(prov, CoreMatchers.notNullValue()); + } } diff --git a/src/test/java/dk/erst/oxalis/as4/validation/signature/SignatureFactoryTest.java b/src/test/java/dk/erst/oxalis/as4/validation/signature/SignatureFactoryTest.java index 3058bbd..2d09391 100644 --- a/src/test/java/dk/erst/oxalis/as4/validation/signature/SignatureFactoryTest.java +++ b/src/test/java/dk/erst/oxalis/as4/validation/signature/SignatureFactoryTest.java @@ -1,37 +1,30 @@ package dk.erst.oxalis.as4.validation.signature; + import com.google.inject.*; -import com.google.inject.name.Named; -import com.google.inject.name.Names; import com.google.inject.util.Modules; -import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; import dk.erst.oxalis.as4.AbstractXmlTest; +import dk.erst.oxalis.as4.EDeliverySpecification; import dk.erst.oxalis.as4.config.documenttype.DocumentTypeModule; import dk.erst.oxalis.as4.error.ErrorCodes; -import dk.erst.oxalis.as4.handlers.dto.ValidationMessage; -import dk.erst.oxalis.as4.signature.*; +import dk.erst.oxalis.as4.handlers.dto.ValidationResult; +import dk.erst.oxalis.as4.persistence.model.Account; +import dk.erst.oxalis.as4.persistence.model.Message; +import dk.erst.oxalis.as4.persistence.model.MessageContent; +import dk.erst.oxalis.as4.persistence.model.MessageDirection; +import dk.erst.oxalis.as4.signature.SignatureException; +import dk.erst.oxalis.as4.signature.SignatureFactory; import dk.erst.oxalis.as4.util.*; -import dk.erst.oxalis.as4.validation.ValidationException; import dk.erst.oxalis.as4.validation.ValidationModule; -import eu.europa.esig.dss.asic.common.ASiCContent; -import eu.europa.esig.dss.asic.xades.ASiCWithXAdESContainerExtractor; -import eu.europa.esig.dss.asic.xades.ASiCWithXAdESSignatureParameters; -import eu.europa.esig.dss.asic.xades.signature.ASiCWithXAdESService; -import eu.europa.esig.dss.diagnostic.CertificateWrapper; -import eu.europa.esig.dss.enumerations.ASiCContainerType; -import eu.europa.esig.dss.enumerations.SignatureLevel; +import eu.europa.esig.dss.enumerations.DigestAlgorithm; import eu.europa.esig.dss.model.DSSDocument; import eu.europa.esig.dss.model.InMemoryDocument; -import eu.europa.esig.dss.model.x509.CertificateToken; import eu.europa.esig.dss.policy.ValidationPolicy; import eu.europa.esig.dss.policy.ValidationPolicyFacade; import eu.europa.esig.dss.policy.jaxb.Level; -import eu.europa.esig.dss.token.AbstractKeyStoreTokenConnection; -import eu.europa.esig.dss.token.DSSPrivateKeyEntry; -import eu.europa.esig.dss.token.KSPrivateKeyEntry; -import network.oxalis.api.lang.OxalisLoadingException; +import eu.europa.esig.dss.spi.x509.revocation.crl.CRLSource; +import eu.europa.esig.dss.spi.x509.revocation.ocsp.OCSPSource; import network.oxalis.api.model.Direction; -import network.oxalis.api.settings.Settings; import network.oxalis.commons.certvalidator.api.CrlFetcher; import network.oxalis.commons.config.ConfigModule; import network.oxalis.commons.filesystem.FileSystemModule; @@ -39,25 +32,18 @@ import network.oxalis.commons.mode.ModeModule; import network.oxalis.commons.mode.OxalisCrlFetcher; import network.oxalis.commons.mode.OxalisOcspFetcher; import network.oxalis.commons.security.CertificateModule; -import network.oxalis.commons.security.KeyStoreConf; import network.oxalis.commons.tracing.TracingModule; import network.oxalis.pkix.ocsp.api.OcspFetcher; -import network.oxalis.vefa.peppol.common.lang.PeppolLoadingException; -import network.oxalis.vefa.peppol.mode.Mode; import network.oxalis.vefa.peppol.sbdh.Ns; -import network.oxalis.vefa.peppol.security.ModeDetector; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.StringUtils; import org.apache.cxf.helpers.MapNamespaceContext; -import org.apache.hc.core5.http.nio.ssl.BasicServerTlsStrategy; import org.apache.http.impl.client.CloseableHttpClient; -import org.h2.value.Transfer; import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.testng.Assert; +import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.w3c.dom.Document; @@ -65,39 +51,26 @@ import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; -import javax.xml.XMLConstants; import javax.xml.bind.JAXBException; import javax.xml.parsers.DocumentBuilder; import javax.xml.stream.XMLStreamException; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; -import javax.xml.xpath.*; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathFactory; import java.io.IOException; -import java.io.InputStream; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.util.Base64; +import java.util.UUID; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; public class SignatureFactoryTest extends AbstractXmlTest{ private Injector injector; private SignatureFactory signatureFactory; - private ASiCWithXAdESService service; @Mock private OxalisCrlFetcher mockCrlFetcher; @@ -111,15 +84,13 @@ public class SignatureFactoryTest extends AbstractXmlTest{ @Mock private SignatureValidationPolicyProvider mockValidationPolicyProvider; - private KeyStore keystore; - private KeystoreSignatureTokenConnectionWrapper signatureToken; - private ASiCWithXAdESSignatureParameters signatureParameters; - private KSPrivateKeyEntry signaturePrivateKey; - private KeystoreSignatureTokenConnectionWrapper innerSignatureKeystoreWrapper; - private ASiCWithXAdESSignatureParameters innerSignatureParameters; - private KSPrivateKeyEntry innerSignaturePrivateKey; - private String cvr_Receiver; - private String cvr_Sender; + @Mock + private CRLSource crlSourceMock; + + @Mock + private OCSPSource ocspSourceMock; + + private XPath sbdXpath; @BeforeMethod public void setup() throws IOException, XMLStreamException, JAXBException, SAXException, KeyStoreException, CertificateException, NoSuchAlgorithmException { @@ -130,6 +101,10 @@ public class SignatureFactoryTest extends AbstractXmlTest{ noRevocationCheckPolicy.getSignatureConstraints().getBasicSignatureConstraints().getSigningCertificate().getRevocationDataAvailable().setLevel(Level.IGNORE); doReturn(noRevocationCheckPolicy).when(mockValidationPolicyProvider).get(); + System.setProperty("oxalis.path.conf", "MitID-dummy-CVR-97386837-conf"); + // invalidate cached system properties to force reload! + ConfigFactory.invalidateCaches(); + injector = Guice.createInjector( Modules.override( new FileSystemModule(), @@ -150,264 +125,132 @@ public class SignatureFactoryTest extends AbstractXmlTest{ bind(CloseableHttpClient.class).toInstance(mockHttpClient); bind(ValidationPolicy.class).toProvider(mockValidationPolicyProvider); + bind(CRLSource.class).toInstance(crlSourceMock); + bind(OCSPSource.class).toInstance(ocspSourceMock); } - // run with MitID cert - @Provides - @Singleton - @Named("file") - protected Config loadConfigurationFile(@Named("conf") Path homePath) { - Path configPath = homePath.resolve("nemhandel-e-delivery-oxalis.conf"); - - return Files.exists(configPath) && Files.isReadable(configPath) ? - ConfigFactory.parseFile(configPath.toFile()).resolve() : - ConfigFactory.empty(); - } - - // reload mode after config path has been overriden @Provides @Singleton - Mode provideMode(Config config, CrlFetcher crlFetcher, OcspFetcher ocspFetcher, X509Certificate certificate) { - Map objectStorage = new HashMap<>(); - objectStorage.put("ocsp_fetcher", ocspFetcher); - objectStorage.put("crlFetcher", crlFetcher); - - try { - Mode mode = ModeDetector.detect(certificate, config, objectStorage); - mode = Mockito.spy(mode); - return mode; - } catch (PeppolLoadingException e) { - throw new OxalisLoadingException("Unable to detect mode.", e); - } + SBDPayloadExtractor payloadExtractor(Provider documentBuilderProvider) { + // make payload extractor a spy to more easily simulate errors during signing process + SBDPayloadExtractor extractor = Mockito.spy(new SBDPayloadExtractorImpl(documentBuilderProvider)); + return extractor; } } )); signatureFactory = injector.getInstance(SignatureFactory.class); - service = injector.getInstance(ASiCWithXAdESService.class); - - keystore = injector.getInstance(KeyStore.class); - signatureToken = injector.getInstance(KeystoreSignatureTokenConnectionWrapper.class); - Settings settings = injector.getInstance(new Key>() {}); - - signaturePrivateKey = (KSPrivateKeyEntry) signatureToken.getKey(settings.getString(KeyStoreConf.KEY_ALIAS)); - CertificateToken signingCertificate = signaturePrivateKey.getCertificate(); - CertificateToken[] certificateChain = signaturePrivateKey.getCertificateChain(); - - - signatureParameters = new ASiCWithXAdESSignatureParameters(); - signatureParameters.bLevel().setSigningDate(new Date()); - signatureParameters.setSigningCertificate(signingCertificate); - signatureParameters.setCertificateChain(certificateChain); - signatureParameters.setSignatureLevel(SignatureLevel.XAdES_BASELINE_B); - signatureParameters.aSiC().setContainerType(ASiCContainerType.ASiC_E); - - // note: this keystore is just a contains a sample MitID FOCES3 certificate which is not used for anything other than - // unit tests - Path homePath = injector.getInstance(Key.get(Path.class, Names.named("home"))); - Path cvr97386837KeystorePath = homePath.resolve("MitID-dummy-CVR-97386837-conf/Dummy_Systemcertifikat_1_CVR-97386837.p12"); - String secondKeystorePassword = "7H+_sRI5r1jN"; - try(InputStream is = Files.newInputStream(cvr97386837KeystorePath)) { - KeyStore keystore = KeyStore.getInstance("PKCS12"); - keystore.load(is, secondKeystorePassword.toCharArray()); - innerSignatureKeystoreWrapper = new KeystoreSignatureTokenConnectionWrapper(keystore, new KeyStore.PasswordProtection(secondKeystorePassword.toCharArray())); - } - - // parameters for inner signatures for testing nested ASiC containers - innerSignaturePrivateKey = (KSPrivateKeyEntry) innerSignatureKeystoreWrapper.getKey("dummy systemcertifikat 1"); - - innerSignatureParameters = new ASiCWithXAdESSignatureParameters(); - innerSignatureParameters.bLevel().setSigningDate(new Date()); - innerSignatureParameters.setSigningCertificate(innerSignaturePrivateKey.getCertificate()); - innerSignatureParameters.setCertificateChain(innerSignaturePrivateKey.getCertificateChain()); - innerSignatureParameters.setSignatureLevel(SignatureLevel.XAdES_BASELINE_B); - innerSignatureParameters.aSiC().setContainerType(ASiCContainerType.ASiC_E); + sbdXpath = XPathFactory.newInstance().newXPath(); + MapNamespaceContext namespaceContext = new MapNamespaceContext(); + namespaceContext.addNamespace("sbd", Ns.SBDH); + sbdXpath.setNamespaceContext(namespaceContext); + } - cvr_Receiver = CertificateUtil.extractCVR(innerSignatureParameters.getSigningCertificate().getCertificate()); - cvr_Sender = CertificateUtil.extractCVR(signatureParameters.getSigningCertificate().getCertificate()); + @AfterMethod + public void cleanup() { + System.clearProperty("oxalis.path.conf"); + // invalidate cached system properties to force reload! + ConfigFactory.invalidateCaches(); } @Test - public void testErrorSigningDocumentsWhenNullOrEmptySbd() throws Exception, TransferDelegationException { - Document doc = loadAndWrapInSBD("ubl-examples/OIOUBL_Invoice_v2p2.xml"); - byte[] originalDoc = DocumentUtil.toByteArray(doc); - - TransferDelegationFactoryImpl tdf = new TransferDelegationFactoryImpl(); - byte[] transferDel = tdf.createTransferDelegation("12345678", "99999999"); - + public void testThrowsExceptionIfSBDToSignIsNullOrEmpty() throws Exception { try { - signatureFactory.signDocument("".getBytes(StandardCharsets.UTF_8), originalDoc, transferDel, new SBDMessageContext()); + signatureFactory.signDocument(null, new SBDMessageContext()); Assert.fail("Should not get here."); } catch(IllegalArgumentException ex) { Assert.assertEquals(ex.getMessage(), ErrorCodes.SIGNING_EDELIVERY_SBD_EMPTY_OR_NULL.formatErrorMessage()); } - } - - @Test - public void testErrorSigningDocumentsWhenNullOrEmptyOriginalDocument() throws Exception, TransferDelegationException { - Document doc = loadAndWrapInSBD("ubl-examples/OIOUBL_Invoice_v2p2.xml"); - byte[] sbd = DocumentUtil.toByteArray(doc); - - TransferDelegationFactoryImpl tdf = new TransferDelegationFactoryImpl(); - byte[] transferDel = tdf.createTransferDelegation("12345678", "99999999"); try { - signatureFactory.signDocument(sbd, "".getBytes(StandardCharsets.UTF_8), transferDel, new SBDMessageContext()); + signatureFactory.signDocument(new byte[0], new SBDMessageContext()); Assert.fail("Should not get here."); } catch(IllegalArgumentException ex) { - Assert.assertEquals(ex.getMessage(), ErrorCodes.SIGNING_EDELIVERY_ORIGINAL_SBD_EMPTY_OR_NULL.formatErrorMessage()); + Assert.assertEquals(ex.getMessage(), ErrorCodes.SIGNING_EDELIVERY_SBD_EMPTY_OR_NULL.formatErrorMessage()); } } - @Test - public void testOriginalDocumentCanBeNullForInternalMessageLevelResponse() throws Exception, TransferDelegationException { - Document doc = loadAndWrapInSBD("ubl-examples/OIOUBL_Invoice_v2p2.xml"); - byte[] sbd = DocumentUtil.toByteArray(doc); - - TransferDelegationFactoryImpl tdf = new TransferDelegationFactoryImpl(); - byte[] transferDel = tdf.createTransferDelegation("12345678", "99999999"); - - signatureFactory.signDocument(sbd, null, transferDel, new SBDMessageContext(true, Direction.OUT)); - } - - @Test - public void testErrorSigningDocumentsWhenNullOrEmptyTransferDelegation() throws Exception, TransferDelegationException { - Document doc = loadAndWrapInSBD("ubl-examples/OIOUBL_Invoice_v2p2.xml"); - byte[] originalDoc = DocumentUtil.toByteArray(doc); - byte[] sbd = DocumentUtil.toByteArray(doc); - - try { - signatureFactory.signDocument(sbd, originalDoc, "".getBytes(StandardCharsets.UTF_8), new SBDMessageContext()); - Assert.fail("Should not get here."); - } catch(IllegalArgumentException ex) { - Assert.assertEquals(ex.getMessage(), ErrorCodes.SIGNING_EDELIVERY_TRANSFER_DELEGATION_EMPTY_OR_NULL.formatErrorMessage()); - } + @Test(expectedExceptions = SignatureException.class) + public void testThrowsSignatureExceptionIfUnexpectedErrorOccursWhileSigning() throws Exception { + byte[] xml = loadAndWrapInSBDAsByteArray("ubl-examples/OIOUBL_Invoice_v2p2.xml"); + SBDPayloadExtractor extractor = injector.getInstance(SBDPayloadExtractor.class); // this is a Mockit Spy! + doThrow(RuntimeException.class).when(extractor).extractPayload(Mockito.any()); + signatureFactory.signDocument(xml, new SBDMessageContext()); } @Test - public void testSignDocumentCorrectlyUpdateDocumentWithAsicContainer() throws Exception, TransferDelegationException { - Document doc = loadAndWrapInSBD("ubl-examples/OIOUBL_Invoice_v2p2.xml"); - byte[] originalDoc = DocumentUtil.toByteArray(doc); - byte[] sbd = DocumentUtil.toByteArray(doc); - byte[] canonicalizedSbd = DocumentUtil.canonicalize(sbd); - - TransferDelegationFactoryImpl tdf = new TransferDelegationFactoryImpl(); - byte[] tranferDel = tdf.createTransferDelegation(cvr_Sender, cvr_Receiver); - byte[] signed = signatureFactory.signDocument(sbd, originalDoc, tranferDel, new SBDMessageContext()); - - Assert.assertTrue(signed != null || signed.length > 0); - Document signedDoc = parseDocument(signed); - - MapNamespaceContext namespaceContext = new MapNamespaceContext(); - namespaceContext.addNamespace("sbd", Ns.SBDH); - - XPath xpath = XPathFactory.newInstance().newXPath(); - xpath.setNamespaceContext(namespaceContext); - - // Checks signature count - NodeList signatures = getEdeliverySignatureNodes(signedDoc, xpath); - - MatcherAssert.assertThat(signatures, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(signatures.getLength(), CoreMatchers.equalTo(1)); - - - Node signatureNode = signatures.item(0); - String txt = signatureNode.getTextContent(); - byte[] decoded = Base64.getDecoder().decode(txt); - InMemoryDocument memDoc = new InMemoryDocument(decoded); - - ASiCWithXAdESContainerExtractor extractor = new ASiCWithXAdESContainerExtractor(memDoc); - ASiCContent content = extractor.extract(); - - List signedDocuments = content.getSignedDocuments(); - - // Checks name and content for ASiC container - DSSDocument sbdDoc = signedDocuments.stream().filter(d -> d.getName().equals(SignatureValidator.ASIC_SBD_FILENAME)).findFirst().orElse(null); - MatcherAssert.assertThat(sbdDoc, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(new String(IOUtils.toByteArray(sbdDoc.openStream()), StandardCharsets.UTF_8), CoreMatchers.equalTo(new String(canonicalizedSbd, StandardCharsets.UTF_8))); - - DSSDocument td = signedDocuments.stream().filter(d -> d.getName().equals(SignatureValidator.ASIC_TRANSFER_DELEGATION_FILENAME)).findFirst().orElse(null); - MatcherAssert.assertThat(td , CoreMatchers.notNullValue()); - MatcherAssert.assertThat(new String(IOUtils.toByteArray(td.openStream()), StandardCharsets.UTF_8), CoreMatchers.equalTo(new String(tranferDel, StandardCharsets.UTF_8))); - - DSSDocument orgSbd = signedDocuments.stream().filter(d -> d.getName().equals(SignatureValidator.ASIC_ORIGINAL_SBD_FILENAME)).findFirst().orElse(null); - MatcherAssert.assertThat(orgSbd, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(new String(IOUtils.toByteArray(orgSbd.openStream()), StandardCharsets.UTF_8), CoreMatchers.equalTo(new String(originalDoc, StandardCharsets.UTF_8))); + public void testAddsSignatureInSBDHeaderAsABusinessScope() throws Exception { + byte[] xml = loadAndWrapInSBDAsByteArray("ubl-examples/OIOUBL_Invoice_v2p2.xml"); + byte[] signed = signatureFactory.signDocument(xml, new SBDMessageContext()); + + Document sbd = parseDocument(signed); + NodeList documentSignatures = XPathUtil.evaluateXPathNodeList(sbd, + sbdXpath, + "/sbd:StandardBusinessDocument/sbd:StandardBusinessDocumentHeader/sbd:BusinessScope/sbd:Scope[sbd:Type='NEMHANDEL_EDELIVERY_SIGNATURE']"); + MatcherAssert.assertThat(documentSignatures, CoreMatchers.notNullValue()); + MatcherAssert.assertThat(documentSignatures.getLength(), CoreMatchers.equalTo(1)); + + Node scope = documentSignatures.item(0); + String signature = XPathUtil.evaluateXPath(scope, sbdXpath, "sbd:InstanceIdentifier", null); + MatcherAssert.assertThat(signature, CoreMatchers.notNullValue()); + MatcherAssert.assertThat(signature.isEmpty(), CoreMatchers.is(false)); + + String specificationVersion = XPathUtil.evaluateXPath(scope, sbdXpath, "sbd:Identifier", null); + MatcherAssert.assertThat(specificationVersion, CoreMatchers.notNullValue()); + MatcherAssert.assertThat(specificationVersion, CoreMatchers.equalTo(EDeliverySpecification.NEMHANDEL_EDELIVERY_1_2.value())); } @Test - public void testSignDocumentCorrectlyCanonicalizesBeforeSigning() throws Exception, TransferDelegationException { - DocumentBuilder builder = new DocumentBuilderProvider().get(); - Document doc; - // Non canonicalized uses different line endings in this case! - try(InputStream is = getClass().getClassLoader().getResourceAsStream("sbd-examples/SBD_OIOUBL_Invoice_unsigned_non_canonicalized.xml")) { - doc = builder.parse(is); - } - - byte[] sbd = DocumentUtil.toByteArray(doc); - byte[] originalDoc = DocumentUtil.toByteArray(doc); - - TransferDelegationFactoryImpl tdf = new TransferDelegationFactoryImpl(); - byte[] tranferDel = tdf.createTransferDelegation(cvr_Sender, cvr_Receiver); - byte[] signed = signatureFactory.signDocument(sbd, originalDoc, tranferDel, new SBDMessageContext()); - - Assert.assertTrue(signed != null || signed.length > 0); - Document signedDoc = parseDocument(signed); - - MapNamespaceContext namespaceContext = new MapNamespaceContext(); - namespaceContext.addNamespace("sbd", Ns.SBDH); - - XPath xpath = XPathFactory.newInstance().newXPath(); - xpath.setNamespaceContext(namespaceContext); - - // Checks signature count - NodeList signatures = getEdeliverySignatureNodes(signedDoc, xpath); - - MatcherAssert.assertThat(signatures, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(signatures.getLength(), CoreMatchers.equalTo(1)); - - - Node signatureNode = signatures.item(0); - String txt = signatureNode.getTextContent(); - byte[] decoded = Base64.getDecoder().decode(txt); - InMemoryDocument memDoc = new InMemoryDocument(decoded); - - ASiCWithXAdESContainerExtractor extractor = new ASiCWithXAdESContainerExtractor(memDoc); - ASiCContent content = extractor.extract(); - - List signedDocuments = content.getSignedDocuments(); - - byte[] canonicalizedOriginalSbd = DocumentUtil.canonicalize(sbd); - - MatcherAssert.assertThat("original document is not canonicalized", canonicalizedOriginalSbd, CoreMatchers.not(CoreMatchers.equalTo(sbd))); - - // Checks name and content for ASiC container - DSSDocument sbdDoc = signedDocuments.stream().filter(d -> d.getName().equals(SignatureValidator.ASIC_SBD_FILENAME)).findFirst().orElse(null); - MatcherAssert.assertThat(sbdDoc, CoreMatchers.notNullValue()); - - byte[] sbdFromAsic; - try(InputStream is = sbdDoc.openStream()){ - sbdFromAsic = IOUtils.toByteArray(is); - } - - MatcherAssert.assertThat("sbd from asic container is canonicalized", sbdFromAsic, CoreMatchers.equalTo(canonicalizedOriginalSbd)); + public void testOnlySignsCanonicalizedPayload() throws Exception { + byte[] payload = loadAsByteArray("ubl-examples/OIOUBL_Invoice_v2p2.xml"); + byte[] xml = wrapInSbd(payload); + byte[] signed = signatureFactory.signDocument(xml, new SBDMessageContext()); + + Document sbd = parseDocument(signed); + String documentSignature = XPathUtil.evaluateXPath(sbd, + sbdXpath, + "/sbd:StandardBusinessDocument/sbd:StandardBusinessDocumentHeader/sbd:BusinessScope/sbd:Scope[sbd:Type='NEMHANDEL_EDELIVERY_SIGNATURE']/sbd:InstanceIdentifier", null); + MatcherAssert.assertThat(documentSignature, CoreMatchers.notNullValue()); + + Document signature = parseDocument(Base64.getDecoder().decode(documentSignature.getBytes(StandardCharsets.UTF_8))); + XPath xadesXpath = XPathFactory.newInstance().newXPath(); + MapNamespaceContext nsCtx = new MapNamespaceContext(); + nsCtx.addNamespace("ds", "http://www.w3.org/2000/09/xmldsig#"); + xadesXpath.setNamespaceContext(nsCtx); + + String digestMethod = XPathUtil.evaluateXPath(signature, xadesXpath, "/ds:Signature/ds:SignedInfo/ds:Reference[@URI='" + SignatureValidator.XAdES_SIGNATURE_PAYLOAD_NAME +"']/ds:DigestMethod/@Algorithm", null); + String digest = XPathUtil.evaluateXPath(signature, xadesXpath, "/ds:Signature/ds:SignedInfo/ds:Reference[@URI='" + SignatureValidator.XAdES_SIGNATURE_PAYLOAD_NAME +"']/ds:DigestValue", null); + + // verify that digest matches canonicalized payload + DSSDocument canonicalizedPayload = new InMemoryDocument(DocumentUtil.canonicalize(payload)); + MatcherAssert.assertThat(digest, CoreMatchers.equalTo(canonicalizedPayload.getDigest(DigestAlgorithm.forXML(digestMethod)))); } @Test - public void testOriginalDocumentCanBeNullIfDocumentSignatureIsOptionalAndC1SendsUnsignedSBD() throws Exception, TransferDelegationException { - Document doc = loadAndWrapInSBD("ubl-examples/OIOUBL_Invoice_v2p2.xml"); - byte[] sbd = DocumentUtil.toByteArray(doc); - - TransferDelegationFactoryImpl tdf = new TransferDelegationFactoryImpl(); - byte[] transferDel = tdf.createTransferDelegation("12345678", "99999999"); - - signatureFactory.signDocument(sbd, null, transferDel, new SBDMessageContext(false, Direction.OUT, false)); + public void testCreatesAValidNemhandelEDelivery12Signature() throws Exception { + byte[] sbd = loadAndWrapInSBDAsByteArray("ubl-examples/OIOUBL_Invoice_v2p2.xml"); + byte[] signed = signatureFactory.signDocument(sbd, new SBDMessageContext()); + + Message message = createTestMessage(signed); + SignatureValidator validator = injector.getInstance(SignatureValidator.class); + ValidationResult result = validator.validate(message, new SBDMessageContext(false, Direction.IN)); + MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); + MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); + MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(0)); } - private NodeList getEdeliverySignatureNodes(Document document, XPath xpath) throws XPathExpressionException { - NodeList documentSignatures = XPathUtil.evaluateXPathNodeList(document, - xpath, - "/sbd:StandardBusinessDocument/sbd:StandardBusinessDocumentHeader/sbd:BusinessScope/sbd:Scope[sbd:Type='NEMHANDEL_EDELIVERY_SIGNATURE']/sbd:InstanceIdentifier"); - return documentSignatures; + private Message createTestMessage(byte[] data) { + Account account = new Account("test-user", "test", "DK"); + + MessageContent content = new MessageContent(data); + Message message = new Message(account, + MessageDirection.OUT, + "0184:12345678", + "0184:22334455", + "api", + UUID.randomUUID().toString(), + "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##OIOUBL-2.1::2.1", + content); + return message; } } diff --git a/src/test/java/dk/erst/oxalis/as4/validation/signature/SignatureValidationPolicyProviderTest.java b/src/test/java/dk/erst/oxalis/as4/validation/signature/SignatureValidationPolicyProviderTest.java new file mode 100644 index 0000000..995491f --- /dev/null +++ b/src/test/java/dk/erst/oxalis/as4/validation/signature/SignatureValidationPolicyProviderTest.java @@ -0,0 +1,23 @@ +package dk.erst.oxalis.as4.validation.signature; + +import eu.europa.esig.dss.enumerations.SignatureLevel; +import eu.europa.esig.dss.policy.ValidationPolicy; +import eu.europa.esig.dss.policy.jaxb.Level; +import org.hamcrest.CoreMatchers; +import org.hamcrest.MatcherAssert; +import org.testng.annotations.Test; + +import java.util.Arrays; + +public class SignatureValidationPolicyProviderTest { + + @Test + public void testPolicyRequiresXAdESBaselineB() { + ValidationPolicy policy = new SignatureValidationPolicyProvider().get(); + MatcherAssert.assertThat(policy, CoreMatchers.notNullValue()); + MatcherAssert.assertThat(policy.getSignatureConstraints(), CoreMatchers.notNullValue()); + MatcherAssert.assertThat(policy.getSignatureConstraints().getAcceptableFormats(), CoreMatchers.notNullValue()); + MatcherAssert.assertThat(policy.getSignatureConstraints().getAcceptableFormats().getId(), CoreMatchers.equalTo(Arrays.asList(SignatureLevel.XAdES_BASELINE_B.toString()))); + MatcherAssert.assertThat(policy.getSignatureConstraints().getAcceptableFormats().getLevel(), CoreMatchers.equalTo(Level.FAIL)); + } +} diff --git a/src/test/java/dk/erst/oxalis/as4/validation/signature/SignatureValidatorTest.java b/src/test/java/dk/erst/oxalis/as4/validation/signature/SignatureValidatorTest.java index 6c6efcd..3228b48 100644 --- a/src/test/java/dk/erst/oxalis/as4/validation/signature/SignatureValidatorTest.java +++ b/src/test/java/dk/erst/oxalis/as4/validation/signature/SignatureValidatorTest.java @@ -1,32 +1,39 @@ package dk.erst.oxalis.as4.validation.signature; import com.google.inject.*; -import com.google.inject.name.Named; -import com.google.inject.name.Names; import com.google.inject.util.Modules; -import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; import dk.erst.oxalis.as4.AbstractXmlTest; +import dk.erst.oxalis.as4.EDeliverySpecification; import dk.erst.oxalis.as4.config.documenttype.DocumentTypeModule; import dk.erst.oxalis.as4.error.ErrorCodes; +import dk.erst.oxalis.as4.handlers.dto.ValidationMessage; import dk.erst.oxalis.as4.handlers.dto.ValidationResult; +import dk.erst.oxalis.as4.mode.Mode; import dk.erst.oxalis.as4.persistence.model.Account; import dk.erst.oxalis.as4.persistence.model.Message; import dk.erst.oxalis.as4.persistence.model.MessageContent; import dk.erst.oxalis.as4.persistence.model.MessageDirection; -import dk.erst.oxalis.as4.util.*; +import dk.erst.oxalis.as4.util.DocumentUtil; +import dk.erst.oxalis.as4.util.SBDMessageContext; +import dk.erst.oxalis.as4.util.UtilityModule; import dk.erst.oxalis.as4.validation.ValidationModule; import eu.europa.esig.dss.asic.xades.ASiCWithXAdESSignatureParameters; import eu.europa.esig.dss.asic.xades.signature.ASiCWithXAdESService; import eu.europa.esig.dss.enumerations.ASiCContainerType; +import eu.europa.esig.dss.enumerations.MimeTypeEnum; import eu.europa.esig.dss.enumerations.SignatureLevel; -import eu.europa.esig.dss.model.*; -import eu.europa.esig.dss.model.x509.CertificateToken; +import eu.europa.esig.dss.model.DSSDocument; +import eu.europa.esig.dss.model.InMemoryDocument; +import eu.europa.esig.dss.model.SignatureValue; +import eu.europa.esig.dss.model.ToBeSigned; import eu.europa.esig.dss.policy.ValidationPolicy; -import eu.europa.esig.dss.policy.ValidationPolicyFacade; import eu.europa.esig.dss.policy.jaxb.Level; +import eu.europa.esig.dss.spi.x509.revocation.crl.CRLSource; import eu.europa.esig.dss.token.KSPrivateKeyEntry; -import network.oxalis.api.lang.OxalisLoadingException; +import eu.europa.esig.dss.validation.CertificateVerifier; +import eu.europa.esig.dss.xades.XAdESSignatureParameters; +import eu.europa.esig.dss.xades.signature.XAdESService; import network.oxalis.api.model.Direction; import network.oxalis.api.settings.Settings; import network.oxalis.commons.certvalidator.api.CrlFetcher; @@ -39,48 +46,39 @@ import network.oxalis.commons.security.CertificateModule; import network.oxalis.commons.security.KeyStoreConf; import network.oxalis.commons.tracing.TracingModule; import network.oxalis.pkix.ocsp.api.OcspFetcher; -import network.oxalis.vefa.peppol.common.lang.PeppolLoadingException; -import network.oxalis.vefa.peppol.mode.Mode; -import network.oxalis.vefa.peppol.sbdh.Ns; -import network.oxalis.vefa.peppol.security.ModeDetector; -import org.apache.cxf.helpers.MapNamespaceContext; import org.apache.http.impl.client.CloseableHttpClient; import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.w3c.dom.Document; -import org.w3c.dom.Node; import org.xml.sax.SAXException; import javax.xml.bind.JAXBException; -import javax.xml.parsers.DocumentBuilder; import javax.xml.stream.XMLStreamException; -import javax.xml.xpath.XPath; import javax.xml.xpath.XPathExpressionException; -import javax.xml.xpath.XPathFactory; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStream; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.util.*; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.UUID; import static org.mockito.Mockito.doReturn; public class SignatureValidatorTest extends AbstractXmlTest { private Injector injector; private SignatureValidator signatureValidator; - private ASiCWithXAdESService service; + private XAdESService service; @Mock private OxalisCrlFetcher mockCrlFetcher; @@ -95,28 +93,26 @@ public class SignatureValidatorTest extends AbstractXmlTest { private SignatureValidationPolicyProvider mockValidationPolicyProvider; @Mock - private CertificateVerifierProvider mockCertificateVerifierProvider; + private CRLSource mockCrlSouce; private KeyStore keystore; private KeystoreSignatureTokenConnectionWrapper signatureToken; - private ASiCWithXAdESSignatureParameters signatureParameters; + private XAdESSignatureParameters signatureParameters; private KSPrivateKeyEntry signaturePrivateKey; - private X509Certificate accessPointCertificate; - private KeystoreSignatureTokenConnectionWrapper innerSignatureKeystoreWrapper; - private ASiCWithXAdESSignatureParameters innerSignatureParameters; - private KSPrivateKeyEntry innerSignaturePrivateKey; - private String cvr_C1; - private String cvr_C2; @BeforeMethod public void setup() throws IOException, XMLStreamException, JAXBException, SAXException, KeyStoreException, CertificateException, NoSuchAlgorithmException { MockitoAnnotations.initMocks(this); // disable revocation check for our test certificates - ValidationPolicy noRevocationCheckPolicy = ValidationPolicyFacade.newFacade().getDefaultValidationPolicy(); + ValidationPolicy noRevocationCheckPolicy = new SignatureValidationPolicyProvider().get(); noRevocationCheckPolicy.getSignatureConstraints().getBasicSignatureConstraints().getSigningCertificate().getRevocationDataAvailable().setLevel(Level.IGNORE); doReturn(noRevocationCheckPolicy).when(mockValidationPolicyProvider).get(); + System.setProperty("oxalis.path.conf", "MitID-dummy-CVR-97386837-conf"); + // invalidate cached system properties to force reload! + ConfigFactory.invalidateCaches(); + injector = Guice.createInjector( Modules.override( new FileSystemModule(), @@ -135,85 +131,36 @@ public class SignatureValidatorTest extends AbstractXmlTest { bind(CrlFetcher.class).toInstance(mockCrlFetcher); bind(OcspFetcher.class).toInstance(mockOcspFetcher); bind(CloseableHttpClient.class).toInstance(mockHttpClient); + bind(CRLSource.class).toInstance(mockCrlSouce); bind(ValidationPolicy.class).toProvider(mockValidationPolicyProvider); } - // run with MitID cert @Provides @Singleton - @Named("file") - protected Config loadConfigurationFile(@Named("conf") Path homePath) { - Path configPath = homePath.resolve("nemhandel-e-delivery-oxalis.conf"); - - return Files.exists(configPath) && Files.isReadable(configPath) ? - ConfigFactory.parseFile(configPath.toFile()).resolve() : - ConfigFactory.empty(); - } - - // reload mode after config path has been overriden - @Provides - @Singleton - Mode provideMode(Config config, CrlFetcher crlFetcher, OcspFetcher ocspFetcher, X509Certificate certificate) { - Map objectStorage = new HashMap<>(); - objectStorage.put("ocsp_fetcher", ocspFetcher); - objectStorage.put("crlFetcher", crlFetcher); - - try { - Mode mode = ModeDetector.detect(certificate, config, objectStorage); - mode = Mockito.spy(mode); - return mode; - } catch (PeppolLoadingException e) { - throw new OxalisLoadingException("Unable to detect mode.", e); - } + Mode provideMode(network.oxalis.vefa.peppol.mode.Mode oxalisMode) { + Mode spy = Mockito.spy(new Mode(oxalisMode)); // override mode with a spy to easily simulated running in non-Nemhandel mode in some tests + return spy; } } )); signatureValidator = injector.getInstance(SignatureValidator.class); - service = injector.getInstance(ASiCWithXAdESService.class); + service = injector.getInstance(XAdESService.class); keystore = injector.getInstance(KeyStore.class); signatureToken = injector.getInstance(KeystoreSignatureTokenConnectionWrapper.class); Settings settings = injector.getInstance(new Key>() {}); signaturePrivateKey = (KSPrivateKeyEntry) signatureToken.getKey(settings.getString(KeyStoreConf.KEY_ALIAS)); - CertificateToken signingCertificate = signaturePrivateKey.getCertificate(); - CertificateToken[] certificateChain = signaturePrivateKey.getCertificateChain(); - - - signatureParameters = new ASiCWithXAdESSignatureParameters(); - signatureParameters.bLevel().setSigningDate(new Date()); - signatureParameters.setSigningCertificate(signingCertificate); - signatureParameters.setCertificateChain(certificateChain); - signatureParameters.setSignatureLevel(SignatureLevel.XAdES_BASELINE_B); - signatureParameters.aSiC().setContainerType(ASiCContainerType.ASiC_E); - - accessPointCertificate = injector.getInstance(X509Certificate.class); - - // note: this keystore is just a contains a sample MitID FOCES3 certificate which is not used for anything other than - // unit tests - Path homePath = injector.getInstance(Key.get(Path.class, Names.named("home"))); - Path cvr97386837KeystorePath = homePath.resolve("MitID-dummy-CVR-97386837-conf/Dummy_Systemcertifikat_1_CVR-97386837.p12"); - String secondKeystorePassword = "7H+_sRI5r1jN"; - try(InputStream is = Files.newInputStream(cvr97386837KeystorePath)) { - KeyStore keystore = KeyStore.getInstance("PKCS12"); - keystore.load(is, secondKeystorePassword.toCharArray()); - innerSignatureKeystoreWrapper = new KeystoreSignatureTokenConnectionWrapper(keystore, new KeyStore.PasswordProtection(secondKeystorePassword.toCharArray())); - } + signatureParameters = injector.getProvider(XAdESSignatureParameters.class).get(); + } - // parameters for inner signatures for testing nested ASiC containers - innerSignaturePrivateKey = (KSPrivateKeyEntry) innerSignatureKeystoreWrapper.getKey("dummy systemcertifikat 1"); - - innerSignatureParameters = new ASiCWithXAdESSignatureParameters(); - innerSignatureParameters.bLevel().setSigningDate(new Date()); - innerSignatureParameters.setSigningCertificate(innerSignaturePrivateKey.getCertificate()); - innerSignatureParameters.setCertificateChain(innerSignaturePrivateKey.getCertificateChain()); - innerSignatureParameters.setSignatureLevel(SignatureLevel.XAdES_BASELINE_B); - innerSignatureParameters.aSiC().setContainerType(ASiCContainerType.ASiC_E); - - cvr_C1 = CertificateUtil.extractCVR(innerSignatureParameters.getSigningCertificate().getCertificate()); - cvr_C2 = CertificateUtil.extractCVR(signatureParameters.getSigningCertificate().getCertificate()); + @AfterMethod + public void cleanup() { + System.clearProperty("oxalis.path.conf"); + // invalidate cached system properties to force reload! + ConfigFactory.invalidateCaches(); } @Test @@ -221,312 +168,206 @@ public class SignatureValidatorTest extends AbstractXmlTest { Message message = createTestInvoiceMessage(loadAndWrapInSBDAsByteArray("ubl-examples/OIOUBL_Invoice_v2p2.xml")); SBDMessageContext msgCtx = new SBDMessageContext(false, Direction.OUT); ValidationResult result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.SENDER_EDELIVERY_SIGNATURE_MISSING.formatErrorMessage(0))); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); + assertValidationResultContainsError(result, + ErrorCodes.SENDER_EDELIVERY_SIGNATURE_MISSING.getErrorCode(), + ErrorCodes.SENDER_EDELIVERY_SIGNATURE_MISSING.formatErrorMessage(0), + SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH); msgCtx.setDirection(Direction.IN); result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.RECEIVER_EDELIVERY_SIGNATURE_MISSING.formatErrorMessage(0))); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); + assertValidationResultContainsError(result, + ErrorCodes.RECEIVER_EDELIVERY_SIGNATURE_MISSING.getErrorCode(), + ErrorCodes.RECEIVER_EDELIVERY_SIGNATURE_MISSING.formatErrorMessage(0), + SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH); } @Test - public void testValidationFailsIfSignatureIsNotValidASiCContainer_invalidSignature() throws Exception { - Document doc = loadAndWrapInSBD("ubl-examples/OIOUBL_Invoice_v2p2.xml"); - byte[] xml = toByteArray(doc); - byte[] delegation = createValidTransferDelegation().getBytes(StandardCharsets.UTF_8); + public void testValidationFailsIfTooManyBusinessScopeWithSignatureIsPresent() throws Exception { + byte[] payload = loadAsByteArray("ubl-examples/OIOUBL_Invoice_v2p2.xml"); + Document sbd = wrapInSBDAsDocument(payload); - InMemoryDocument sbd = new InMemoryDocument(xml, SignatureValidator.ASIC_SBD_FILENAME, MimeType.XML); - InMemoryDocument td = new InMemoryDocument(delegation, SignatureValidator.ASIC_TRANSFER_DELEGATION_FILENAME, MimeType.XML); + InMemoryDocument unsigned = new InMemoryDocument(DocumentUtil.canonicalize(payload), SignatureValidator.XAdES_SIGNATURE_PAYLOAD_NAME, MimeTypeEnum.XML); + DSSDocument signature = createXAdESSignature(unsigned, signatureParameters, signatureToken, signaturePrivateKey); + addSignatureToSBD(sbd, signature, EDeliverySpecification.NEMHANDEL_EDELIVERY_1_2); - List documentsToSign = Arrays.asList(sbd, td); - ToBeSigned dataToSign = service.getDataToSign(documentsToSign, signatureParameters); - SignatureValue signatureValue = signatureToken.sign(dataToSign, signatureParameters.getDigestAlgorithm(), signaturePrivateKey); - signatureValue.setValue("foo".getBytes(StandardCharsets.UTF_8)); // invalid signature value - DSSDocument asicContainer = service.signDocument(documentsToSign, signatureParameters, signatureValue); - addASiCContainerToDocument(doc, asicContainer); + unsigned = new InMemoryDocument(DocumentUtil.canonicalize(payload), SignatureValidator.XAdES_SIGNATURE_PAYLOAD_NAME, MimeTypeEnum.XML); + signature = createXAdESSignature(unsigned, signatureParameters, signatureToken, signaturePrivateKey); + addSignatureToSBD(sbd, signature, EDeliverySpecification.NEMHANDEL_EDELIVERY_1_2); - Message message = createTestInvoiceMessage(toByteArray(doc)); + Message message = createTestInvoiceMessage(DocumentUtil.toByteArray(sbd)); SBDMessageContext msgCtx = new SBDMessageContext(false, Direction.OUT); ValidationResult result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.SENDER_EDELIVERY_SIGNATURE_INVALID.getErrorCode())); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); + assertValidationResultContainsError(result, + ErrorCodes.SENDER_EDELIVERY_TOO_MANY_SIGNATURES.getErrorCode(), + ErrorCodes.SENDER_EDELIVERY_TOO_MANY_SIGNATURES.formatErrorMessage(2), + SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH); msgCtx.setDirection(Direction.IN); result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.RECEIVER_EDELIVERY_SIGNATURE_INVALID.getErrorCode())); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); + assertValidationResultContainsError(result, + ErrorCodes.RECEIVER_EDELIVERY_TOO_MANY_SIGNATURES.getErrorCode(), + ErrorCodes.RECEIVER_EDELIVERY_TOO_MANY_SIGNATURES.formatErrorMessage(2), + SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH); } @Test - public void testValidationFailsIfTransferDelegationIsNotPresent() throws Exception { - Document doc = loadAndWrapInSBD("ubl-examples/OIOUBL_Invoice_v2p2.xml"); - byte[] xml = toByteArray(doc); - - InMemoryDocument sbd = new InMemoryDocument(xml, SignatureValidator.ASIC_SBD_FILENAME, MimeType.XML); - DSSDocument asicContainer = createAsicContainer(Arrays.asList(sbd), signatureParameters, signatureToken, signaturePrivateKey); - addASiCContainerToDocument(doc, asicContainer); + public void testAcceptsValidSignature() throws Exception { + byte[] payload = loadAsByteArray("ubl-examples/OIOUBL_Invoice_v2p2.xml"); + Document doc = wrapInSBDAsDocument(payload); + InMemoryDocument unsigned = new InMemoryDocument(DocumentUtil.canonicalize(payload), SignatureValidator.XAdES_SIGNATURE_PAYLOAD_NAME, MimeTypeEnum.XML); + DSSDocument signature = createXAdESSignature(unsigned, signatureParameters, signatureToken, signaturePrivateKey); + addSignatureToSBD(doc, signature, EDeliverySpecification.NEMHANDEL_EDELIVERY_1_2); Message message = createTestInvoiceMessage(toByteArray(doc)); SBDMessageContext msgCtx = new SBDMessageContext(false, Direction.OUT); ValidationResult result = signatureValidator.validate(message, msgCtx); MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.SENDER_EDELIVERY_MISSING_TRANSFER_DELEGATION.formatErrorMessage(0))); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); - - msgCtx.setDirection(Direction.IN); - result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.RECEIVER_EDELIVERY_MISSING_TRANSFER_DELEGATION.formatErrorMessage(0))); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); + MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(0)); } @Test - public void testValidationFailsIfSBDIsNotPartOfSignature() throws Exception { - Document doc = loadAndWrapInSBD("ubl-examples/OIOUBL_Invoice_v2p2.xml"); - byte[] delegation = createValidTransferDelegation().getBytes(StandardCharsets.UTF_8); + public void testValidationFailsIfSignatureIsNotValidXAdESSignature_invalidSignature() throws Exception { + byte[] payload = loadAsByteArray("ubl-examples/OIOUBL_Invoice_v2p2.xml"); + Document sbd = wrapInSBDAsDocument(payload); - InMemoryDocument td = new InMemoryDocument(delegation, SignatureValidator.ASIC_TRANSFER_DELEGATION_FILENAME, MimeType.XML); - DSSDocument asicContainer = createAsicContainer(Arrays.asList(td), signatureParameters, signatureToken, signaturePrivateKey); - addASiCContainerToDocument(doc, asicContainer); + InMemoryDocument unsigned = new InMemoryDocument(DocumentUtil.canonicalize(payload), SignatureValidator.XAdES_SIGNATURE_PAYLOAD_NAME, MimeTypeEnum.XML); + ToBeSigned dataToSign = service.getDataToSign(unsigned, signatureParameters); + SignatureValue signatureValue = signatureToken.sign(dataToSign, signatureParameters.getDigestAlgorithm(), signaturePrivateKey); + signatureValue.setValue("foo".getBytes(StandardCharsets.UTF_8)); // invalid signature value + DSSDocument signature = service.signDocument(unsigned, signatureParameters, signatureValue); + addSignatureToSBD(sbd, signature, EDeliverySpecification.NEMHANDEL_EDELIVERY_1_2); - Message message = createTestInvoiceMessage(toByteArray(doc)); + Message message = createTestInvoiceMessage(toByteArray(sbd)); SBDMessageContext msgCtx = new SBDMessageContext(false, Direction.OUT); ValidationResult result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.SENDER_EDELIVERY_MISSING_SBD_IN_ASIC.formatErrorMessage(0))); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); + assertValidationResultContainsError(result, + ErrorCodes.SENDER_EDELIVERY_SIGNATURE_INVALID.getErrorCode(), + ErrorCodes.SENDER_EDELIVERY_SIGNATURE_INVALID.formatErrorMessage("BBB_CV_ISI_ANS: The signature is not intact!"), + SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH); msgCtx.setDirection(Direction.IN); result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.RECEIVER_EDELIVERY_MISSING_SBD_IN_ASIC.formatErrorMessage(0))); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); + assertValidationResultContainsError(result, + ErrorCodes.RECEIVER_EDELIVERY_SIGNATURE_INVALID.getErrorCode(), + ErrorCodes.RECEIVER_EDELIVERY_SIGNATURE_INVALID.formatErrorMessage("BBB_CV_ISI_ANS: The signature is not intact!"), + SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH); } @Test - public void testValidationFailsIfOriginalSbdInAsicContainerIsNotSBD() throws Exception { - Document doc = loadAndWrapInSBD("ubl-examples/OIOUBL_Invoice_v2p2.xml"); - byte[] xml = toByteArray(doc); - byte[] delegation = createValidTransferDelegation().getBytes(StandardCharsets.UTF_8); + public void testValidationFailsIfSignatureIsNotAXAdESSignature() throws Exception { + byte[] payload = loadAsByteArray("ubl-examples/OIOUBL_Invoice_v2p2.xml"); + Document sbd = wrapInSBDAsDocument(payload); - InMemoryDocument sbd = new InMemoryDocument(xml, SignatureValidator.ASIC_SBD_FILENAME, MimeType.XML); - InMemoryDocument original = new InMemoryDocument("".getBytes(StandardCharsets.UTF_8), SignatureValidator.ASIC_ORIGINAL_SBD_FILENAME, MimeType.XML); - InMemoryDocument td = new InMemoryDocument(delegation, SignatureValidator.ASIC_TRANSFER_DELEGATION_FILENAME, MimeType.XML); - DSSDocument asicContainer = createAsicContainer(Arrays.asList(sbd, original, td), signatureParameters, signatureToken, signaturePrivateKey); - addASiCContainerToDocument(doc, asicContainer); + appendNemhandelSignatureScopeNode(sbd, "".getBytes(StandardCharsets.UTF_8), EDeliverySpecification.NEMHANDEL_EDELIVERY_1_2); - Message message = createTestInvoiceMessage(toByteArray(doc)); + Message message = createTestInvoiceMessage(toByteArray(sbd)); SBDMessageContext msgCtx = new SBDMessageContext(false, Direction.OUT); ValidationResult result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.SENDER_EDELIVERY_ORIGINAL_SBD_IN_ASIC_IS_NOT_SBD.formatErrorMessage(0))); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); + assertValidationResultContainsError(result, + ErrorCodes.SENDER_EDELIVERY_SIGNATURE_INVALID.getErrorCode(), + ErrorCodes.SENDER_EDELIVERY_SIGNATURE_INVALID.formatErrorMessage("Signature is not in XAdES format or contains no signatures"), + SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH); msgCtx.setDirection(Direction.IN); result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.RECEIVER_EDELIVERY_ORIGINAL_SBD_IN_ASIC_IS_NOT_SBD.formatErrorMessage(0))); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); + assertValidationResultContainsError(result, + ErrorCodes.RECEIVER_EDELIVERY_SIGNATURE_INVALID.getErrorCode(), + ErrorCodes.RECEIVER_EDELIVERY_SIGNATURE_INVALID.formatErrorMessage("Signature is not in XAdES format or contains no signatures"), + SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH); } @Test - public void testAcceptsValidSignature() throws Exception { - Document doc = loadAndWrapInSBD("ubl-examples/OIOUBL_Invoice_v2p2.xml"); - byte[] xml = toByteArray(doc); - byte[] delegation = createValidTransferDelegation().getBytes(StandardCharsets.UTF_8); + public void testValidationFailsIfSignatureIsNotXML() throws Exception { + byte[] payload = loadAsByteArray("ubl-examples/OIOUBL_Invoice_v2p2.xml"); + Document sbd = wrapInSBDAsDocument(payload); - InMemoryDocument sbd = new InMemoryDocument(xml, SignatureValidator.ASIC_SBD_FILENAME, MimeType.XML); - InMemoryDocument td = new InMemoryDocument(delegation, SignatureValidator.ASIC_TRANSFER_DELEGATION_FILENAME, MimeType.XML); - DSSDocument asicContainer = createAsicContainer(Arrays.asList(sbd, td), signatureParameters, signatureToken, signaturePrivateKey); - addASiCContainerToDocument(doc, asicContainer); + appendNemhandelSignatureScopeNode(sbd, "foo".getBytes(StandardCharsets.UTF_8), EDeliverySpecification.NEMHANDEL_EDELIVERY_1_2); - Message message = createTestInvoiceMessage(toByteArray(doc)); + Message message = createTestInvoiceMessage(toByteArray(sbd)); SBDMessageContext msgCtx = new SBDMessageContext(false, Direction.OUT); ValidationResult result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(0)); - } - - @Test - public void testAcceptsValidRecursivelyNestedAsicContainers() throws Exception { - Document doc = loadAndWrapInSBD("ubl-examples/OIOUBL_Invoice_v2p2.xml"); - byte[] xml = toByteArray(doc); - byte[] delegation = createTransferDelegation(cvr_C1, cvr_C2).getBytes(StandardCharsets.UTF_8); - - - InMemoryDocument sbd = new InMemoryDocument(xml, SignatureValidator.ASIC_SBD_FILENAME, MimeType.XML); - InMemoryDocument td = new InMemoryDocument(delegation, SignatureValidator.ASIC_TRANSFER_DELEGATION_FILENAME, MimeType.XML); - DSSDocument asicContainer = createAsicContainer(Arrays.asList(sbd, td), innerSignatureParameters, innerSignatureKeystoreWrapper, innerSignaturePrivateKey); - addASiCContainerToDocument(doc, asicContainer); + assertValidationResultContainsError(result, + ErrorCodes.SENDER_EDELIVERY_SIGNATURE_INVALID.getErrorCode(), + ErrorCodes.SENDER_EDELIVERY_SIGNATURE_INVALID.formatErrorMessage("Signature is not in XAdES format or contains no signatures"), + SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH); - byte[] document1 = toByteArray(doc); - - // Cx translates the document and resigns - byte[] translatedXml = new String(xml, StandardCharsets.UTF_8).replace("A00095678", "" + UUID.randomUUID().toString() + "").getBytes(StandardCharsets.UTF_8); - byte[] delegation2 = createTransferDelegation(cvr_C2, cvr_C2).getBytes(StandardCharsets.UTF_8); // we assume here that C2 and C3 have the same CVR so we can settle for using two keystores - doc = parseDocument(translatedXml); - - sbd = new InMemoryDocument(translatedXml, SignatureValidator.ASIC_SBD_FILENAME, MimeType.XML); - DSSDocument original = new InMemoryDocument(document1, SignatureValidator.ASIC_ORIGINAL_SBD_FILENAME, MimeType.XML); - td = new InMemoryDocument(delegation2, SignatureValidator.ASIC_TRANSFER_DELEGATION_FILENAME, MimeType.XML); - DSSDocument asicContainer2 = createAsicContainer(Arrays.asList(sbd, original, td), signatureParameters, signatureToken, signaturePrivateKey); - addASiCContainerToDocument(doc, asicContainer2); - - Message message = createTestInvoiceMessage(toByteArray(doc)); - SBDMessageContext msgCtx = new SBDMessageContext(false, Direction.OUT); - ValidationResult result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(0)); + msgCtx.setDirection(Direction.IN); + result = signatureValidator.validate(message, msgCtx); + assertValidationResultContainsError(result, + ErrorCodes.RECEIVER_EDELIVERY_SIGNATURE_INVALID.getErrorCode(), + ErrorCodes.RECEIVER_EDELIVERY_SIGNATURE_INVALID.formatErrorMessage("Signature is not in XAdES format or contains no signatures"), + SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH); } @Test - public void testAcceptsValidNonSaxonFormattedRecursivelyNestedAsicContainers() throws Exception { - DocumentBuilder builder = new DocumentBuilderProvider().get(); - Document doc; - // Non canonicalized uses different line endings in this case! - try(InputStream is = getClass().getClassLoader().getResourceAsStream("sbd-examples/SBD_OIOUBL_Invoice_signed_non_canonicalized.xml")) { - doc = builder.parse(is); - } + public void testValidationFailsIfSignatureIsASICFormat() throws Exception { + byte[] payload = loadAsByteArray("ubl-examples/OIOUBL_Invoice_v2p2.xml"); + Document sbd = wrapInSBDAsDocument(payload); - byte[] originalSbd = toByteArray(doc); - byte[] canonicalizedSbd = DocumentUtil.canonicalize(originalSbd); + ASiCWithXAdESService asicService = new ASiCWithXAdESService(injector.getInstance(CertificateVerifier.class)); - // Sanity check that original is not canonicalized already, because this implies the canonicalization implemented - // in the validator is actually handling canonicalization as intended. - MatcherAssert.assertThat("original document should not be canonicalized already", originalSbd, CoreMatchers.not(CoreMatchers.equalTo(canonicalizedSbd))); + ASiCWithXAdESSignatureParameters asicParameters = new ASiCWithXAdESSignatureParameters(); + asicParameters.bLevel().setSigningDate(new Date()); + asicParameters.setSigningCertificate(signaturePrivateKey.getCertificate()); + asicParameters.setCertificateChain(signaturePrivateKey.getCertificateChain()); + asicParameters.setSignatureLevel(SignatureLevel.XAdES_BASELINE_B); + asicParameters.aSiC().setContainerType(ASiCContainerType.ASiC_E); - Message message = createTestInvoiceMessage(originalSbd); - SBDMessageContext msgCtx = new SBDMessageContext(false, Direction.OUT); - ValidationResult result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(0)); - } + InMemoryDocument unsigned = new InMemoryDocument(DocumentUtil.canonicalize(payload), SignatureValidator.XAdES_SIGNATURE_PAYLOAD_NAME, MimeTypeEnum.XML); - @Test - public void testRejectsInvalidRecursivelyNestedAsicContainers() throws Exception { - // create inner, invalid asic - Document doc = loadAndWrapInSBD("ubl-examples/OIOUBL_Invoice_v2p2.xml"); - byte[] xml = toByteArray(doc); - byte[] delegation = createTransferDelegation(cvr_C1, cvr_C2).getBytes(StandardCharsets.UTF_8); - - InMemoryDocument sbd = new InMemoryDocument(xml, SignatureValidator.ASIC_SBD_FILENAME, MimeType.XML); - InMemoryDocument td = new InMemoryDocument(delegation, SignatureValidator.ASIC_TRANSFER_DELEGATION_FILENAME, MimeType.XML); - - List documentsToSign = Arrays.asList(sbd, td); - ToBeSigned dataToSign = service.getDataToSign(documentsToSign, innerSignatureParameters); - SignatureValue signatureValue = innerSignatureKeystoreWrapper.sign(dataToSign, innerSignatureParameters.getDigestAlgorithm(), innerSignaturePrivateKey); - signatureValue.setValue("foo".getBytes(StandardCharsets.UTF_8)); // invalid signature value - DSSDocument asicContainer = service.signDocument(documentsToSign, innerSignatureParameters, signatureValue); - addASiCContainerToDocument(doc, asicContainer); - byte[] originalSbd = toByteArray(doc); - - // Cx translates the document and re-signs - byte[] translatedXml = new String(xml, StandardCharsets.UTF_8).replace("A00095678", "" + UUID.randomUUID().toString() + "").getBytes(StandardCharsets.UTF_8); - byte[] delegation2 = createTransferDelegation(cvr_C2, cvr_C2).getBytes(StandardCharsets.UTF_8); // normally this would be C2 -> C3 the test will fail for other reasons - doc = parseDocument(translatedXml); + ToBeSigned dataToSign = asicService.getDataToSign(unsigned, asicParameters); + SignatureValue signatureValue = signatureToken.sign(dataToSign, asicParameters.getDigestAlgorithm(), signaturePrivateKey); + DSSDocument signedDocument = asicService.signDocument(unsigned, asicParameters, signatureValue); + addSignatureToSBD(sbd, signedDocument, EDeliverySpecification.NEMHANDEL_EDELIVERY_1_2); - sbd = new InMemoryDocument(translatedXml, SignatureValidator.ASIC_SBD_FILENAME, MimeType.XML); - DSSDocument original = new InMemoryDocument(originalSbd, SignatureValidator.ASIC_ORIGINAL_SBD_FILENAME, MimeType.XML); - td = new InMemoryDocument(delegation2, SignatureValidator.ASIC_TRANSFER_DELEGATION_FILENAME, MimeType.XML); - DSSDocument asicContainer2 = createAsicContainer(Arrays.asList(sbd, original, td), signatureParameters, signatureToken, signaturePrivateKey); - addASiCContainerToDocument(doc, asicContainer2); - - Message message = createTestInvoiceMessage(toByteArray(doc)); + Message message = createTestInvoiceMessage(toByteArray(sbd)); SBDMessageContext msgCtx = new SBDMessageContext(false, Direction.OUT); ValidationResult result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.SENDER_EDELIVERY_SIGNATURE_INVALID.formatErrorMessage(1, ""))); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); + assertValidationResultContainsError(result, + ErrorCodes.SENDER_EDELIVERY_SIGNATURE_INVALID.getErrorCode(), + ErrorCodes.SENDER_EDELIVERY_SIGNATURE_INVALID.formatErrorMessage("Signature is not in XAdES format or contains no signatures"), + SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH); msgCtx.setDirection(Direction.IN); result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.RECEIVER_EDELIVERY_SIGNATURE_INVALID.formatErrorMessage(1, ""))); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); + assertValidationResultContainsError(result, + ErrorCodes.RECEIVER_EDELIVERY_SIGNATURE_INVALID.getErrorCode(), + ErrorCodes.RECEIVER_EDELIVERY_SIGNATURE_INVALID.formatErrorMessage("Signature is not in XAdES format or contains no signatures"), + SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH); } @Test - public void testValidationFailsIfSBDDoesMatchSBDInASiCContainer() throws Exception { - Document doc = loadAndWrapInSBD("ubl-examples/OIOUBL_Invoice_v2p2.xml"); - byte[] xml = toByteArray(doc); - byte[] delegation = createValidTransferDelegation().getBytes(StandardCharsets.UTF_8); - - InMemoryDocument sbd = new InMemoryDocument(xml, SignatureValidator.ASIC_SBD_FILENAME, MimeType.XML); - InMemoryDocument td = new InMemoryDocument(delegation, SignatureValidator.ASIC_TRANSFER_DELEGATION_FILENAME, MimeType.XML); - DSSDocument asicContainer = createAsicContainer(Arrays.asList(sbd, td), signatureParameters, signatureToken, signaturePrivateKey); - addASiCContainerToDocument(doc, asicContainer); - byte[] original = toByteArray(doc); - - // change some node so they no longer match - Document altered = parseDocument(original); - MapNamespaceContext namespaceContext = new MapNamespaceContext(); - namespaceContext.addNamespace("sbd", Ns.SBDH); - namespaceContext.addNamespace("inv", "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"); - namespaceContext.addNamespace("cbc", "urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"); - - XPath xpath = XPathFactory.newInstance().newXPath(); - xpath.setNamespaceContext(namespaceContext); - - Node node = XPathUtil.evaluateXPathNode(altered, - xpath, - "/sbd:StandardBusinessDocument/inv:Invoice/cbc:ID"); - node.setTextContent(UUID.randomUUID().toString()); - - Message message = createTestInvoiceMessage(toByteArray(altered)); + public void testValidationFailsIfPayloadIsNotPartOfSignature() throws Exception { + byte[] payload = loadAsByteArray("ubl-examples/OIOUBL_Invoice_v2p2.xml"); + Document doc = wrapInSBDAsDocument(payload); + + InMemoryDocument unsigned = new InMemoryDocument("foo".getBytes(StandardCharsets.UTF_8), "foo", MimeTypeEnum.TEXT); + DSSDocument signature = createXAdESSignature(unsigned, signatureParameters, signatureToken, signaturePrivateKey); + addSignatureToSBD(doc, signature, EDeliverySpecification.NEMHANDEL_EDELIVERY_1_2); + + Message message = createTestInvoiceMessage(toByteArray(doc)); SBDMessageContext msgCtx = new SBDMessageContext(false, Direction.OUT); ValidationResult result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.SENDER_EDELIVERY_SBD_DOES_NOT_MATCH_ASIC.formatErrorMessage(0))); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); + assertValidationResultContainsError(result, + ErrorCodes.SENDER_EDELIVERY_SIGNATURE_INVALID.getErrorCode(), + ErrorCodes.SENDER_EDELIVERY_SIGNATURE_INVALID.formatErrorMessage("BBB_CV_IRDOI_ANS: The reference data object is not intact!"), + SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH); msgCtx.setDirection(Direction.IN); result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.RECEIVER_EDELIVERY_SBD_DOES_NOT_MATCH_ASIC.formatErrorMessage(0))); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); + assertValidationResultContainsError(result, + ErrorCodes.RECEIVER_EDELIVERY_SIGNATURE_INVALID.getErrorCode(), + ErrorCodes.RECEIVER_EDELIVERY_SIGNATURE_INVALID.formatErrorMessage("BBB_CV_IRDOI_ANS: The reference data object is not intact!"), + SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH); } @Test public void testOnlyValidatesSignatureInNemhandelEdeliveryMode() throws Exception { Mode mode = injector.getInstance(Mode.class); - doReturn("TEST").when(mode).getIdentifier(); // simulate peppol test mode + doReturn(false).when(mode).isNemhandelEdeliveryMode(); // simulate non-Nemhandel mode + Message message = createTestInvoiceMessage(loadAndWrapInSBDAsByteArray("ubl-examples/OIOUBL_Invoice_v2p2.xml")); SBDMessageContext msgCtx = new SBDMessageContext(false, Direction.OUT); @@ -537,337 +378,141 @@ public class SignatureValidatorTest extends AbstractXmlTest { } @Test - public void testValidationFailsIfFirstLevelReceiverDoesNotMatchAPCvr() throws Exception { - String wrongReceiverCVR = "99887766"; - Document doc = loadAndWrapInSBD("ubl-examples/OIOUBL_Invoice_v2p2.xml"); - byte[] xml = toByteArray(doc); - byte[] delegation = createTransferDelegation(cvr_C2, wrongReceiverCVR).getBytes(StandardCharsets.UTF_8); - - InMemoryDocument sbd = new InMemoryDocument(xml, SignatureValidator.ASIC_SBD_FILENAME, MimeType.XML); - InMemoryDocument td = new InMemoryDocument(delegation, SignatureValidator.ASIC_TRANSFER_DELEGATION_FILENAME, MimeType.XML); - DSSDocument asicContainer = createAsicContainer(Arrays.asList(sbd, td), signatureParameters, signatureToken, signaturePrivateKey); - addASiCContainerToDocument(doc, asicContainer); - - Message message = createTestInvoiceMessage(toByteArray(doc)); - SBDMessageContext msgCtx = new SBDMessageContext(false, Direction.OUT); + public void testAcceptsUnsignedDocumentIfDocumentSignatureIsOptional() throws Exception { + Message message = createTestInvoiceMessage(loadAndWrapInSBDAsByteArray("ubl-examples/OIOUBL_Invoice_v2p2.xml")); + SBDMessageContext msgCtx = new SBDMessageContext(false, Direction.OUT, false); ValidationResult result = signatureValidator.validate(message, msgCtx); MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.SENDER_EDELIVERY_TRANSFER_DELEGATION_INVALID_RECEIVER_CVR.formatErrorMessage(wrongReceiverCVR, cvr_C2, 0))); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); - - msgCtx.setDirection(Direction.IN); - result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.RECEIVER_EDELIVERY_TRANSFER_DELEGATION_INVALID_RECEIVER_CVR.formatErrorMessage(wrongReceiverCVR, cvr_C2, 0))); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); + MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(0)); } @Test - public void testValidationFailsIfTransferDelegationReceiverSchemeIsNotDKCVR() throws Exception { - Document doc = loadAndWrapInSBD("ubl-examples/OIOUBL_Invoice_v2p2.xml"); - byte[] xml = toByteArray(doc); - String delegationString = createValidTransferDelegation(); - byte[] delegation = delegationString.replace("", "").getBytes(StandardCharsets.UTF_8); - - InMemoryDocument sbd = new InMemoryDocument(xml, SignatureValidator.ASIC_SBD_FILENAME, MimeType.XML); - InMemoryDocument td = new InMemoryDocument(delegation, SignatureValidator.ASIC_TRANSFER_DELEGATION_FILENAME, MimeType.XML); - DSSDocument asicContainer = createAsicContainer(Arrays.asList(sbd, td), signatureParameters, signatureToken, signaturePrivateKey); - addASiCContainerToDocument(doc, asicContainer); - - Message message = createTestInvoiceMessage(toByteArray(doc)); - SBDMessageContext msgCtx = new SBDMessageContext(false, Direction.OUT); + public void testRejectsUnsignedDocumentInC3() throws Exception { + Message message = createTestInvoiceMessage(loadAndWrapInSBDAsByteArray("ubl-examples/OIOUBL_Invoice_v2p2.xml")); + SBDMessageContext msgCtx = new SBDMessageContext(false, Direction.IN, true); ValidationResult result = signatureValidator.validate(message, msgCtx); MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.SENDER_EDELIVERY_TRANSFER_DELEGATION_INVALID_RECEIVER_SCHEME.formatErrorMessage("foo", "DK:CVR", 0))); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); - - msgCtx.setDirection(Direction.IN); - result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.RECEIVER_EDELIVERY_TRANSFER_DELEGATION_INVALID_RECEIVER_SCHEME.formatErrorMessage("foo", "DK:CVR", 0))); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); + assertValidationResultContainsError(result, + ErrorCodes.RECEIVER_EDELIVERY_SIGNATURE_MISSING.getErrorCode(), + ErrorCodes.RECEIVER_EDELIVERY_SIGNATURE_MISSING.formatErrorMessage(), + SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH); } @Test - public void testValidationFailsIfTransferDelegationSenderSchemeIsNotDKCVR() throws Exception { - Document doc = loadAndWrapInSBD("ubl-examples/OIOUBL_Invoice_v2p2.xml"); - byte[] xml = toByteArray(doc); - String delegationString = createValidTransferDelegation(); - byte[] delegation = delegationString.replace("", "").getBytes(StandardCharsets.UTF_8); - - InMemoryDocument sbd = new InMemoryDocument(xml, SignatureValidator.ASIC_SBD_FILENAME, MimeType.XML); - InMemoryDocument td = new InMemoryDocument(delegation, SignatureValidator.ASIC_TRANSFER_DELEGATION_FILENAME, MimeType.XML); - DSSDocument asicContainer = createAsicContainer(Arrays.asList(sbd, td), signatureParameters, signatureToken, signaturePrivateKey); - addASiCContainerToDocument(doc, asicContainer); - - Message message = createTestInvoiceMessage(toByteArray(doc)); - SBDMessageContext msgCtx = new SBDMessageContext(false, Direction.OUT); - ValidationResult result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.SENDER_EDELIVERY_TRANSFER_DELEGATION_INVALID_SENDER_SCHEME.formatErrorMessage("foo", "DK:CVR", 0))); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); - - msgCtx.setDirection(Direction.IN); - result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.RECEIVER_EDELIVERY_TRANSFER_DELEGATION_INVALID_SENDER_SCHEME.formatErrorMessage("foo", "DK:CVR", 0))); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); - } + public void testRejectsInvalidSignatureIfPresentEvenIfSignatureIsOptional() throws Exception { + byte[] payload = loadAsByteArray("ubl-examples/OIOUBL_Invoice_v2p2.xml"); + Document doc = wrapInSBDAsDocument(payload); - @Test - public void testValidationFailsForTransferDelegationIfSenderCVRDoesNotMatchCVRFromSigningCertificate() throws Exception { - Document doc = loadAndWrapInSBD("ubl-examples/OIOUBL_Invoice_v2p2.xml"); - byte[] xml = toByteArray(doc); - byte[] delegation = createTransferDelegation("12345678", cvr_C2).getBytes(StandardCharsets.UTF_8); // assume C2 and C3 has same cvr + InMemoryDocument unsigned = new InMemoryDocument(DocumentUtil.canonicalize(payload), SignatureValidator.XAdES_SIGNATURE_PAYLOAD_NAME, MimeTypeEnum.XML); - InMemoryDocument sbd = new InMemoryDocument(xml, SignatureValidator.ASIC_SBD_FILENAME, MimeType.XML); - InMemoryDocument td = new InMemoryDocument(delegation, SignatureValidator.ASIC_TRANSFER_DELEGATION_FILENAME, MimeType.XML); - DSSDocument asicContainer = createAsicContainer(Arrays.asList(sbd, td), signatureParameters, signatureToken, signaturePrivateKey); - addASiCContainerToDocument(doc, asicContainer); + ToBeSigned dataToSign = service.getDataToSign(unsigned, signatureParameters); + SignatureValue signatureValue = signatureToken.sign(dataToSign, signatureParameters.getDigestAlgorithm(), signaturePrivateKey); + signatureValue.setValue("foo".getBytes(StandardCharsets.UTF_8)); // invalid signature value + DSSDocument signature = service.signDocument(unsigned, signatureParameters, signatureValue); + addSignatureToSBD(doc, signature, EDeliverySpecification.NEMHANDEL_EDELIVERY_1_2); Message message = createTestInvoiceMessage(toByteArray(doc)); - SBDMessageContext msgCtx = new SBDMessageContext(false, Direction.OUT); + SBDMessageContext msgCtx = new SBDMessageContext(false, Direction.OUT, false); ValidationResult result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.SENDER_EDELIVERY_TRANSFER_DELEGATION_INVALID_SENDER_CVR.formatErrorMessage("12345678", cvr_C2, 0))); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); - - msgCtx.setDirection(Direction.IN); - result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.RECEIVER_EDELIVERY_TRANSFER_DELEGATION_INVALID_SENDER_CVR.formatErrorMessage("12345678", cvr_C2, 0))); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); + assertValidationResultContainsError(result, + ErrorCodes.SENDER_EDELIVERY_SIGNATURE_INVALID.getErrorCode(), + ErrorCodes.SENDER_EDELIVERY_SIGNATURE_INVALID.formatErrorMessage("BBB_CV_ISI_ANS: The signature is not intact!"), + SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH); } @Test - public void testValidationFailsIfTransferDelegationSenderIsNotSpecified() throws Exception { - Document doc = loadAndWrapInSBD("ubl-examples/OIOUBL_Invoice_v2p2.xml"); - byte[] xml = toByteArray(doc); - String delegationString = createValidTransferDelegation(); - byte[] delegation = delegationString.replaceAll("(.*)", "").getBytes(StandardCharsets.UTF_8); + public void testExpectsDocumentToBeCanonicalizedBeforeSigning() throws Exception { + byte[] payload = loadAsByteArray("ubl-examples/OIOUBL_Invoice_v2p2.xml"); + Document doc = wrapInSBDAsDocument(payload); - InMemoryDocument sbd = new InMemoryDocument(xml, SignatureValidator.ASIC_SBD_FILENAME, MimeType.XML); - InMemoryDocument td = new InMemoryDocument(delegation, SignatureValidator.ASIC_TRANSFER_DELEGATION_FILENAME, MimeType.XML); - DSSDocument asicContainer = createAsicContainer(Arrays.asList(sbd, td), signatureParameters, signatureToken, signaturePrivateKey); - addASiCContainerToDocument(doc, asicContainer); + // dont canonicalize document before signing + InMemoryDocument unsigned = new InMemoryDocument(payload, SignatureValidator.XAdES_SIGNATURE_PAYLOAD_NAME, MimeTypeEnum.XML); + DSSDocument signature = createXAdESSignature(unsigned, signatureParameters, signatureToken, signaturePrivateKey); + addSignatureToSBD(doc, signature, EDeliverySpecification.NEMHANDEL_EDELIVERY_1_2); Message message = createTestInvoiceMessage(toByteArray(doc)); SBDMessageContext msgCtx = new SBDMessageContext(false, Direction.OUT); ValidationResult result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.SENDER_EDELIVERY_TRANSFER_DELEGATION_MISSING_SENDER_CVR.formatErrorMessage( 0))); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); + assertValidationResultContainsError(result, + ErrorCodes.SENDER_EDELIVERY_SIGNATURE_INVALID.getErrorCode(), // signature validation will fail because it will "compare" against a canonicalized value + ErrorCodes.SENDER_EDELIVERY_SIGNATURE_INVALID.formatErrorMessage("BBB_CV_IRDOI_ANS: The reference data object is not intact!"), + SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH); msgCtx.setDirection(Direction.IN); result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.RECEIVER_EDELIVERY_TRANSFER_DELEGATION_MISSING_SENDER_CVR.formatErrorMessage( 0))); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); + assertValidationResultContainsError(result, + ErrorCodes.RECEIVER_EDELIVERY_SIGNATURE_INVALID.getErrorCode(), + ErrorCodes.RECEIVER_EDELIVERY_SIGNATURE_INVALID.formatErrorMessage("BBB_CV_IRDOI_ANS: The reference data object is not intact!"), + SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH); } @Test - public void testValidationFailsIfTransferDelegationReceiverIsNotSpecified() throws Exception { - Document doc = loadAndWrapInSBD("ubl-examples/OIOUBL_Invoice_v2p2.xml"); - byte[] xml = toByteArray(doc); - String delegationString = createValidTransferDelegation(); - byte[] delegation = delegationString.replaceAll("(.*)", "").getBytes(StandardCharsets.UTF_8); - - InMemoryDocument sbd = new InMemoryDocument(xml, SignatureValidator.ASIC_SBD_FILENAME, MimeType.XML); - InMemoryDocument td = new InMemoryDocument(delegation, SignatureValidator.ASIC_TRANSFER_DELEGATION_FILENAME, MimeType.XML); - DSSDocument asicContainer = createAsicContainer(Arrays.asList(sbd, td), signatureParameters, signatureToken, signaturePrivateKey); - addASiCContainerToDocument(doc, asicContainer); - - Message message = createTestInvoiceMessage(toByteArray(doc)); - SBDMessageContext msgCtx = new SBDMessageContext(false, Direction.OUT); - ValidationResult result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.SENDER_EDELIVERY_TRANSFER_DELEGATION_MISSING_RECEIVER_CVR.formatErrorMessage( 0))); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); - - msgCtx.setDirection(Direction.IN); - result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.RECEIVER_EDELIVERY_TRANSFER_DELEGATION_MISSING_RECEIVER_CVR.formatErrorMessage( 0))); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); + public void testRejectsSignatureIfBusinessScopeDoesNotContainSpecificationVersion() throws Exception { + testRejectsSignatureForInvalidSpecificationVersionInBusinessScope(null); } - /** - * Note: This test makes sure the receiver cvr matches the signing certificate from the previous recursion level: - * Example: C1 signs SBD with transfer-delegation C1 -> C2. C2 makes transfer-delegation C2-C3 and resigns document which gives the following (simplified) nested structure: - * - C2 SBD - * - ASiC-E container (C2's certificate) - * - transfer-delegation.xml: C2 -> C3 - * - original-standard-business-document.xml (C1's SBD) - * - ASiC-E container (C1's certificate) - * - transfer-delegation.xml: C1 -> C2 - * - ... - * Must verify that C1's transfer-delegation receiver CVR matches CVR in the certificate from the ASiC-E container "above" - i.e. C2's ASiC-E container - */ @Test - public void testRejectsRecursivelyNestedAsicContainersIfTransferDelegationReceiverCVRDoesNotMatchCVRFromSigningCertificateForPreviousRecursionLevel() throws Exception { - String wrongReceiverCVR = "99887766"; - Document doc = loadAndWrapInSBD("ubl-examples/OIOUBL_Invoice_v2p2.xml"); - byte[] xml = toByteArray(doc); - byte[] delegation = createTransferDelegation(cvr_C1, wrongReceiverCVR).getBytes(StandardCharsets.UTF_8); // wrong receiver CVR - - - InMemoryDocument sbd = new InMemoryDocument(xml, SignatureValidator.ASIC_SBD_FILENAME, MimeType.XML); - InMemoryDocument td = new InMemoryDocument(delegation, SignatureValidator.ASIC_TRANSFER_DELEGATION_FILENAME, MimeType.XML); - DSSDocument asicContainer = createAsicContainer(Arrays.asList(sbd, td), innerSignatureParameters, innerSignatureKeystoreWrapper, innerSignaturePrivateKey); - addASiCContainerToDocument(doc, asicContainer); - - byte[] document1 = toByteArray(doc); + public void testRejectsSignatureIfBusinessScopeDoesContainUnsupportedSpecificationVersion() throws Exception { + testRejectsSignatureForInvalidSpecificationVersionInBusinessScope("foo"); + } - // Cx translates the document and resigns - byte[] translatedXml = new String(xml, StandardCharsets.UTF_8).replace("A00095678", "" + UUID.randomUUID().toString() + "").getBytes(StandardCharsets.UTF_8); - byte[] delegation2 = createTransferDelegation(cvr_C2, cvr_C2).getBytes(StandardCharsets.UTF_8); // we assume here that C2 and C3 have the same CVR so we can settle for using two keystores - doc = parseDocument(translatedXml); + private void testRejectsSignatureForInvalidSpecificationVersionInBusinessScope(String spec) throws Exception { + byte[] payload = loadAsByteArray("ubl-examples/OIOUBL_Invoice_v2p2.xml"); + Document doc = wrapInSBDAsDocument(payload); - sbd = new InMemoryDocument(translatedXml, SignatureValidator.ASIC_SBD_FILENAME, MimeType.XML); - DSSDocument original = new InMemoryDocument(document1, SignatureValidator.ASIC_ORIGINAL_SBD_FILENAME, MimeType.XML); - td = new InMemoryDocument(delegation2, SignatureValidator.ASIC_TRANSFER_DELEGATION_FILENAME, MimeType.XML); - DSSDocument asicContainer2 = createAsicContainer(Arrays.asList(sbd, original, td), signatureParameters, signatureToken, signaturePrivateKey); - addASiCContainerToDocument(doc, asicContainer2); + InMemoryDocument unsigned = new InMemoryDocument(DocumentUtil.canonicalize(payload), SignatureValidator.XAdES_SIGNATURE_PAYLOAD_NAME, MimeTypeEnum.XML); + DSSDocument signature = createXAdESSignature(unsigned, signatureParameters, signatureToken, signaturePrivateKey); + addSignatureToSBD(doc, signature, spec); Message message = createTestInvoiceMessage(toByteArray(doc)); SBDMessageContext msgCtx = new SBDMessageContext(false, Direction.OUT); ValidationResult result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.SENDER_EDELIVERY_TRANSFER_DELEGATION_INVALID_RECEIVER_CVR.formatErrorMessage(wrongReceiverCVR, cvr_C2, 1))); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); + List specList = Arrays.asList(EDeliverySpecification.NEMHANDEL_EDELIVERY_1_2.value()); + assertValidationResultContainsError(result, + ErrorCodes.SENDER_EDELIVERY_SIGNATURE_INVALID_SPECIFICATION_VERSION.getErrorCode(), + ErrorCodes.SENDER_EDELIVERY_SIGNATURE_INVALID_SPECIFICATION_VERSION.formatErrorMessage(specList), + SignatureValidatorImpl.EDELIVERY_SIGNATURE_SPEC_VERSION_XPATH); msgCtx.setDirection(Direction.IN); result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.RECEIVER_EDELIVERY_TRANSFER_DELEGATION_INVALID_RECEIVER_CVR.formatErrorMessage(wrongReceiverCVR, cvr_C2, 1))); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); + assertValidationResultContainsError(result, + ErrorCodes.RECEIVER_EDELIVERY_SIGNATURE_INVALID_SPECIFICATION_VERSION.getErrorCode(), + ErrorCodes.RECEIVER_EDELIVERY_SIGNATURE_INVALID_SPECIFICATION_VERSION.formatErrorMessage(specList), + SignatureValidatorImpl.EDELIVERY_SIGNATURE_SPEC_VERSION_XPATH); } - @Test - public void testValidationFailsIfOriginalStandardBusinessDocumentDoesNotContainASiCContainer() throws Exception { - Document doc = loadAndWrapInSBD("ubl-examples/OIOUBL_Invoice_v2p2.xml"); - byte[] xml = toByteArray(doc); - // no asic container in nested SBD - - // Cx translates the document and resigns - byte[] translatedXml = new String(xml, StandardCharsets.UTF_8).replace("A00095678", "" + UUID.randomUUID().toString() + "").getBytes(StandardCharsets.UTF_8); - byte[] delegation2 = createTransferDelegation(cvr_C2, cvr_C2).getBytes(StandardCharsets.UTF_8); // we assume here that C2 and C3 have the same CVR so we can settle for using two keystores - doc = parseDocument(translatedXml); - - DSSDocument sbd = new InMemoryDocument(translatedXml, SignatureValidator.ASIC_SBD_FILENAME, MimeType.XML); - DSSDocument original = new InMemoryDocument(xml, SignatureValidator.ASIC_ORIGINAL_SBD_FILENAME, MimeType.XML); - DSSDocument td = new InMemoryDocument(delegation2, SignatureValidator.ASIC_TRANSFER_DELEGATION_FILENAME, MimeType.XML); - DSSDocument asicContainer2 = createAsicContainer(Arrays.asList(sbd, original, td), signatureParameters, signatureToken, signaturePrivateKey); - addASiCContainerToDocument(doc, asicContainer2); - - Message message = createTestInvoiceMessage(toByteArray(doc)); - SBDMessageContext msgCtx = new SBDMessageContext(false, Direction.OUT); - ValidationResult result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.SENDER_EDELIVERY_SIGNATURE_MISSING.formatErrorMessage(1))); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); - - msgCtx.setDirection(Direction.IN); - result = signatureValidator.validate(message, msgCtx); + private void assertValidationResultContainsError(ValidationResult result, String errorCode, String errorMessage, String lineReference) { MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.RECEIVER_EDELIVERY_SIGNATURE_MISSING.formatErrorMessage(1))); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); - } - @Test - public void testAcceptsUnsignedDocumentIfDocumentSignatureIsOptional() throws Exception { - Message message = createTestInvoiceMessage(loadAndWrapInSBDAsByteArray("ubl-examples/OIOUBL_Invoice_v2p2.xml")); - SBDMessageContext msgCtx = new SBDMessageContext(false, Direction.OUT, false); - ValidationResult result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(0)); + ValidationMessage msg = result.getErrors().stream() + .filter(m -> errorCode.equals(m.getCode())) + .findFirst() + .orElse(null); + MatcherAssert.assertThat("should contain error code " + errorCode, msg, CoreMatchers.notNullValue()); + MatcherAssert.assertThat(msg.getMessage(), CoreMatchers.equalTo(errorMessage)); + MatcherAssert.assertThat(msg.getLineReference(), CoreMatchers.equalTo(lineReference)); } - @Test - public void testRejectsInvalidSignatureIfPresentEvenIfSignatureIsOptional() throws Exception { - Document doc = loadAndWrapInSBD("ubl-examples/OIOUBL_Invoice_v2p2.xml"); - byte[] xml = toByteArray(doc); - byte[] delegation = createValidTransferDelegation().getBytes(StandardCharsets.UTF_8); - - InMemoryDocument sbd = new InMemoryDocument(xml, SignatureValidator.ASIC_SBD_FILENAME, MimeType.XML); - InMemoryDocument td = new InMemoryDocument(delegation, SignatureValidator.ASIC_TRANSFER_DELEGATION_FILENAME, MimeType.XML); - - List documentsToSign = Arrays.asList(sbd, td); - ToBeSigned dataToSign = service.getDataToSign(documentsToSign, signatureParameters); - SignatureValue signatureValue = signatureToken.sign(dataToSign, signatureParameters.getDigestAlgorithm(), signaturePrivateKey); - signatureValue.setValue("foo".getBytes(StandardCharsets.UTF_8)); // invalid signature value - DSSDocument asicContainer = service.signDocument(documentsToSign, signatureParameters, signatureValue); - addASiCContainerToDocument(doc, asicContainer); - - Message message = createTestInvoiceMessage(toByteArray(doc)); - SBDMessageContext msgCtx = new SBDMessageContext(false, Direction.OUT, false); - ValidationResult result = signatureValidator.validate(message, msgCtx); - MatcherAssert.assertThat(result, CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors(), CoreMatchers.notNullValue()); - MatcherAssert.assertThat(result.getErrors().size(), CoreMatchers.equalTo(1)); - MatcherAssert.assertThat(result.getErrors().get(0).getMessage(), CoreMatchers.containsString(ErrorCodes.SENDER_EDELIVERY_SIGNATURE_INVALID.getErrorCode())); - MatcherAssert.assertThat(result.getErrors().get(0).getLineReference(), CoreMatchers.containsString(SignatureValidatorImpl.EDELIVERY_SIGNATURE_XPATH)); + private void addSignatureToSBD(Document doc, DSSDocument signature, EDeliverySpecification spec) throws IOException, XPathExpressionException { + addSignatureToSBD(doc, signature, spec != null ? spec.value() : null); } - - private void addASiCContainerToDocument(Document doc, DSSDocument asicContainer) throws IOException, XPathExpressionException { + private void addSignatureToSBD(Document doc, DSSDocument signature, String spec) throws IOException, XPathExpressionException { try(ByteArrayOutputStream bos = new ByteArrayOutputStream()) { - asicContainer.writeTo(bos); - appendNemhandelSignatureScopeNode(doc, bos.toByteArray()); + signature.writeTo(bos); + appendNemhandelSignatureScopeNode(doc, bos.toByteArray(), spec); } } - private DSSDocument createAsicContainer(List documentsToSign, ASiCWithXAdESSignatureParameters signatureParameters, KeystoreSignatureTokenConnectionWrapper token, KSPrivateKeyEntry signaturePrivateKey) throws IOException { - ToBeSigned dataToSign = service.getDataToSign(documentsToSign, signatureParameters); - SignatureValue signatureValue = token.sign(dataToSign, signatureParameters.getDigestAlgorithm(), signaturePrivateKey); - DSSDocument signedDocument = service.signDocument(documentsToSign, signatureParameters, signatureValue); + private DSSDocument createXAdESSignature(DSSDocument unsigned, XAdESSignatureParameters signatureParameters, KeystoreSignatureTokenConnectionWrapper signatureToken, KSPrivateKeyEntry signaturePrivateKey) { + ToBeSigned dataToSign = service.getDataToSign(unsigned, signatureParameters); + SignatureValue signatureValue = signatureToken.sign(dataToSign, signatureParameters.getDigestAlgorithm(), signaturePrivateKey); + DSSDocument signedDocument = service.signDocument(unsigned, signatureParameters, signatureValue); return signedDocument; } - private String createValidTransferDelegation() { - return createTransferDelegation(cvr_C2, cvr_C2); // assume C2 and C3 are same oxalis AP for testing purposes so we need fewer keystores - } - - private String createTransferDelegation(String fromCvr, String toCvr) { - String xml = "\n" + - "\n" + - " " + fromCvr + "\n" + - " " + toCvr + "\n" + - ""; - return xml; - } private Message createTestInvoiceMessage(byte[] data) { Account account = new Account("test-user", "test", "DK"); diff --git a/src/test/java/dk/erst/oxalis/as4/validation/signature/TransferDelegationTest.java b/src/test/java/dk/erst/oxalis/as4/validation/signature/TransferDelegationTest.java deleted file mode 100644 index e47a2aa..0000000 --- a/src/test/java/dk/erst/oxalis/as4/validation/signature/TransferDelegationTest.java +++ /dev/null @@ -1,63 +0,0 @@ -package dk.erst.oxalis.as4.validation.signature; -import dk.erst.oxalis.as4.error.ErrorCodes; -import dk.erst.oxalis.as4.signature.TransferDelegationException; -import dk.erst.oxalis.as4.signature.TransferDelegationFactoryImpl; -import org.testng.Assert; -import org.testng.annotations.Test; - -import javax.xml.bind.JAXBException; -import java.nio.charset.StandardCharsets; - -import static org.junit.Assert.assertThrows; - -public class TransferDelegationTest { - @Test - public void testTransferDelegationContainsCorrectOutput() throws JAXBException, TransferDelegationException { - String senderCVR = "12345678"; - String receiverCVR = "99999999"; - TransferDelegationFactoryImpl tdf = new TransferDelegationFactoryImpl(); - String transferDelegationDocument = new String(tdf.createTransferDelegation(senderCVR, receiverCVR), StandardCharsets.UTF_8); - - String expectedTransferDelegation = "\n" + - "\n" + - " " + senderCVR + "\n" + - " " + receiverCVR + "\n" + - "\n"; - - Assert.assertEquals(transferDelegationDocument, expectedTransferDelegation); - } - - @Test - public void testTransferDelegationEmptyOrNullSenderCvr() throws JAXBException { - String senderCVR = null; - String receiverCVR = "99999999"; - TransferDelegationFactoryImpl tdf = new TransferDelegationFactoryImpl(); - - try { - Assert.assertThrows(TransferDelegationException.class, () -> { - tdf.createTransferDelegation(senderCVR, receiverCVR); - }); - tdf.createTransferDelegation(senderCVR, receiverCVR); - Assert.fail("Should not get here"); - } catch(TransferDelegationException ex) { - Assert.assertEquals(ex.getMessage(), ErrorCodes.SIGNING_EDELIVERY_TRANSFER_DELEGATION_EMPTY_OR_NULL_SENDER_CVR.formatErrorMessage()); - } - } - - @Test - public void testTransferDelegationEmptyOrNullReceiverCvr() throws JAXBException { - String senderCVR = "12345678"; - String receiverCVR = ""; - TransferDelegationFactoryImpl tdf = new TransferDelegationFactoryImpl(); - - try { - Assert.assertThrows(TransferDelegationException.class, () -> { - tdf.createTransferDelegation(senderCVR, receiverCVR); - }); - tdf.createTransferDelegation(senderCVR, receiverCVR); - Assert.fail("Should not get here"); - } catch(TransferDelegationException ex) { - Assert.assertEquals(ex.getMessage(), ErrorCodes.SIGNING_EDELIVERY_TRANSFER_DELEGATION_EMPTY_OR_NULL_RECEIVER_CVR.formatErrorMessage()); - } - } -} diff --git a/src/test/java/dk/erst/oxalis/as4/validation/version/VersionValidatorTest.java b/src/test/java/dk/erst/oxalis/as4/validation/version/VersionValidatorTest.java index 5cb94be..c34c20b 100644 --- a/src/test/java/dk/erst/oxalis/as4/validation/version/VersionValidatorTest.java +++ b/src/test/java/dk/erst/oxalis/as4/validation/version/VersionValidatorTest.java @@ -21,6 +21,7 @@ import network.oxalis.commons.config.ConfigModule; import network.oxalis.commons.filesystem.FileSystemModule; import network.oxalis.vefa.peppol.lookup.LookupClient; import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; import org.joda.time.LocalDate; import org.junit.After; import org.junit.Before; @@ -36,6 +37,7 @@ import org.junit.Test; import java.nio.charset.StandardCharsets; import java.util.List; +import java.util.TimeZone; import java.util.UUID; import static org.hamcrest.CoreMatchers.containsString; @@ -50,7 +52,7 @@ import static org.testng.Assert.assertTrue; @PrepareForTest(OxalisAS4Version.class) public class VersionValidatorTest extends AbstractXmlTest { private Injector injector; - private VersionValidator versionValidator; + private VersionValidatorImpl versionValidator; private final DateTime YEAR_2000 = convertToStartOfDayDate(new LocalDate(2000, 1, 1)); private final DateTime YEAR_3000 = convertToStartOfDayDate(new LocalDate(3000, 1, 1)); @@ -419,6 +421,75 @@ public class VersionValidatorTest extends AbstractXmlTest { } + @Test + public void testGetSoonOutOfDateTextUTCtoCPHWinterTime() { + String text= versionValidator.getSoonOutOfDateText(new DateTime(2023, 12, 9, 13, 0, 0, DateTimeZone.forTimeZone(TimeZone.getTimeZone("UTC"))), + "test"); + assertEquals(text, + "\n" + + "###############################################################################\n" + + "# test is soon to be out of date! #\n" + + "###############################################################################\n" + + "# #\n" + + "# On Saturday, Dec 09, 2023 14:00:00 this test #\n" + + "# will be obsolete! #\n" + + "# Please make sure you have upgraded before then #\n" + + "# as by then it can no longer serve to receive or send documents. #\n" + + "# #\n" + + "# Find the newest version of test: #\n" + + "# #\n" + + "# https://rep.erst.dk/git/openebusiness/nemhandeledelivery/oxalis #\n" + + "# https://rep.erst.dk/git/openebusiness/nemhandeledelivery/oxalis-as4 #\n" + + "# #\n" + + "###############################################################################"); + } + + @Test + public void testGetSoonOutOfDateTextUTCtoCPHSummerTime() { + String text= versionValidator.getSoonOutOfDateText(new DateTime(2023, 6, 9, 13, 0, 0, DateTimeZone.forTimeZone(TimeZone.getTimeZone("UTC"))), + "test"); + assertEquals(text, + "\n" + + "###############################################################################\n" + + "# test is soon to be out of date! #\n" + + "###############################################################################\n" + + "# #\n" + + "# On Friday, Jun 09, 2023 15:00:00 this test #\n" + + "# will be obsolete! #\n" + + "# Please make sure you have upgraded before then #\n" + + "# as by then it can no longer serve to receive or send documents. #\n" + + "# #\n" + + "# Find the newest version of test: #\n" + + "# #\n" + + "# https://rep.erst.dk/git/openebusiness/nemhandeledelivery/oxalis #\n" + + "# https://rep.erst.dk/git/openebusiness/nemhandeledelivery/oxalis-as4 #\n" + + "# #\n" + + "###############################################################################"); + } + + @Test + public void testGetSoonOutOfDateTextCPHtoCPHTime() { + String text= versionValidator.getSoonOutOfDateText(new DateTime(2023, 12, 9, 13, 0, 0, DateTimeZone.forTimeZone(TimeZone.getTimeZone("Europe/Copenhagen"))), + "test"); + assertEquals(text, + "\n" + + "###############################################################################\n" + + "# test is soon to be out of date! #\n" + + "###############################################################################\n" + + "# #\n" + + "# On Saturday, Dec 09, 2023 13:00:00 this test #\n" + + "# will be obsolete! #\n" + + "# Please make sure you have upgraded before then #\n" + + "# as by then it can no longer serve to receive or send documents. #\n" + + "# #\n" + + "# Find the newest version of test: #\n" + + "# #\n" + + "# https://rep.erst.dk/git/openebusiness/nemhandeledelivery/oxalis #\n" + + "# https://rep.erst.dk/git/openebusiness/nemhandeledelivery/oxalis-as4 #\n" + + "# #\n" + + "###############################################################################"); + } + private Message createTestInvoiceMessage() { Account account = new Account("test-user", "test", "DK"); diff --git a/src/test/java/dk/erst/oxalis/as4/validation/version/model/EdelComponentVersionListTest.java b/src/test/java/dk/erst/oxalis/as4/validation/version/model/EdelComponentVersionListTest.java index 2a39d4b..88fdf8e 100644 --- a/src/test/java/dk/erst/oxalis/as4/validation/version/model/EdelComponentVersionListTest.java +++ b/src/test/java/dk/erst/oxalis/as4/validation/version/model/EdelComponentVersionListTest.java @@ -7,8 +7,6 @@ import org.testng.annotations.Test; import javax.xml.bind.JAXBContext; import javax.xml.bind.Unmarshaller; import java.io.StringReader; -import java.time.ZoneId; -import java.util.Calendar; import java.util.List; import java.util.TimeZone; @@ -17,26 +15,27 @@ import static org.junit.Assert.assertEquals; public class EdelComponentVersionListTest { @Test public void testUnmarshal() throws Exception { + JodaTimeAdapter jodaTimeAdapter = new JodaTimeAdapter(); String xml = " \n" + " \n" + - " 2023-12-24T23:59:59\n" + + " 2023-12-24T23:59:59+01:00\n" + " Foo\n" + " 1\n" + " http://nemhandel.dk/foo\n" + " some text\n" + " true\n" + - " 2023-10-24T12:30:45\n" + + " 2023-10-24T12:30:45+02:00\n" + " 1.0.0\n" + " \n" + " \n" + - " 2023-12-24T23:59:59\n" + + " 2023-12-24T23:59:59+01:00\n" + " Bar\n" + " 2\n" + " http://nemhandel.dk/bar\n" + " some text\n" + " true\n" + - " 2023-10-24T12:30:45\n" + + " 2023-10-24T12:30:45+02:00\n" + " 2.0.0\n" + " \n" + " "; @@ -48,33 +47,30 @@ public class EdelComponentVersionListTest { List componentVersions = componentVersionList.getEdelComponentVersion(); assertEquals(2, componentVersions.size()); - Calendar cal = Calendar.getInstance(); - cal.setTimeZone(TimeZone.getTimeZone(ZoneId.of("UTC"))); - cal.set(Calendar.MILLISECOND, 0); - cal.set(2023, Calendar.DECEMBER, 24, 23, 59, 59); - DateTime dateTimeDecember = new DateTime(cal.getTimeInMillis(), DateTimeZone.UTC); - cal.set(2023, Calendar.OCTOBER, 24, 12, 30, 45); - DateTime dateTimeOctober = new DateTime(cal.getTimeInMillis(), DateTimeZone.UTC); + DateTime dateTimeDecember = new DateTime(2023, 12, 24, 23, 59, 59, DateTimeZone.forTimeZone(TimeZone.getTimeZone("Europe/Copenhagen"))); + + DateTime dateTimeOctober = new DateTime(2023, 10, 24, 12, 30, 45, DateTimeZone.forTimeZone(TimeZone.getTimeZone("Europe/Copenhagen"))); + EdelComponentVersion version1 = componentVersions.get(0); - assertEquals(dateTimeDecember, version1.getBlockedDate()); + assertEquals(jodaTimeAdapter.marshal(dateTimeDecember), jodaTimeAdapter.marshal(version1.getBlockedDate())); assertEquals("Foo", version1.getComponentName()); assertEquals(1L, version1.getId().longValue()); assertEquals("http://nemhandel.dk/foo", version1.getInfoLink()); assertEquals("some text", version1.getInfoText()); assertEquals(true, version1.isMandatory()); - assertEquals(dateTimeOctober, version1.getThresholdDate()); + assertEquals(jodaTimeAdapter.marshal(dateTimeOctober), jodaTimeAdapter.marshal(version1.getThresholdDate())); assertEquals("1.0.0", version1.getVersion()); EdelComponentVersion version2 = componentVersions.get(1); - assertEquals(dateTimeDecember, version2.getBlockedDate()); + assertEquals(jodaTimeAdapter.marshal(dateTimeDecember), jodaTimeAdapter.marshal(version2.getBlockedDate())); assertEquals("Bar", version2.getComponentName()); assertEquals(2L, version2.getId().longValue()); assertEquals("http://nemhandel.dk/bar", version2.getInfoLink()); assertEquals("some text", version2.getInfoText()); assertEquals(true, version2.isMandatory()); - assertEquals(dateTimeOctober, version2.getThresholdDate()); + assertEquals(jodaTimeAdapter.marshal(dateTimeOctober), jodaTimeAdapter.marshal(version2.getThresholdDate())); assertEquals("2.0.0", version2.getVersion()); } } diff --git a/src/test/resources/cef-sbd-with-identifiers.xml b/src/test/resources/cef-sbd-with-identifiers.xml new file mode 100644 index 0000000..b9eecfd --- /dev/null +++ b/src/test/resources/cef-sbd-with-identifiers.xml @@ -0,0 +1,250 @@ + + + + 1.0 + + + + urn:oasis:names:tc:ebcore:partyid-type:unregistered:C1 + + + + + urn:oasis:names:tc:ebcore:partyid-type:unregistered:C4 + + + + + + NONE + 1.0 + 555bcb4c-940b-4694-9b90-d9b0ae1e937b + CEF Connectivity test + 2019-10-30T11:20:05.304+02:00 + + + + + + + DOCUMENTID + + + e-delivery + submitMessage + + + + + + PROCESSID + + e-delivery + + http://ec.europa.eu/edelivery/services/connectivity-service + + + + COUNTRY_C1 + DK + + + + + 2.1 + OIOUBL-2.1 + urn:www.nesubl.eu:profiles:profile5:ver2.0 + A00095678 + false + 555bcb4c-940b-4694-9b90-d9b0ae1e937b + 2005-11-20 + 380 + DKK + 5250124502 + + 5002701 + 9756b468-8815-1029-857a-e388fe63f399 + 2005-11-01 + + + + DK16356706 + + DK16356706 + + + Tavleverandøren HANS + + + StructuredDK + Leverandørvej + 11 + Dyssegård + 2870 + + DK + + + + DK16356706 + + 63 + Moms + + + + Tavleleverandøren + DK16356706 + + + 23456 + Hugo Jensen + 15812337 + Hugo@tavl.dk + + + + + + 5798009811639 + + 5798009811639 + + + Den Lille Skole + + + StructuredDK + Fredericiavej + 10 + Helsingør + 3000 + + DK + + + + 7778 + Hans Hansen + 26532147 + Hans@dls.dk + + + + + 2005-11-15 + + + 1 + 42 + 2005-11-25 + DK:BANK + + 1234567890 + A00095678 + + 1234 + + + + + 1 + 1 + 6312.50 + + + 1262.50 + + 5050.00 + 1262.50 + + StandardRated + 25 + + 63 + Moms + + + + + + 5050.00 + 1262.50 + 6312.50 + 6312.50 + + + 1 + 1.00 + 5000.00 + + 1 + + + 1250.00 + + 5000.00 + 1250.00 + + StandardRated + 25 + + 63 + Moms + + + + + + Hejsetavle + Hejsetavle + + 5712345780121 + + + + 5000.00 + 1 + 1 + + + + 2 + 2.00 + 50.00 + + 2 + + + 12.50 + + 50.00 + 12.50 + + StandardRated + 25 + + 63 + Moms + + + + + + Beslag + Beslag + + 5712345780111 + + + + 25.00 + 1 + 1 + + + + \ No newline at end of file diff --git a/src/test/resources/cef-sbd-without-identifiers.xml b/src/test/resources/cef-sbd-without-identifiers.xml new file mode 100644 index 0000000..11bf74e --- /dev/null +++ b/src/test/resources/cef-sbd-without-identifiers.xml @@ -0,0 +1,249 @@ + + + + 1.0 + + + + urn:oasis:names:tc:ebcore:partyid-type:unregistered:C1 + + + + + urn:oasis:names:tc:ebcore:partyid-type:unregistered:C4 + + + + + + NONE + 1.0 + 555bcb4c-940b-4694-9b90-d9b0ae1e937b + CEF Connectivity test + 2019-10-30T11:20:05.304+02:00 + + + + + + + DOCUMENTID + + + + submitMessage + + + + + + PROCESSID + + + http://ec.europa.eu/edelivery/services/connectivity-service + + + + COUNTRY_C1 + DK + + + + + 2.1 + OIOUBL-2.1 + urn:www.nesubl.eu:profiles:profile5:ver2.0 + A00095678 + false + 555bcb4c-940b-4694-9b90-d9b0ae1e937b + 2005-11-20 + 380 + DKK + 5250124502 + + 5002701 + 9756b468-8815-1029-857a-e388fe63f399 + 2005-11-01 + + + + DK16356706 + + DK16356706 + + + Tavleverandøren HANS + + + StructuredDK + Leverandørvej + 11 + Dyssegård + 2870 + + DK + + + + DK16356706 + + 63 + Moms + + + + Tavleleverandøren + DK16356706 + + + 23456 + Hugo Jensen + 15812337 + Hugo@tavl.dk + + + + + + 5798009811639 + + 5798009811639 + + + Den Lille Skole + + + StructuredDK + Fredericiavej + 10 + Helsingør + 3000 + + DK + + + + 7778 + Hans Hansen + 26532147 + Hans@dls.dk + + + + + 2005-11-15 + + + 1 + 42 + 2005-11-25 + DK:BANK + + 1234567890 + A00095678 + + 1234 + + + + + 1 + 1 + 6312.50 + + + 1262.50 + + 5050.00 + 1262.50 + + StandardRated + 25 + + 63 + Moms + + + + + + 5050.00 + 1262.50 + 6312.50 + 6312.50 + + + 1 + 1.00 + 5000.00 + + 1 + + + 1250.00 + + 5000.00 + 1250.00 + + StandardRated + 25 + + 63 + Moms + + + + + + Hejsetavle + Hejsetavle + + 5712345780121 + + + + 5000.00 + 1 + 1 + + + + 2 + 2.00 + 50.00 + + 2 + + + 12.50 + + 50.00 + 12.50 + + StandardRated + 25 + + 63 + Moms + + + + + + Beslag + Beslag + + 5712345780111 + + + + 25.00 + 1 + 1 + + + + \ No newline at end of file diff --git a/src/test/resources/dummy-db-changelog.xml b/src/test/resources/dummy-db-changelog.xml new file mode 100644 index 0000000..8c3d52b --- /dev/null +++ b/src/test/resources/dummy-db-changelog.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/largefile.txt b/src/test/resources/largefile.txt new file mode 100644 index 0000000..78eb3d8 --- /dev/null +++ b/src/test/resources/largefile.txt @@ -0,0 +1 @@ +UEsDBAoAAAgAAMVryVaKIflFHwAAAB8AAAAIAAAAbWltZXR5cGVhcHBsaWNhdGlvbi92bmQuZXRzaS5hc2ljLWUremlwUEsDBBQACAgIAMVryVYAAAAAAAAAAAAAAAAeAAAAc3RhbmRhcmQtYnVzaW5lc3MtZG9jdW1lbnQueG1s7NxZlxtHmqDp+/kVftiXc0gxuIjLUapHqaVKJ7ealDK7p27mhMiQKjq56DCCWan5ZXM/f2zAzdxp5u6ALGGoD4Zn5qZLMgnmAPV+BqvC89l//8fzZ8PfL15dXb588ZsbZ7du3xguXjx5+fTyxU+/ufGX77+5+fDGf//8s++uz188PX/19Levry5fXFxdffXyyevnFy+uh80//eLqNzf+4/r658effPKf//mft16/uHhycevlq58+eXLx4/mT609enD+/uPr5/MnF1SdL/5p/vTh/evHqxuf/2zAsvtS7NW+WbBa9+x/++m7bn292/dknH/+ld8u+u3iR/pnN//jt082/5/LHy4tXwxevr//j5avL619+c+Py6uWn9+/cvbnZ6+avPL35+ufLqxuf3z57eO/x2Z279+5/+uDhZ5+M/+j7f/Un03/3Z3/ePPPl3/+Zl7pz5+7de/fu3597qY//7Z99eEc+LHxyfp0eefIGfv761YvHL8+vLq8ev/0IHl/9fPEkLX/8+odnj6+e/MfF8/PH/7h6+vgv15fPNnvc/MPXF2/+5TfvfJY+rvSv/v6Xny8+vMN3bp199sn0L6RHf3G1+eeeXEye49HdR4/un53dvnn/04vbN+89Ovv05qPbd364effujw8v7t3/4eGT2+ebBy//wekLf55v8d3LpzVfvrp4+2Rfbf72Fy+efn/5/OLzO7c37/btOzdvn31/dvfx/UeP7z24de/eg//99tnj25s/M3P/yPv3fO1N/uzDn83vnrycbOCj/+nDpr/605d/+cPXf/z+268+3u78G/VPfmSPi7/23/7bn779019++/ubm4/r8eO3n9nK2/zJDg/0b3/+05dff/fd7s/zJgqbN2uz+VsXrx///Orlj5fPNo/2/v9x//HmD/adN/8B/5P7+vJPf/nj93/+v/7vL8922dhXv9v5BT/7pPi4P9tSsrdr8o/iQyv/yc/4xrt/z+Mn509+5b/ry5fPn7988cVPP726+Gnzr9v8jz+/fLH5d15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 \ No newline at end of file diff --git a/src/test/resources/nemkonto-examples/Kvittering 0.xml b/src/test/resources/nemkonto-examples/Kvittering 0.xml new file mode 100644 index 0000000..7bb3a58 --- /dev/null +++ b/src/test/resources/nemkonto-examples/Kvittering 0.xml @@ -0,0 +1,29 @@ + + + + + + NKS + 5798000016446 + + + DATLEV + + Bad XML + + NKS KVITTERING0 73 + 2005-08-15T17:13:33 + REF12345 + + + + Authstn: The field length was less than minLen + + + + diff --git a/src/test/resources/nemkonto-examples/Kvittering 1.xml b/src/test/resources/nemkonto-examples/Kvittering 1.xml new file mode 100644 index 0000000..b1da828 --- /dev/null +++ b/src/test/resources/nemkonto-examples/Kvittering 1.xml @@ -0,0 +1,24 @@ + + + + + + NKS + 5798000016446 + + + DATLEV + + ACPT + + NKS KVITTERING1 73 + 2005-08-26T09:54:56 + TMO-20050826095549 + + + diff --git a/src/test/resources/nemkonto-examples/Payment_GLN_5798009811578.xml b/src/test/resources/nemkonto-examples/Payment_GLN_5798009811578.xml new file mode 100644 index 0000000..f8a3ab7 --- /dev/null +++ b/src/test/resources/nemkonto-examples/Payment_GLN_5798009811578.xml @@ -0,0 +1,101 @@ + + + + + + NAVIST + + 5798000011127 + + + NKS + 5798009811578 + + + + 5798000011127~802 + 2005-06-11T09:30:00Z + + + + + 5798009811127~802 + 2005-06-11T09:30:00Z + 1 + 200000 + 2 + true + + + + 25 + ADMID + + + + + + 2005-07-14 + TRF + + + BANK01234 + + + + + 81090001000539 + + + Deb.tekst AFL.D1.B3 + + + + 0027510647 + AFL.D1.B3.UPR01 + + + Kred. tekst ...UPR01 + + + 100000 + + + + + 27510647 + CVR + + + + true + NKSLEV + + + + + 2502414871 + AFL.D1.B3.UPR02 + + + Kred. tekst ...UPR02 + + + 100000 + + + + 2502414871 + + + true + NKSLEV + + + diff --git a/src/test/resources/nemkonto-examples/Reply - CPR-nr - DK konto og ingen kontonummer.XML b/src/test/resources/nemkonto-examples/Reply - CPR-nr - DK konto og ingen kontonummer.XML new file mode 100644 index 0000000..8b86c75 --- /dev/null +++ b/src/test/resources/nemkonto-examples/Reply - CPR-nr - DK konto og ingen kontonummer.XML @@ -0,0 +1,99 @@ + + + + 1234567 + system + bundt001 + + + + + 11223344 + + Betalingsreference001 + + 0503914883 + + + 2008-03-03T13:25:46Z + + + 9991 + 0503915530 + + + + + + + 11223344 + + Betalingsreference002 + + 0504854868 + + + 2008-03-03T13:25:46Z + + + 9994 + 0504855349 + + + + + + + 11223344 + + Betalingsreference003 + + 0712614455 + + + 2008-03-03T13:25:46Z + + + 2137 + 5005988889 + + + + + + + 11223344 + + Betalingsreference004 + + 1312814362 + + + 2008-03-03T13:25:46Z + + 2100 + Ingen NemKonto fundet + + + + + + 11223344 + + Betalingsreference005 + + 0706614818 + + + 2008-03-03T13:25:46Z + + 2100 + Ingen NemKonto fundet + + + diff --git a/src/test/resources/nemkonto-examples/Request - CVR-nr SE-nr P-nr.xml b/src/test/resources/nemkonto-examples/Request - CVR-nr SE-nr P-nr.xml new file mode 100644 index 0000000..c043a70 --- /dev/null +++ b/src/test/resources/nemkonto-examples/Request - CVR-nr SE-nr P-nr.xml @@ -0,0 +1,74 @@ + + + + 1234567 + system + Bundt003 + + + + 11223344 + + Betalingsreference009 + + 82002049 + + + + + 11223344 + + Betalingsreference010 + + 15700211 + + + + + 11223344 + + Betalingsreference011 + + 14840400 + + + + + 11223344 + + Betalingsreference012 + + + 27510647 + 66112527 + + + + + + 11223344 + + Betalingsreference013 + + + 15700211 + 1000000201 + + + + + + 11223344 + + Betalingsreference014 + + + 00000000 + 16202010 + + + + diff --git a/src/test/resources/nemkonto-examples/Retursvar 2 med ACPT.xml b/src/test/resources/nemkonto-examples/Retursvar 2 med ACPT.xml new file mode 100644 index 0000000..4b92c64 --- /dev/null +++ b/src/test/resources/nemkonto-examples/Retursvar 2 med ACPT.xml @@ -0,0 +1,44 @@ + + + + + + NKS + 5798000016446 + + + DATLEV + + + NKS2C RETURSVAR2 0000000795 + 2005-08-17T14:00:44 + TMO-20050817014232 + + + + NKS2C RETURSVAR2 0000000795 + 2005-08-17T14:00:44 + + + + 1 + ADMID + + + + + + TMO-20050817014232 + NKSBetaling + + ACPT + + + diff --git a/src/test/resources/nemkonto-examples/Retursvar 5 med PART.xml b/src/test/resources/nemkonto-examples/Retursvar 5 med PART.xml new file mode 100644 index 0000000..7f57b9b --- /dev/null +++ b/src/test/resources/nemkonto-examples/Retursvar 5 med PART.xml @@ -0,0 +1,51 @@ + + + + + + NKS + 5798000016446 + + + DATLEV + + + NKS2C RETURSVAR5 0000000795 + 2005-08-17T14:00:44 + TMO-20050817014232 + + + + NKS2C RETURSVAR5 0000000795 + 2005-08-17T14:00:44 + + + + 1 + ADMID + + + + + + TMO-20050817014232 + NKSBetaling + + PART + + + + + 99999999-189,BAR,20270,2005 + + RJCT + BETALING(ER) STANDSET + + + diff --git a/src/test/resources/nemkonto-examples/Retursvar 7.xml b/src/test/resources/nemkonto-examples/Retursvar 7.xml new file mode 100644 index 0000000..42736b0 --- /dev/null +++ b/src/test/resources/nemkonto-examples/Retursvar 7.xml @@ -0,0 +1,94 @@ + + + + + NKS2C RETURSVAR7 0000000073 + 2005-08-26T11:00:39 + + + + 1 + ADMID + + + + + + TMO-20050826095549 + NKSBetaling + + + + + 20101377-1,FLE,20507,12490, + + RJCT + RJCT + Konto findes ikke. Myndighed har valgt check retur. + + + + 100000 + + 1 + + + 12345678 + + + + + + + + + + 20101377-1,FLE,20509,12492, + + RJCT + RJCT + Konto findes ikke. Myndighed har valgt check retur. + + + + 200000 + + 1 + + + 23456789 + + + + + + + + + + 20101377-1,FLE,20511,12494, + + RJCT + RJCT + Konto findes ikke. Myndighed har valgt check retur. + + + + 300000 + + 1 + + + 34567890 + + + + + + + diff --git a/src/test/resources/nemkonto-examples/Retursvar 8.xml b/src/test/resources/nemkonto-examples/Retursvar 8.xml new file mode 100644 index 0000000..7fcc442 --- /dev/null +++ b/src/test/resources/nemkonto-examples/Retursvar 8.xml @@ -0,0 +1,86 @@ + + + + + NKS2C RETURSVAR8 0000000079 + 2005-08-30T10:54:48 + + + + 1 + ADMID + + + + + + TMO-20050826022933 + NKSBetaling + + + 2005-08-26 + + + BANK01234 + + + + + 12340123456789 + + + Test udbetaling + + + 12345678-1,BAR,20589,12532, + + ACPT + + + 100000 + + 1 + + + 12345678 + + + + + 12341212121212 + + + + + + + 12345678-1,BAR,20591,12534, + + ACPT + + + 100000 + + 1 + + + + 12345678 + CVR + + + + + + 12341212121212 + + + + + + diff --git a/src/test/resources/nemkonto-examples/Retursvar 9.xml b/src/test/resources/nemkonto-examples/Retursvar 9.xml new file mode 100644 index 0000000..a597c36 --- /dev/null +++ b/src/test/resources/nemkonto-examples/Retursvar 9.xml @@ -0,0 +1,71 @@ + + + + + NKS2C RETURSVAR9 0000000079 + 2005-08-30T10:54:50 + + + + 1 + ADMID + + + + + + TMO-20050826022933 + NKSBetaling + + + + + 20101377-1,BAR,20589,12532, + + RJCT + 0001 + DEBITERING AFVIST Aftale mangler for udbetalingskonto + + + + 100000 + + 1 + + + 12345678 + + + + + + + + + + 20905808-1,BAR,20591,12534, + + RJCT + 0001 + DEBITERING AFVIST Aftale mangler for udbetalingskonto + + + + 200000 + + 1 + + + 23456789 + + + + + + + diff --git a/src/test/resources/oxalis_home/configure-liquibase-changelog-conf/oxalis.conf b/src/test/resources/oxalis_home/configure-liquibase-changelog-conf/oxalis.conf new file mode 100644 index 0000000..97b1f1c --- /dev/null +++ b/src/test/resources/oxalis_home/configure-liquibase-changelog-conf/oxalis.conf @@ -0,0 +1,144 @@ + +jdbc.url = "jdbc:h2:mem:oxalis-liquibase-changelog-test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=true;MODE=MYSQL;" +jdbc.liquibase.changelog = dummy-db-changelog.xml + +// dont preload schematron for unit tests to speed things up +document.type.reminder { + preloadSchema = false + preloadSchematron = false +} +document.type.utilityStatement { + preloadSchema = false + preloadSchematron = false +} +document.type.invoice { + preloadSchema = false + preloadSchematron = false +} +document.type.creditNote { + preloadSchema = false + preloadSchematron = false +} +document.type.applicationResponse { + preloadSchema = false + preloadSchematron = false +} +document.type.statement { + preloadSchema = false + preloadSchematron = false +} +document.type.order { + preloadSchema = false + preloadSchematron = false +} +document.type.orderResponseSimple { + preloadSchema = false + preloadSchematron = false +} +document.type.orderResponse { + preloadSchema = false + preloadSchematron = false +} +document.type.orderCancellation { + preloadSchema = false + preloadSchematron = false +} +document.type.orderChange { + preloadSchema = false + preloadSchematron = false +} +document.type.catalogue { + preloadSchema = false + preloadSchematron = false +} +document.type.catalogueDeletion { + preloadSchema = false + preloadSchematron = false +} +document.type.catalogueRequest { + preloadSchema = false + preloadSchematron = false +} +document.type.catalogueItemSpecificationUpdate { + preloadSchema = false + preloadSchematron = false +} +document.type.cataloguePricingUpdate { + preloadSchema = false + preloadSchematron = false +} +document.type.cataloguePricingUpdateDKPeppol { + preloadSchema = false + preloadSchematron = false +} +document.type.catalogueUpdateResponseDKPeppol { + preloadSchema = false + preloadSchematron = false +} +document.type.orderAgreementPeppol { + preloadSchema = false + preloadSchematron = false +} +document.type.orderAgreementResponseDKPeppol { + preloadSchema = false + preloadSchematron = false +} +document.type.reminder30 { + preloadSchema = false + preloadSchematron = false +} +document.type.simplifiedInvoice30 { + preloadSchema = false + preloadSchematron = false +} +document.type.invoiceResponseTransaction30 { + preloadSchema = false + preloadSchematron = false +} +document.type.messageLevelResponseTransaction30 { + preloadSchema = false + preloadSchematron = false +} +document.type.peppolBISBillingInvoice { + preloadSchema = false + preloadSchematron = false +} +document.type.peppolBISBillingCreditNote { + preloadSchema = false + preloadSchematron = false +} +document.type.peppolCatalogue { + preloadSchema = false + preloadSchematron = false +} +document.type.peppolCatalogueResponse { + preloadSchema = false + preloadSchematron = false +} +document.type.peppolOrder { + preloadSchema = false + preloadSchematron = false +} +document.type.peppolOrderAgreement { + preloadSchema = false + preloadSchematron = false +} +document.type.peppolDespatchAdvice { + preloadSchema = false + preloadSchematron = false +} +document.type.peppolPunchOut { + preloadSchema = false + preloadSchematron = false +} +document.type.peppolOrderChange { + preloadSchema = false + preloadSchematron = false +} +document.type.peppolOrderCancellation { + preloadSchema = false + preloadSchematron = false +} + + + diff --git a/src/test/resources/oxalis_home/schematron-preload-conf/oxalis.conf b/src/test/resources/oxalis_home/schematron-preload-conf/oxalis.conf index 521e370..44581ff 100644 --- a/src/test/resources/oxalis_home/schematron-preload-conf/oxalis.conf +++ b/src/test/resources/oxalis_home/schematron-preload-conf/oxalis.conf @@ -1,3 +1,9 @@ -document.type.invoice.preloadSchematron = true -document.type.applicationResponse.preloadSchematron = true +document.type.invoice { + preloadSchema = true + preloadSchematron = true +} +document.type.applicationResponse { + preloadSchema = true + preloadSchematron = true +} diff --git a/src/test/resources/reference.conf b/src/test/resources/reference.conf index 4e4623a..3e60ddf 100644 --- a/src/test/resources/reference.conf +++ b/src/test/resources/reference.conf @@ -21,40 +21,142 @@ jdbc { } // dont preload schematron for unit tests to speed things up -document.type.reminder.preloadSchematron = false -document.type.utilityStatement.preloadSchematron = false -document.type.invoice.preloadSchematron = false -document.type.creditNote.preloadSchematron = false -document.type.applicationResponse.preloadSchematron = false -document.type.statement.preloadSchematron = false -document.type.order.preloadSchematron = false -document.type.orderResponseSimple.preloadSchematron = false -document.type.orderResponse.preloadSchematron = false -document.type.orderCancellation.preloadSchematron = false -document.type.orderChange.preloadSchematron = false -document.type.catalogue.preloadSchematron = false -document.type.catalogueDeletion.preloadSchematron = false -document.type.catalogueRequest.preloadSchematron = false -document.type.catalogueItemSpecificationUpdate.preloadSchematron = false -document.type.cataloguePricingUpdate.preloadSchematron = false -document.type.cataloguePricingUpdateDKPeppol.preloadSchematron = false -document.type.catalogueUpdateResponseDKPeppol.preloadSchematron = false -document.type.orderAgreementPeppol.preloadSchematron = false -document.type.orderAgreementResponseDKPeppol.preloadSchematron = false -document.type.reminder30.preloadSchematron = false -document.type.simplifiedInvoice30.preloadSchematron = false -document.type.invoiceResponseTransaction30.preloadSchematron = false -document.type.messageLevelResponseTransaction30.preloadSchematron = false -document.type.peppolBISBillingInvoice.preloadSchematron = false -document.type.peppolBISBillingCreditNote.preloadSchematron = false -document.type.peppolCatalogue.preloadSchematron = false -document.type.peppolCatalogueResponse.preloadSchematron = false -document.type.peppolOrder.preloadSchematron = false -document.type.peppolOrderAgreement.preloadSchematron = false -document.type.peppolDespatchAdvice.preloadSchematron = false -document.type.peppolPunchOut.preloadSchematron = false -document.type.peppolOrderChange.preloadSchematron = false -document.type.peppolOrderCancellation.preloadSchematron = false +document.type.reminder { + preloadSchema = false + preloadSchematron = false +} +document.type.utilityStatement { + preloadSchema = false + preloadSchematron = false +} +document.type.invoice { + preloadSchema = false + preloadSchematron = false +} +document.type.creditNote { + preloadSchema = false + preloadSchematron = false +} +document.type.applicationResponse { + preloadSchema = false + preloadSchematron = false +} +document.type.statement { + preloadSchema = false + preloadSchematron = false +} +document.type.order { + preloadSchema = false + preloadSchematron = false +} +document.type.orderResponseSimple { + preloadSchema = false + preloadSchematron = false +} +document.type.orderResponse { + preloadSchema = false + preloadSchematron = false +} +document.type.orderCancellation { + preloadSchema = false + preloadSchematron = false +} +document.type.orderChange { + preloadSchema = false + preloadSchematron = false +} +document.type.catalogue { + preloadSchema = false + preloadSchematron = false +} +document.type.catalogueDeletion { + preloadSchema = false + preloadSchematron = false +} +document.type.catalogueRequest { + preloadSchema = false + preloadSchematron = false +} +document.type.catalogueItemSpecificationUpdate { + preloadSchema = false + preloadSchematron = false +} +document.type.cataloguePricingUpdate { + preloadSchema = false + preloadSchematron = false +} +document.type.cataloguePricingUpdateDKPeppol { + preloadSchema = false + preloadSchematron = false +} +document.type.catalogueUpdateResponseDKPeppol { + preloadSchema = false + preloadSchematron = false +} +document.type.orderAgreementPeppol { + preloadSchema = false + preloadSchematron = false +} +document.type.orderAgreementResponseDKPeppol { + preloadSchema = false + preloadSchematron = false +} +document.type.reminder30 { + preloadSchema = false + preloadSchematron = false +} +document.type.simplifiedInvoice30 { + preloadSchema = false + preloadSchematron = false +} +document.type.invoiceResponseTransaction30 { + preloadSchema = false + preloadSchematron = false +} +document.type.messageLevelResponseTransaction30 { + preloadSchema = false + preloadSchematron = false +} +document.type.peppolBISBillingInvoice { + preloadSchema = false + preloadSchematron = false +} +document.type.peppolBISBillingCreditNote { + preloadSchema = false + preloadSchematron = false +} +document.type.peppolCatalogue { + preloadSchema = false + preloadSchematron = false +} +document.type.peppolCatalogueResponse { + preloadSchema = false + preloadSchematron = false +} +document.type.peppolOrder { + preloadSchema = false + preloadSchematron = false +} +document.type.peppolOrderAgreement { + preloadSchema = false + preloadSchematron = false +} +document.type.peppolDespatchAdvice { + preloadSchema = false + preloadSchematron = false +} +document.type.peppolPunchOut { + preloadSchema = false + preloadSchematron = false +} +document.type.peppolOrderChange { + preloadSchema = false + preloadSchematron = false +} +document.type.peppolOrderCancellation { + preloadSchema = false + preloadSchematron = false +} diff --git a/src/test/resources/schematron-error-examples/F-INV134_INV_The_Sum_of_PaymentTerms_Amount_Does_Not_Equal_PayableAmount_BZ1425.xml b/src/test/resources/schematron-error-examples/F-INV134_INV_The_Sum_of_PaymentTerms_Amount_Does_Not_Equal_PayableAmount_BZ1425.xml index 97829ea..989c8f6 100644 --- a/src/test/resources/schematron-error-examples/F-INV134_INV_The_Sum_of_PaymentTerms_Amount_Does_Not_Equal_PayableAmount_BZ1425.xml +++ b/src/test/resources/schematron-error-examples/F-INV134_INV_The_Sum_of_PaymentTerms_Amount_Does_Not_Equal_PayableAmount_BZ1425.xml @@ -66,7 +66,7 @@ 2.1 - OIOUBL-2.02 + OIOUBL-2.1 Procurement-OrdSimR-BilSim-1.0 A00095678 false -- GitLab