From fe14832b2d2163afdf87d4d0fd7e5e09ed216e0c Mon Sep 17 00:00:00 2001 From: Mikkel Ricky Date: Tue, 19 Aug 2025 11:21:49 +0200 Subject: [PATCH 01/10] 5189: Added support for MeMo 1.2 --- CHANGELOG.md | 3 +++ src/Service/SF1601/SF1601.php | 8 ++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 526daab..cb4fa64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +- [PR-41](https://github.com/itk-dev/serviceplatformen/pull/41) + Added support for MeMo 1.2 + ## [1.6.1] - 2025-03-30 - [PR-39](https://github.com/itk-dev/serviceplatformen/pull/39) diff --git a/src/Service/SF1601/SF1601.php b/src/Service/SF1601/SF1601.php index eeb6109..c8503be 100644 --- a/src/Service/SF1601/SF1601.php +++ b/src/Service/SF1601/SF1601.php @@ -39,6 +39,9 @@ class SF1601 extends AbstractRESTService self::TYPE_NEM_SMS, ]; + public const MEMO_1_1 = 1.1; + public const MEMO_1_2 = 1.2; + public const FORESPOERG_TYPE_DIGITAL_POST = 'digitalpost'; public const FORESPOERG_TYPE_NEM_SMS = 'nemsms'; @@ -182,9 +185,10 @@ private function buildKombiRequestDocument(string $type, ?Message $message, ?For if (null !== $message) { // Set default values on some required attributes. if (empty($message->getMemoVersion())) { - $message->setMemoVersion(1.1); + $message->setMemoVersion(self::MEMO_1_1); } - if (empty($message->getMemoSchVersion())) { + // memoSchVersion is required for MeMo 1.1 only (cf. https://digitaliser.dk/Media/638608781984779669/MeMo%20Versionshistorik%20v1.2.pdf) + if (empty($message->getMemoSchVersion()) && self::MEMO_1_1 === $message->getMemoVersion()) { $message->setMemoSchVersion('1.1.0'); } From 2dbe39051efbf318e483b9480f9c39dab290d89d Mon Sep 17 00:00:00 2001 From: Mikkel Ricky Date: Tue, 19 Aug 2025 12:21:11 +0200 Subject: [PATCH 02/10] 5189: Cleaned up code --- src/Command/SF1601/KombiPostAfsendCommand.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Command/SF1601/KombiPostAfsendCommand.php b/src/Command/SF1601/KombiPostAfsendCommand.php index 6cec893..810aace 100644 --- a/src/Command/SF1601/KombiPostAfsendCommand.php +++ b/src/Command/SF1601/KombiPostAfsendCommand.php @@ -11,7 +11,6 @@ namespace ItkDev\Serviceplatformen\Command\SF1601; use DataGovDk\Model\Core\Address; -use DateTime; use DigitalPost\MeMo\Action; use DigitalPost\MeMo\AdditionalDocument; use DigitalPost\MeMo\AttentionData; @@ -25,7 +24,6 @@ use DigitalPost\MeMo\Recipient; use DigitalPost\MeMo\Reservation; use DigitalPost\MeMo\Sender; -use DOMDocument; use GuzzleHttp\Client; use Http\Adapter\Guzzle7\Client as GuzzleAdapter; use Http\Factory\Guzzle\RequestFactory; @@ -206,8 +204,8 @@ private function buildAction(string $spec): Action ->setLabel($options['label']); if (SF1601::ACTION_AFTALE === $options['action']) { $reservation = (new Reservation()) - ->setStartDateTime(new DateTime('+2 days')) - ->setEndDateTime(new DateTime('+2 days 1 hour')) + ->setStartDateTime(new \DateTime('+2 days')) + ->setEndDateTime(new \DateTime('+2 days 1 hour')) ->setLocation('Meeting room 1') ->setAbstract('Abstract') ->setDescription('Description') @@ -351,7 +349,7 @@ private function buildMessage(array $options): Message $message->setMessageHeader($messageHeader); $body = (new MessageBody()) - ->setCreatedDateTime(new DateTime()); + ->setCreatedDateTime(new \DateTime()); if (isset($options['file'])) { $mimeTypes = new MimeTypes(); From 72c9d69f9b84bb88db512670f133b08adab0345c Mon Sep 17 00:00:00 2001 From: Mikkel Ricky Date: Tue, 19 Aug 2025 12:22:50 +0200 Subject: [PATCH 03/10] 5189: PHP 8.3 --- .github/workflows/pr.yaml | 6 +++--- .php-version | 1 - README.md | 26 ++++++++++++------------- docs/SF1601.md | 6 +++--- src/Service/Exception/SoapException.php | 2 +- 5 files changed, 20 insertions(+), 21 deletions(-) delete mode 100644 .php-version diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 46ae0b3..5c59486 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -23,7 +23,7 @@ jobs: strategy: fail-fast: false matrix: - php: [ '8.1', '8.2', '8.3' ] + php: [ '8.3' ] name: Validate composer (${{ matrix.php}}) steps: - name: Checkout @@ -55,7 +55,7 @@ jobs: strategy: fail-fast: false matrix: - php: [ '8.1', '8.2', '8.3' ] + php: [ '8.3' ] name: PHP coding Standards (${{ matrix.php }}) steps: - name: Checkout @@ -85,7 +85,7 @@ jobs: strategy: fail-fast: false matrix: - php: [ '8.1', '8.2', '8.3' ] + php: [ '8.3' ] name: PHP code analysis (${{ matrix.php }}) steps: - name: Checkout diff --git a/.php-version b/.php-version deleted file mode 100644 index b8eb026..0000000 --- a/.php-version +++ /dev/null @@ -1 +0,0 @@ -8.1 diff --git a/README.md b/README.md index b5ccc60..a76c7fc 100644 --- a/README.md +++ b/README.md @@ -18,32 +18,32 @@ generate PHP classes for talking to SOAP services. To update [resources](./resources) and [generated classes](./generated-classes), run ``` shell -docker run --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.1-fpm:latest composer install +docker run --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.3-fpm:latest composer install # Update WSDL resources. -docker run --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.1-fpm:latest bin/generate resources +docker run --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.3-fpm:latest bin/generate resources # Generate PHP classes from WSDL resources. -docker run --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.1-fpm:latest bin/generate classes +docker run --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.3-fpm:latest bin/generate classes ``` ## Test commands ``` shell -docker run --rm --volume ${PWD}:/app itkdev/php8.1-fpm:latest vendor/bin/serviceplatformen-sf1601-kombipostafsend --help +docker run --rm --volume ${PWD}:/app itkdev/php8.3-fpm:latest vendor/bin/serviceplatformen-sf1601-kombipostafsend --help ``` Use `bin/serviceplatformen-sf1601-kombipostafsend` (symlinked to `bin/SF1601/kombipostafsend`) during development of this library. i.e. ``` shell -docker run --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.1-fpm:latest bin/serviceplatformen-sf1601-kombipostafsend +docker run --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.3-fpm:latest bin/serviceplatformen-sf1601-kombipostafsend ``` ``` shell -docker run --rm --volume ${PWD}:/app itkdev/php8.1-fpm:latest vendor/bin/serviceplatformen-sf1601-postforespoerg --help +docker run --rm --volume ${PWD}:/app itkdev/php8.3-fpm:latest vendor/bin/serviceplatformen-sf1601-postforespoerg --help ``` ``` shell -docker run --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.1-fpm:latest bin/serviceplatformen-sf1601-postforespoerg +docker run --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.3-fpm:latest bin/serviceplatformen-sf1601-postforespoerg ``` ## Getting Started @@ -79,13 +79,13 @@ composer install Unit tests: ``` shell -docker run --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.1-fpm:latest composer tests/unit +docker run --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.3-fpm:latest composer tests/unit ``` End to end tests: ``` shell -docker run --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.1-fpm:latest composer tests/end-to-end +docker run --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.3-fpm:latest composer tests/end-to-end ``` ### And coding style tests @@ -237,9 +237,9 @@ reviewer to merge it for you. ### Coding standards ``` shell -docker run --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.1-fpm:latest composer install -docker run --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.1-fpm:latest composer coding-standards-apply -docker run --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.1-fpm:latest composer coding-standards-check +docker run --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.3-fpm:latest composer install +docker run --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.3-fpm:latest composer coding-standards-apply +docker run --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.3-fpm:latest composer coding-standards-check ``` ``` shell @@ -250,7 +250,7 @@ docker run --rm --volume ${PWD}:/md peterdavehello/markdownlint markdownlint --i ### Code analysis ``` shell -docker run --interactive --tty --rm --volume ${PWD}:/app --env COMPOSER_MEMORY_LIMIT=-1 itkdev/php8.1-fpm:latest composer code-analysis +docker run --interactive --tty --rm --volume ${PWD}:/app --env COMPOSER_MEMORY_LIMIT=-1 itkdev/php8.3-fpm:latest composer code-analysis ``` ## Versioning diff --git a/docs/SF1601.md b/docs/SF1601.md index aaadf04..a4a4926 100644 --- a/docs/SF1601.md +++ b/docs/SF1601.md @@ -182,8 +182,8 @@ classes. To generate or update the generated PHP classes, run ``` shell -docker run --rm --volume ${PWD}:/app itkdev/php8.1-fpm:latest composer install -docker run --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.1-fpm:latest bin/xsd2php +docker run --rm --volume ${PWD}:/app itkdev/php8.3-fpm:latest composer install +docker run --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.3-fpm:latest bin/xsd2php ``` ## Sending test messages @@ -192,5 +192,5 @@ A console command, [`bin/SF1601/kombipostafsend`](bin/SF1601/kombipostafsend), c the “[KombiPostAfsend](https://digitaliseringskataloget.dk/integration/sf1601#table-of-contents-1-2)” service: ``` shell -docker run --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.1-fpm:latest bin/SF1601/kombipostafsend –help +docker run --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.3-fpm:latest bin/SF1601/kombipostafsend –help ``` diff --git a/src/Service/Exception/SoapException.php b/src/Service/Exception/SoapException.php index 09ad5c1..51813cd 100644 --- a/src/Service/Exception/SoapException.php +++ b/src/Service/Exception/SoapException.php @@ -17,7 +17,7 @@ */ class SoapException extends ServiceException { - public function __construct(readonly private \SoapFault $soapFault, readonly private ?string $request, readonly private ?string $response) + public function __construct(private readonly \SoapFault $soapFault, private readonly ?string $request, private readonly ?string $response) { parent::__construct($this->soapFault->getMessage(), $this->soapFault->getCode(), $this->soapFault); } From 18201bcddb92eb8f152ab663f3c45222a46671f1 Mon Sep 17 00:00:00 2001 From: Mikkel Ricky Date: Tue, 19 Aug 2025 13:53:32 +0200 Subject: [PATCH 04/10] 5189: Added filename sanitizer --- .github/workflows/pr.yaml | 30 ++++++ .gitignore | 1 + phpunit.xml.dist | 39 +++---- src/Service/SF1601/SF1601.php | 21 ++++ tests/Unit/Service/SF1601/SF1601Test.php | 127 +++++++++++++++++++++++ 5 files changed, 193 insertions(+), 25 deletions(-) create mode 100644 tests/Unit/Service/SF1601/SF1601Test.php diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 5c59486..21ab944 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -118,3 +118,33 @@ jobs: uses: actions/checkout@v4 - name: coding-standards-check run: docker run --rm --volume ${PWD}:/md peterdavehello/markdownlint markdownlint --ignore vendor --ignore LICENSE.md '**/*.md' + + test-unit: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php: [ '8.3' ] + name: Run unit tests (${{ matrix.php}}) + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup PHP, with composer and extensions + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php}} + extensions: apcu, ctype, iconv, imagick, json, redis, soap, xmlreader, zip + coverage: none + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + - name: Cache composer dependencies + uses: actions/cache@v4 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ matrix.php }}-composer- + - name: Composer install + run: composer install + - name: Run unit tests + run: composer tests/unit diff --git a/.gitignore b/.gitignore index 6db1725..085d55c 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ generated-classes/tutorial.php /.php-cs-fixer.cache /phpcs.xml ###< squizlabs/php_codesniffer ### +*.cache diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 75776d4..55d46e3 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,27 +1,16 @@ - - - - ./tests/Unit - - - - ./tests/EndToEnd - - - - - ./src - - + + + + ./src + + + + + ./tests/Unit + + + ./tests/EndToEnd + + diff --git a/src/Service/SF1601/SF1601.php b/src/Service/SF1601/SF1601.php index c8503be..83d9a6c 100644 --- a/src/Service/SF1601/SF1601.php +++ b/src/Service/SF1601/SF1601.php @@ -224,4 +224,25 @@ private function buildKombiRequestDocument(string $type, ?Message $message, ?For return $document; } + + /** + * Sanitize MeMo filename (cf. https://digitaliser.dk/digital-post/nyhedsarkiv/2024/nov/oeget-validering-i-digital-post) + * + * Non-empty sequences of invalid characters are replaced with a single character. + */ + public static function sanitizeFilename(string $filename, string $replacer = '-'): string + { + // < > : " / \ | ? * CR LF CRLF U+00A0 U+2000 U+2001 U+2002 U+2003 U+2004 U+2005 U+2006 U+2007 U+2008 U+2009 U+200A U+2028 U+205F U+2060 U+3000 + $pattern = '@[<>:"/\\\\|?*\x{000D}\x{000A}\x{00A0}\x{2000}\x{2001}\x{2002}\x{2003}\x{2004}\x{2005}\x{2006}\x{2007}\x{2008}\x{2009}\x{200A}\x{2028}\x{205F}\x{2060}\x{3000}]+@u'; + + if (mb_strlen($replacer) !== 1) { + throw new \InvalidArgumentException(sprintf('Replacer must have length 1 (is %d)', mb_strlen($replacer))); + } + + if (preg_match($pattern, $replacer, $matches)) { + throw new \InvalidArgumentException(sprintf('Replacer %s contains invalid character %s', var_export($replacer, true), var_export($matches[0], true))); + } + + return preg_replace($pattern, $replacer, $filename); + } } diff --git a/tests/Unit/Service/SF1601/SF1601Test.php b/tests/Unit/Service/SF1601/SF1601Test.php new file mode 100644 index 0000000..bbe5ad3 --- /dev/null +++ b/tests/Unit/Service/SF1601/SF1601Test.php @@ -0,0 +1,127 @@ +assertEquals($expected, $actual); + } + + /** @dataProvider sanitizeFilenameWithReplacerProvider */ + public function testSanitizeFilenameWithReplacer(string $filename, string $replacer, string|\Exception $expected): void + { + if ($expected instanceof \Exception) { + $this->expectException($expected::class); + } + $actual = SF1601::sanitizeFilename($filename, $replacer); + $this->assertEquals($expected, $actual); + } + + public static function sanitizeFilenameProvider(): iterable + { + yield ['a.pdf', 'a.pdf']; + + // The ones we cannot use + + // < + yield ['a + yield ['a>b.pdf', 'a-b.pdf']; + // : + yield ['a:b.pdf', 'a-b.pdf']; + // " + yield ['a"b.pdf', 'a-b.pdf']; + // / + yield ['a/b.pdf', 'a-b.pdf']; + // \ + yield ['a\b.pdf', 'a-b.pdf']; + // | + yield ['a|b.pdf', 'a-b.pdf']; + // ? + yield ['a?b.pdf', 'a-b.pdf']; + // * + yield ['a*b.pdf', 'a-b.pdf']; + // CR + yield ['a'."\n".'b.pdf', 'a-b.pdf']; + // LF + yield ['a'."\r".'b.pdf', 'a-b.pdf']; + // CRLF + yield ['a'."\n\r".'b.pdf', 'a-b.pdf']; + // U+00A0 + yield ['a b.pdf', 'a-b.pdf']; + // U+2000 + yield ['a b.pdf', 'a-b.pdf']; + // U+2001 + yield ['a b.pdf', 'a-b.pdf']; + // U+2002 + yield ['a b.pdf', 'a-b.pdf']; + // U+2003 + yield ['a b.pdf', 'a-b.pdf']; + // U+2004 + yield ['a b.pdf', 'a-b.pdf']; + // U+2005 + yield ['a b.pdf', 'a-b.pdf']; + // U+2006 + yield ['a b.pdf', 'a-b.pdf']; + // U+2007 + yield ['a b.pdf', 'a-b.pdf']; + // U+2008 + yield ['a b.pdf', 'a-b.pdf']; + // U+2009 + yield ['a b.pdf', 'a-b.pdf']; + // U+200A + yield ['a b.pdf', 'a-b.pdf']; + // U+2028 + yield ['a
b.pdf', 'a-b.pdf']; + // U+205F + yield ['a b.pdf', 'a-b.pdf']; + // U+2060 + yield ['a⁠b.pdf', 'a-b.pdf']; + // U+3000 + yield ['a b.pdf', 'a-b.pdf']; + + // Some we can use + + yield ['a💩b.pdf', 'a💩b.pdf']; + yield ['a👻b.pdf', 'a👻b.pdf']; + } + + public static function sanitizeFilenameWithReplacerProvider(): iterable + { + yield ['a', new \InvalidArgumentException('Replacer contains invalid character')]; + } +} From 7f6be44152623701996ed5e707bc99a4c6a97e2d Mon Sep 17 00:00:00 2001 From: Mikkel Ricky Date: Tue, 19 Aug 2025 14:14:08 +0200 Subject: [PATCH 05/10] 5189: Set default MeMo version --- docs/SF1601.md | 22 +++++++++++++++++++++- src/Service/SF1601/SF1601.php | 12 +++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/docs/SF1601.md b/docs/SF1601.md index a4a4926..a977d2c 100644 --- a/docs/SF1601.md +++ b/docs/SF1601.md @@ -2,6 +2,20 @@ +## MeMo version + +If the MeMe version not set explicitly on [a message](lib/DigitalPost/MeMo/Message.php), this library set the version to +[MeMo 1.2](https://digitaliser.dk/digital-post/nyhedsarkiv/2025/mar/lancering-af-memo-version-12). + +The default version can be overridden by settings the `SERVICEPLATFORMEN_MEMO_DEFAULT_VERSION` environment variable to +one the allowed values `1.1` or `1.2`, e.g. + +``` env +SERVICEPLATFORMEN_MEMO_DEFAULT_VERSION=1.1 +``` + +------------------------------------------------------------------------------------------------------------------------ + Calling this service is extremely simple, but it's extremely hard to navigate the documentation and understand how to actually authenticate and call the service. @@ -192,5 +206,11 @@ A console command, [`bin/SF1601/kombipostafsend`](bin/SF1601/kombipostafsend), c the “[KombiPostAfsend](https://digitaliseringskataloget.dk/integration/sf1601#table-of-contents-1-2)” service: ``` shell -docker run --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.3-fpm:latest bin/SF1601/kombipostafsend –help +docker run --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.3-fpm:latest bin/SF1601/kombipostafsend –-help +``` + +To test with another default MeMo version, e.g. `1.1`, run + +``` shell +docker run --env SERVICEPLATFORMEN_MEMO_DEFAULT_VERSION=1.1 --interactive --tty --rm --volume ${PWD}:/app itkdev/php8.3-fpm:latest bin/SF1601/kombipostafsend ``` diff --git a/src/Service/SF1601/SF1601.php b/src/Service/SF1601/SF1601.php index 83d9a6c..b990e87 100644 --- a/src/Service/SF1601/SF1601.php +++ b/src/Service/SF1601/SF1601.php @@ -185,7 +185,7 @@ private function buildKombiRequestDocument(string $type, ?Message $message, ?For if (null !== $message) { // Set default values on some required attributes. if (empty($message->getMemoVersion())) { - $message->setMemoVersion(self::MEMO_1_1); + $message->setMemoVersion($this->getDefaultMeMoVersion()); } // memoSchVersion is required for MeMo 1.1 only (cf. https://digitaliser.dk/Media/638608781984779669/MeMo%20Versionshistorik%20v1.2.pdf) if (empty($message->getMemoSchVersion()) && self::MEMO_1_1 === $message->getMemoVersion()) { @@ -225,6 +225,16 @@ private function buildKombiRequestDocument(string $type, ?Message $message, ?For return $document; } + private function getDefaultMeMoVersion(): float + { + $value = floatval(getenv('SERVICEPLATFORMEN_MEMO_DEFAULT_VERSION')); + + return match ($value) { + self::MEMO_1_1 => self::MEMO_1_1, + default => self::MEMO_1_2, + }; + } + /** * Sanitize MeMo filename (cf. https://digitaliser.dk/digital-post/nyhedsarkiv/2024/nov/oeget-validering-i-digital-post) * From 3173fb5f7b721145374682c145e340f0db5da4b7 Mon Sep 17 00:00:00 2001 From: Mikkel Ricky Date: Wed, 20 Aug 2025 09:21:47 +0200 Subject: [PATCH 06/10] 5189: Added action URL runtime validation --- src/Service/SF1601/SF1601.php | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/Service/SF1601/SF1601.php b/src/Service/SF1601/SF1601.php index b990e87..973abff 100644 --- a/src/Service/SF1601/SF1601.php +++ b/src/Service/SF1601/SF1601.php @@ -207,6 +207,11 @@ private function buildKombiRequestDocument(string $type, ?Message $message, ?For throw new InvalidMemoException('MeMo message header must have a sender with a label'); } + $this->validateActions($message->getMessageBody()->getMainDocument()->getAction()); + foreach ($message->getMessageBody()->getAdditionalDocument() as $document) { + $this->validateActions($document->getAction()); + } + // Serialize message and import and append it to kombi_request element. $messageDocument = Serializer::loadXML((new Serializer())->serialize($message)); @@ -255,4 +260,26 @@ public static function sanitizeFilename(string $filename, string $replacer = '-' return preg_replace($pattern, $replacer, $filename); } + + /** + * Validate MeMo actions. + * + * @param \DigitalPost\MeMo\Action[] $actions + */ + private function validateActions(array $actions) + { + foreach ($actions as $action) { + /** @phpstan-ignore nullsafe.neverNull (Action::getEntryPoint can actually return null) */ + $url = $action->getEntryPoint()?->getUrl(); + // URL must be absolute and use https (cf. https://digitaliser.dk/digital-post/nyhedsarkiv/2024/nov/oeget-validering-i-digital-post) + if ($url && 'https' !== parse_url($url, PHP_URL_SCHEME)) { + throw new \RuntimeException(sprintf( + 'URL %s for action "%s" (%s) must be absolute and use the https scheme, i.e. start with "https://".', + $url, + $action->getLabel(), + $action->getActionCode() + )); + } + } + } } From 102a1c0ec3c6f7f43b103d632adcbeb80ace7ea7 Mon Sep 17 00:00:00 2001 From: Mikkel Ricky Date: Wed, 20 Aug 2025 10:59:43 +0200 Subject: [PATCH 07/10] Update docs/SF1601.md Co-authored-by: Jeppe Kuhlmann Andersen <78410897+jekuaitk@users.noreply.github.com> --- docs/SF1601.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/SF1601.md b/docs/SF1601.md index a977d2c..f15a02f 100644 --- a/docs/SF1601.md +++ b/docs/SF1601.md @@ -4,7 +4,7 @@ ## MeMo version -If the MeMe version not set explicitly on [a message](lib/DigitalPost/MeMo/Message.php), this library set the version to +If the MeMo version not set explicitly on [a message](lib/DigitalPost/MeMo/Message.php), this library set the version to [MeMo 1.2](https://digitaliser.dk/digital-post/nyhedsarkiv/2025/mar/lancering-af-memo-version-12). The default version can be overridden by settings the `SERVICEPLATFORMEN_MEMO_DEFAULT_VERSION` environment variable to From 6d825e0aace472d7f747665bb11da618d8f73f39 Mon Sep 17 00:00:00 2001 From: Mikkel Ricky Date: Wed, 20 Aug 2025 10:59:58 +0200 Subject: [PATCH 08/10] Update docs/SF1601.md Co-authored-by: Jeppe Kuhlmann Andersen <78410897+jekuaitk@users.noreply.github.com> --- docs/SF1601.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/SF1601.md b/docs/SF1601.md index f15a02f..10a2f0f 100644 --- a/docs/SF1601.md +++ b/docs/SF1601.md @@ -8,7 +8,7 @@ If the MeMo version not set explicitly on [a message](lib/DigitalPost/MeMo/Messa [MeMo 1.2](https://digitaliser.dk/digital-post/nyhedsarkiv/2025/mar/lancering-af-memo-version-12). The default version can be overridden by settings the `SERVICEPLATFORMEN_MEMO_DEFAULT_VERSION` environment variable to -one the allowed values `1.1` or `1.2`, e.g. +one of the allowed values `1.1` or `1.2`, e.g. ``` env SERVICEPLATFORMEN_MEMO_DEFAULT_VERSION=1.1 From 3667420f35da2f3dcf189c173126c820abbfd4aa Mon Sep 17 00:00:00 2001 From: Mikkel Ricky Date: Wed, 20 Aug 2025 11:02:31 +0200 Subject: [PATCH 09/10] 5189: Fixed typo --- docs/SF1601.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/SF1601.md b/docs/SF1601.md index 10a2f0f..aba11ce 100644 --- a/docs/SF1601.md +++ b/docs/SF1601.md @@ -4,8 +4,8 @@ ## MeMo version -If the MeMo version not set explicitly on [a message](lib/DigitalPost/MeMo/Message.php), this library set the version to -[MeMo 1.2](https://digitaliser.dk/digital-post/nyhedsarkiv/2025/mar/lancering-af-memo-version-12). +If the MeMo version is not set explicitly on [a message](lib/DigitalPost/MeMo/Message.php), this library set the version +to [MeMo 1.2](https://digitaliser.dk/digital-post/nyhedsarkiv/2025/mar/lancering-af-memo-version-12). The default version can be overridden by settings the `SERVICEPLATFORMEN_MEMO_DEFAULT_VERSION` environment variable to one of the allowed values `1.1` or `1.2`, e.g. From 4d3e09a74c1c0f0b51c1a418ed2afe817b3baedc Mon Sep 17 00:00:00 2001 From: Mikkel Ricky Date: Wed, 20 Aug 2025 11:02:45 +0200 Subject: [PATCH 10/10] 5189: Removed superfluous comment --- tests/Unit/Service/SF1601/SF1601Test.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/Unit/Service/SF1601/SF1601Test.php b/tests/Unit/Service/SF1601/SF1601Test.php index bbe5ad3..bcc87b9 100644 --- a/tests/Unit/Service/SF1601/SF1601Test.php +++ b/tests/Unit/Service/SF1601/SF1601Test.php @@ -10,14 +10,6 @@ namespace ItkDev\Serviceplatformen\Tests\Unit\Service\SF1601; -/** - * This file is part of itk-dev/serviceplatformen. - * - * (c) 2020 ITK Development - * - * This source file is subject to the MIT license. - */ - use ItkDev\Serviceplatformen\Service\SF1601\SF1601; use PHPUnit\Framework\TestCase;