Skip to content

Commit

Permalink
Reader and validator are separated (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
jaytaph authored Feb 7, 2023
1 parent 6d246af commit c4256cf
Show file tree
Hide file tree
Showing 10 changed files with 298 additions and 178 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
strategy:
max-parallel: 3
matrix:
php-versions: [ '7.3', '7.4', '8.0', '8.1', '8.2' ]
php-versions: [ '7.4', '8.0', '8.1', '8.2' ]

steps:
- uses: actions/checkout@v2
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
}
],
"require": {
"php": ">=7.3",
"php": ">=7.4",
"phpseclib/phpseclib": "^3.0.5",
"psr/http-message": "^1.0",
"ext-json": "*"
Expand Down
47 changes: 12 additions & 35 deletions src/UziReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@

namespace MinVWS\PUZI;

use MinVWS\PUZI\Exceptions\UziCardExpired;
use MinVWS\PUZI\Exceptions\UziCertificateException;
use MinVWS\PUZI\Exceptions\UziCertificateNotUziException;
use phpseclib3\File\X509;
use MinVWS\PUZI\Exceptions\UziException;
use Symfony\Component\HttpFoundation\Request;

/**
Expand All @@ -19,40 +15,19 @@
class UziReader
{
/**
* @param Request $request The request object
* @param array $caCerts Additional CA certificates to check against
* @return UziUser
* @throws UziCardExpired
* @throws UziCertificateException
* @throws UziCertificateNotUziException
* @param Request $request
* @return UziUser|null
*/
public function getDataFromRequest(Request $request, array $caCerts = []): UziUser
public function getDataFromRequest(Request $request): ?UziUser
{
if (!$request->server->has('SSL_CLIENT_VERIFY') || $request->server->get('SSL_CLIENT_VERIFY') !== 'SUCCESS') {
throw new UziCertificateException('Webserver client cert check not passed');
}
if (!$request->server->has('SSL_CLIENT_CERT')) {
throw new UziCertificateException('No client certificate presented');
}

$x509 = new X509();
$cert = $x509->loadX509($request->server->get('SSL_CLIENT_CERT'));
foreach ($caCerts as $caCert) {
$x509->loadCA($caCert);
}

// Check valid CA path
if (! $x509->validateSignature(count($caCerts) > 0)) {
throw new UziCertificateException('Invalid CA path');
}

// Check if the certificate is expired
if (! $x509->validateDate()) {
throw new UziCardExpired('Uzi card expired');
if (!$cert) {
return null;
}

if (!isset($cert['tbsCertificate']['subject']['rdnSequence'])) {
throw new UziCertificateNotUziException('No subject rdnSequence');
return null;
}

// Check if the certificate is a UZI certificate
Expand Down Expand Up @@ -82,9 +57,9 @@ public function getDataFromRequest(Request $request, array $caCerts = []): UziUs
}

if (!isset($value['otherName']['value']['ia5String'])) {
throw new UziCertificateException('No ia5String');
return null;
}
$subjectAltName = $value['otherName']['value']['ia5String'];

/**
* @var array $data
* Reference page 60
Expand All @@ -97,9 +72,11 @@ public function getDataFromRequest(Request $request, array $caCerts = []): UziUs
* [5] Role (reference page 89)
* [6] AGB code
*/
$subjectAltName = $value['otherName']['value']['ia5String'];
/** @var string[]|false $data */
$data = explode('-', $subjectAltName);
if (!is_array($data) || count($data) < 6) {
throw new UziCertificateException('Incorrect SAN found');
return null;
}

$user = new UziUser();
Expand All @@ -117,6 +94,6 @@ public function getDataFromRequest(Request $request, array $caCerts = []): UziUs
}
}

