From fea60bb317f0497ab1eb247629b2749d829230dd Mon Sep 17 00:00:00 2001 From: Joshua Thijssen Date: Mon, 20 Feb 2023 11:51:02 +0100 Subject: [PATCH] Added validator callback + roles only on care providers (#22) * Added validator callback * fix phpci --- src/UziValidator.php | 24 +++++++++++++++++--- tests/UziReaderTest.php | 2 +- tests/UziValidatorTest.php | 26 ++++++++++++++++++--- tests/certs/generate-mock-certs.sh | 2 +- tests/certs/mock-011-correct.cert | 36 +++++++++++++++--------------- 5 files changed, 64 insertions(+), 26 deletions(-) diff --git a/src/UziValidator.php b/src/UziValidator.php index 7a51458..510397e 100644 --- a/src/UziValidator.php +++ b/src/UziValidator.php @@ -24,18 +24,26 @@ class UziValidator protected array $allowedRoles; protected array $caCerts = []; + /** @var callable|null */ + protected $validatorCallback = null; + + /** + * @param callable|null $validatorCallback + */ public function __construct( UziReader $reader, bool $strictCaCheck, array $allowedTypes, array $allowedRoles, - array $caCerts = [] + array $caCerts = [], + $validatorCallback = null ) { + $this->reader = $reader; $this->strictCAcheck = $strictCaCheck; $this->allowedTypes = $allowedTypes; $this->allowedRoles = $allowedRoles; - $this->reader = $reader; $this->caCerts = $caCerts; + $this->validatorCallback = $validatorCallback; } public function isValid(Request $request): bool @@ -94,8 +102,18 @@ public function validate(Request $request): void if (!in_array($uziInfo->getCardType(), $this->allowedTypes)) { throw new UziAllowedTypeException('UZI card type not allowed'); } - if (!in_array(substr($uziInfo->getRole(), 0, 3), $this->allowedRoles)) { + + // Check roles for care provider + if ( + $uziInfo->getCardType() === UziConstants::UZI_TYPE_CARE_PROVIDER && + !in_array(substr($uziInfo->getRole(), 0, 3), $this->allowedRoles) + ) { throw new UziAllowedRoleException("UZI card role not allowed"); } + + // If a specific callback is set, call it + if ($this->validatorCallback && call_user_func($this->validatorCallback, $uziInfo) === false) { + throw new UziException('Uzi certificate validation failed (callback returned false)'); + } } } diff --git a/tests/UziReaderTest.php b/tests/UziReaderTest.php index 520173a..73ca64e 100644 --- a/tests/UziReaderTest.php +++ b/tests/UziReaderTest.php @@ -125,7 +125,7 @@ public function testCheckValidCert(): void $uziInfo = $uzi->getDataFromRequest($request); $this->assertEquals('00000000', $uziInfo->getAgbCode()); - $this->assertEquals('N', $uziInfo->getCardType()); + $this->assertEquals('Z', $uziInfo->getCardType()); $this->assertEquals('john', $uziInfo->getGivenName()); $this->assertEquals('2.16.528.1.1003.1.3.5.5.2', $uziInfo->getOidCa()); $this->assertEquals('30.015', $uziInfo->getRole()); diff --git a/tests/UziValidatorTest.php b/tests/UziValidatorTest.php index 54ca64d..1e859a4 100644 --- a/tests/UziValidatorTest.php +++ b/tests/UziValidatorTest.php @@ -123,7 +123,7 @@ public function testNotAllowedType(): void $this->expectExceptionMessage("UZI card type not allowed"); $reader = new UziReader(); - $validator = new UziValidator($reader, true, [UziConstants::UZI_TYPE_CARE_PROVIDER], []); + $validator = new UziValidator($reader, true, [UziConstants::UZI_TYPE_SERVER], []); $validator->validate($request); } @@ -140,7 +140,7 @@ public function testNotAllowedRole(): void $validator = new UziValidator( $reader, true, - [UziConstants::UZI_TYPE_NAMED_EMPLOYEE], + [UziConstants::UZI_TYPE_CARE_PROVIDER], [UziConstants::UZI_ROLE_PHARMACIST] ); $validator->validate($request); @@ -156,9 +156,29 @@ public function testIsValid(): void $validator = new UziValidator( $reader, true, - [UziConstants::UZI_TYPE_NAMED_EMPLOYEE], + [UziConstants::UZI_TYPE_CARE_PROVIDER], [UziConstants::UZI_ROLE_NURSE] ); $this->assertTrue($validator->isValid($request)); } + + public function testCallback(): void + { + $request = new Request(); + $request->server->set('SSL_CLIENT_VERIFY', "SUCCESS"); + $request->server->set('SSL_CLIENT_CERT', file_get_contents(__DIR__ . '/certs/mock-011-correct.cert')); + + $reader = new UziReader(); + $validator = new UziValidator( + $reader, + true, + [UziConstants::UZI_TYPE_CARE_PROVIDER], + [UziConstants::UZI_ROLE_NURSE], + [], + function (UziUser $info) { + return false; + } + ); + $this->assertFalse($validator->isValid($request)); + } } diff --git a/tests/certs/generate-mock-certs.sh b/tests/certs/generate-mock-certs.sh index b3a206d..1f0e31a 100644 --- a/tests/certs/generate-mock-certs.sh +++ b/tests/certs/generate-mock-certs.sh @@ -85,7 +85,7 @@ openssl req -x509 \ -out mock-011-correct.cert \ -days 3650 \ -subj "/C=NL/O=MockTest Cert/title=physician/SN=doe-12345678/GN=john/CN=john doe-12345678" \ - -addext "subjectAltName = otherName:2.5.5.5;IA5STRING:2.16.528.1.1003.1.3.5.5.2-1-12345678-N-90000111-30.015-00000000" + -addext "subjectAltName = otherName:2.5.5.5;IA5STRING:2.16.528.1.1003.1.3.5.5.2-1-12345678-Z-90000111-30.015-00000000" openssl req -x509 \ -nodes \ diff --git a/tests/certs/mock-011-correct.cert b/tests/certs/mock-011-correct.cert index 211b18b..6e2cd1c 100644 --- a/tests/certs/mock-011-correct.cert +++ b/tests/certs/mock-011-correct.cert @@ -1,25 +1,25 @@ -----BEGIN CERTIFICATE----- -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 -MWbF/YmKA2CibSQc9px7+ttY +Njc4LVotOTAwMDAxMTEtMzAuMDE1LTAwMDAwMDAwMA0GCSqGSIb3DQEBCwUAA4IB +AQAzTqEaXVQFb4Q7jslxqG8l4BZ3HWTN0f1fMZawe7ti/X4/8+2xjmWASxyH2jiq +GSUK4gd6Adh5Pcl0/gU0yyOC15kFWr4qMxyIO2XW/r1dEmpLT1MxFc9lu6hA/HWN +biaZHDw99yMWJmwL5fXS+BR7AEq9EYnijDoL3xzdgmSZWyEhVdJ5pvKYX/d0iNt5 +egktt8qdaTcNyRmp/4vn0r+zCswQ3VKitiqnPAIkzvIGrz0dpqrBlk1ixJjSKL9H +qGeoxYhKkjPEVlCV8+C+dEVRJ20uBIZAMWH06uEa2mB1wuqh2zJ3+V8GxC5au52/ +s09C7+ewBmyTxexi9ZJmiign -----END CERTIFICATE-----