throw new UziCertificateNotUziException('No valid UZI card found');
return null;
}
}
27 changes: 9 additions & 18 deletions src/UziUser.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,15 @@
*/
class UziUser implements \JsonSerializable
{
/** @var string */
protected $agb_code = "";
/** @var string */
protected $card_type = "";
/** @var string */
protected $given_name = "";
/** @var string */
protected $oid_ca = "";
/** @var string */
protected $role = "";
/** @var string */
protected $subscriber_number = "";
/** @var string */
protected $sur_name = "";
/** @var string */
protected $uzi_number = "";
/** @var string */
protected $uzi_version = "";
protected string $agb_code = "";
protected string $card_type = "";
protected string $given_name = "";
protected string $oid_ca = "";
protected string $role = "";
protected string $subscriber_number = "";
protected string $sur_name = "";
protected string $uzi_number = "";
protected string $uzi_version = "";

/**
* @return string
Expand Down
90 changes: 58 additions & 32 deletions src/UziValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,70 +5,96 @@
use MinVWS\PUZI\Exceptions\UziAllowedRoleException;
use MinVWS\PUZI\Exceptions\UziAllowedTypeException;
use MinVWS\PUZI\Exceptions\UziCaException;
use MinVWS\PUZI\Exceptions\UziCardExpired;
use MinVWS\PUZI\Exceptions\UziCertificateException;
use MinVWS\PUZI\Exceptions\UziException;
use MinVWS\PUZI\Exceptions\UziVersionException;
use phpseclib3\File\X509;
use Symfony\Component\HttpFoundation\Request;

/**
* Class UziValidator
* @package MinVWS\Laravel\Puzi
*/
class UziValidator
{
/** @var bool */
protected $strictCAcheck;
/** @var array */
protected $allowedTypes;
/** @var array */
protected $allowedRoles;
protected UziReader $reader;
protected bool $strictCAcheck;
protected array $allowedTypes;
protected array $allowedRoles;
protected array $caCerts = [];

/**
* UziValidator constructor.
*
* @param bool $strictCaCheck
* @param array $allowedTypes
* @param array $allowedRoles
*/
public function __construct(bool $strictCaCheck, array $allowedTypes, array $allowedRoles)
{
public function __construct(
UziReader $reader,
bool $strictCaCheck,
array $allowedTypes,
array $allowedRoles,
array $caCerts = []
) {
$this->strictCAcheck = $strictCaCheck;
$this->allowedTypes = $allowedTypes;
$this->allowedRoles = $allowedRoles;
$this->reader = $reader;
$this->caCerts = $caCerts;
}

/**
* @param UziUser $user
* @return bool
*/
public function isValid(UziUser $user): bool
public function isValid(Request $request): bool
{
try {
$this->validate($user);
$this->validate($request);
} catch (UziException $e) {
return false;
}

return true;
}

/**
* @param UziUser $user
* @throws UziException
*/
public function validate(UziUser $user): void
public function validate(Request $request): void
{
if (!$request->server->has('SSL_CLIENT_VERIFY') || $request->server->get('SSL_CLIENT_VERIFY') !== 'SUCCESS') {
throw new UziCertificateException('Webserver client cert check not passed');
}
if (!$request->server->has('SSL_CLIENT_CERT')) {
throw new UziCertificateException('No client certificate presented');
}

$x509 = new X509();
$x509->loadX509($request->server->get('SSL_CLIENT_CERT'));
foreach ($this->caCerts as $caCert) {
$x509->loadCA($caCert);
}

if ($this->strictCAcheck === false) {
$x509->disableURLFetch();
}

$uziInfo = $this->reader->getDataFromRequest($request);
if (!$uziInfo) {
throw new UziCertificateException('No UZI data found in certificate');
}

if (
$this->strictCAcheck == true &&
$user->getOidCa() !== UziConstants::OID_CA_CARE_PROVIDER &&
$user->getOidCa() !== UziConstants::OID_CA_NAMED_EMPLOYEE
$this->strictCAcheck === true &&
$uziInfo->getOidCa() !== UziConstants::OID_CA_CARE_PROVIDER &&
$uziInfo->getOidCa() !== UziConstants::OID_CA_NAMED_EMPLOYEE
) {
throw new UziCaException('CA OID not UZI register Care Provider or named employee');
}
if ($user->getUziVersion() !== '1') {

if (! $x509->validateSignature(count($this->caCerts) > 0)) {
throw new UziCertificateException('Uzi certificate path not valid');
}
if (! $x509->validateDate()) {
throw new UziCardExpired('Uzi card expired');
}

if ($uziInfo->getUziVersion() !== '1') {
throw new UziVersionException('UZI version not 1');
}
if (!in_array($user->getCardType(), $this->allowedTypes)) {
if (!in_array($uziInfo->getCardType(), $this->allowedTypes)) {
throw new UziAllowedTypeException('UZI card type not allowed');
}
if (!in_array(substr($user->getRole(), 0, 3), $this->allowedRoles)) {
if (!in_array(substr($uziInfo->getRole(), 0, 3), $this->allowedRoles)) {
throw new UziAllowedRoleException("UZI card role not allowed");
}
}
Expand Down
Loading

0 comments on commit c4256cf

Please sign in to comment.