#13035 added JWT repository

This commit is contained in:
Daniel Cebrian 2024-04-02 16:53:13 +02:00
parent fbec8c0a05
commit 670829fa58
94 changed files with 3751 additions and 200 deletions

View File

@ -28,7 +28,8 @@
"php-di/php-di": "^7.0",
"zircote/swagger-php": "^4.8",
"doctrine/annotations": "^2.0",
"ramsey/uuid": "^4.7"
"ramsey/uuid": "^4.7",
"lcobucci/jwt": "^5.2"
},
"repositories": {
"phpchartjs": {

View File

@ -0,0 +1,139 @@
<?php
/**
* Class to JWT.
*
* @category Class
* @package Pandora FMS
* @subpackage Token
* @version 1.0.0
* @license See below
*
* ______ ___ _______ _______ ________
* | __ \.-----.--.--.--| |.-----.----.-----. | ___| | | __|
* | __/| _ | | _ || _ | _| _ | | ___| |__ |
* |___| |___._|__|__|_____||_____|__| |___._| |___| |__|_|__|_______|
*
* ============================================================================
* Copyright (c) 2005-2023 Pandora FMS
* Please see https://pandorafms.com/community/ for full contribution list
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation for version 2.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* ============================================================================
*/
// Begin.
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Encoding\JoseEncoder;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Token\Parser;
use Lcobucci\JWT\Validation\Constraint\SignedWith;
/**
* JWT Repository.
*/
final class JWTRepository
{
/**
* Signature
*
* @var string
*/
private $signature;
/**
* Token
*
* @var Token
*/
private $token;
/**
* Constructor
*
* @param string $_signature Signature of JWT.
*/
public function __construct(string $_signature)
{
$this->signature = $_signature;
}
/**
* Create token
*
* @return string
*/
public function create(): string
{
global $config;
$sha = new Sha256();
$configJWT = Configuration::forSymmetricSigner(
$sha,
InMemory::plainText($this->signature)
);
$now = new DateTimeImmutable();
$token = $configJWT->builder()->issuedAt($now)->canOnlyBeUsedAfter($now)->expiresAt($now->modify('+1 minute'))->withClaim('id_user', $config['id_user'])->getToken($configJWT->signer(), $configJWT->signingKey());
return $token->toString();
}
/**
* Validate a JWT, USE FIRST setToken().
*
* @return boolean
*/
public function validate():bool
{
$sha = new Sha256();
$configJWT = Configuration::forSymmetricSigner(
$sha,
InMemory::plainText($this->signature)
);
$signed = new SignedWith($sha, InMemory::plainText($this->signature));
$constraints = [$signed];
return $configJWT->validator()->validate($this->token, ...$constraints);
}
/**
* Get payload of token.
*
* @return object
*/
public function payload():object
{
return $this->token->claims();
}
public function setToken(string $tokenString)
{
$encoder = new JoseEncoder();
$parser = new Parser($encoder);
$this->token = $parser->parse($tokenString);
}
/**
* Generate random signature.
*
* @return string
*/
public static function generateSignature(): string
{
return bin2hex(random_bytes(32));
}
}

View File

@ -2,24 +2,6 @@
// autoload.php @generated by Composer
if (PHP_VERSION_ID < 50600) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, $err);
} elseif (!headers_sent()) {
echo $err;
}
}
trigger_error(
$err,
E_USER_ERROR
);
}
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit94a17e624d873685991e8ae888e00eb9::getLoader();

View File

@ -42,9 +42,6 @@ namespace Composer\Autoload;
*/
class ClassLoader
{
/** @var \Closure(string):void */
private static $includeFile;
/** @var ?string */
private $vendorDir;
@ -109,7 +106,6 @@ class ClassLoader
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
self::initializeIncludeClosure();
}
/**
@ -429,8 +425,7 @@ class ClassLoader
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
$includeFile = self::$includeFile;
$includeFile($file);
includeFile($file);
return true;
}
@ -560,26 +555,18 @@ class ClassLoader
return false;
}
/**
* @return void
*/
private static function initializeIncludeClosure()
{
if (self::$includeFile !== null) {
return;
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
*/
self::$includeFile = \Closure::bind(static function($file) {
include $file;
}, null, null);
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
* @private
*/
function includeFile($file)
{
include $file;
}

View File

@ -21,14 +21,12 @@ use Composer\Semver\VersionParser;
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
*
* To require its presence, you can require `composer-runtime-api ^2.0`
*
* @final
*/
class InstalledVersions
{
/**
* @var mixed[]|null
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
* @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null
*/
private static $installed;
@ -39,7 +37,7 @@ class InstalledVersions
/**
* @var array[]
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
* @psalm-var array<string, array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
*/
private static $installedByVendor = array();
@ -98,7 +96,7 @@ class InstalledVersions
{
foreach (self::getInstalled() as $installed) {
if (isset($installed['versions'][$packageName])) {
return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
}
}
@ -119,7 +117,7 @@ class InstalledVersions
*/
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
$constraint = $parser->parseConstraints((string) $constraint);
$constraint = $parser->parseConstraints($constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
return $provided->matches($constraint);
@ -243,7 +241,7 @@ class InstalledVersions
/**
* @return array
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
* @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
*/
public static function getRootPackage()
{
@ -257,7 +255,7 @@ class InstalledVersions
*
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
* @return array[]
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
* @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}
*/
public static function getRawData()
{
@ -266,7 +264,7 @@ class InstalledVersions
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C' && is_file(__DIR__ . '/installed.php')) {
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = include __DIR__ . '/installed.php';
} else {
self::$installed = array();
@ -280,7 +278,7 @@ class InstalledVersions
* Returns the raw data of all installed.php which are currently loaded for custom implementations
*
* @return array[]
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
*/
public static function getAllRawData()
{
@ -303,7 +301,7 @@ class InstalledVersions
* @param array[] $data A vendor/composer/installed.php data set
* @return void
*
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
* @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} $data
*/
public static function reload($data)
{
@ -313,7 +311,7 @@ class InstalledVersions
/**
* @return array[]
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
*/
private static function getInstalled()
{
@ -328,9 +326,7 @@ class InstalledVersions
if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir];
} elseif (is_file($vendorDir.'/composer/installed.php')) {
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
$required = require $vendorDir.'/composer/installed.php';
$installed[] = self::$installedByVendor[$vendorDir] = $required;
$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
self::$installed = $installed[count($installed) - 1];
}
@ -341,18 +337,13 @@ class InstalledVersions
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C' && is_file(__DIR__ . '/installed.php')) {
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
$required = require __DIR__ . '/installed.php';
self::$installed = $required;
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = require __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
if (self::$installed !== array()) {
$installed[] = self::$installed;
}
$installed[] = self::$installed;
return $installed;
}

View File

@ -2,7 +2,7 @@
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(__DIR__);
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(

View File

@ -2,33 +2,28 @@
// autoload_files.php @generated by Composer
$vendorDir = dirname(__DIR__);
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'e8aa6e4b5a1db2f56ae794f1505391a8' => $vendorDir . '/amphp/amp/lib/functions.php',
'76cd0796156622033397994f25b0d8fc' => $vendorDir . '/amphp/amp/lib/Internal/functions.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
'6cd5651c4fef5ed6b63e8d8b8ffbf3cc' => $vendorDir . '/amphp/byte-stream/lib/functions.php',
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
'6cd5651c4fef5ed6b63e8d8b8ffbf3cc' => $vendorDir . '/amphp/byte-stream/lib/functions.php',
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
'3da389f428d8ee50333e4391c3f45046' => $vendorDir . '/amphp/serialization/src/functions.php',
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
'8dc56fe697ca93c4b40d876df1c94584' => $vendorDir . '/amphp/process/lib/functions.php',
'bcb7d4fc55f4b1a7e10f5806723e9892' => $vendorDir . '/amphp/sync/src/functions.php',
'e187e371b30897d6dc51cac6a8c94ff6' => $vendorDir . '/amphp/sync/src/ConcurrentIterator/functions.php',
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
'430de19db8b7ee88fdbe5c545d82d33d' => $vendorDir . '/amphp/parallel/lib/Context/functions.php',
'888e1afeed2e8d13ef5a662692091e6e' => $vendorDir . '/amphp/parallel/lib/Sync/functions.php',
'384cf4f2eb4d2f896db72315a76066ad' => $vendorDir . '/amphp/parallel/lib/Worker/functions.php',
'6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
'253c157292f75eb38082b5acb06f3f01' => $vendorDir . '/nikic/fast-route/src/functions.php',
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
'def43f6c87e4f8dfd0c9e1b1bab14fe8' => $vendorDir . '/symfony/polyfill-iconv/bootstrap.php',
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
'861372841bb4b8ba9fdd215894666f40' => $vendorDir . '/amphp/parallel-functions/src/functions.php',
'07d7f1a47144818725fd8d91a907ac57' => $vendorDir . '/laminas/laminas-diactoros/src/functions/create_uploaded_file.php',
'da94ac5d3ca7d2dbab84ce561ce72bfd' => $vendorDir . '/laminas/laminas-diactoros/src/functions/marshal_headers_from_sapi.php',
'3d97c8dcdfba8cb85d3b34f116bb248b' => $vendorDir . '/laminas/laminas-diactoros/src/functions/marshal_method_from_sapi.php',
@ -36,6 +31,11 @@ return array(
'de95e0ac670b27c84ef8c5ac41fc1b34' => $vendorDir . '/laminas/laminas-diactoros/src/functions/normalize_server.php',
'b6c2870932b0250c10334a86dcb33c7f' => $vendorDir . '/laminas/laminas-diactoros/src/functions/normalize_uploaded_files.php',
'd02cf21124526632320d6f20b1bbf905' => $vendorDir . '/laminas/laminas-diactoros/src/functions/parse_cookie_header.php',
'6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
'253c157292f75eb38082b5acb06f3f01' => $vendorDir . '/nikic/fast-route/src/functions.php',
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
'def43f6c87e4f8dfd0c9e1b1bab14fe8' => $vendorDir . '/symfony/polyfill-iconv/bootstrap.php',
'861372841bb4b8ba9fdd215894666f40' => $vendorDir . '/amphp/parallel-functions/src/functions.php',
'db356362850385d08a5381de2638b5fd' => $vendorDir . '/mpdf/mpdf/src/functions.php',
'b33e3d135e5d9e47d845c576147bda89' => $vendorDir . '/php-di/php-di/src/functions.php',
'e39a8b23c42d4e1452234d762b03835a' => $vendorDir . '/ramsey/uuid/src/functions.php',

View File

@ -2,7 +2,7 @@
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(__DIR__);
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(

View File

@ -2,7 +2,7 @@
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(__DIR__);
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
@ -31,6 +31,7 @@ return array(
'Psr\\Http\\Server\\' => array($vendorDir . '/psr/http-server-handler/src', $vendorDir . '/psr/http-server-middleware/src'),
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'),
'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
'Psr\\Clock\\' => array($vendorDir . '/psr/clock/src'),
'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'),
'PandoraFMS\\Enterprise\\' => array($baseDir . '/enterprise/include/lib'),
'PandoraFMS\\' => array($baseDir . '/include/lib'),
@ -42,6 +43,7 @@ return array(
'Mpdf\\' => array($vendorDir . '/mpdf/mpdf/src'),
'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
'Models\\' => array($baseDir . '/include/rest-api/models'),
'Lcobucci\\JWT\\' => array($vendorDir . '/lcobucci/jwt/src'),
'Laravel\\SerializableClosure\\' => array($vendorDir . '/laravel/serializable-closure/src'),
'Laminas\\Json\\' => array($vendorDir . '/laminas/laminas-json/src'),
'Laminas\\Diactoros\\' => array($vendorDir . '/laminas/laminas-diactoros/src'),

View File

@ -25,26 +25,56 @@ class ComposerAutoloaderInit94a17e624d873685991e8ae888e00eb9
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInit94a17e624d873685991e8ae888e00eb9', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
spl_autoload_unregister(array('ComposerAutoloaderInit94a17e624d873685991e8ae888e00eb9', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit94a17e624d873685991e8ae888e00eb9::getInitializer($loader));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit94a17e624d873685991e8ae888e00eb9::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->register(true);
$filesToLoad = \Composer\Autoload\ComposerStaticInit94a17e624d873685991e8ae888e00eb9::$files;
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
require $file;
}
}, null, null);
foreach ($filesToLoad as $fileIdentifier => $file) {
$requireFile($fileIdentifier, $file);
if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInit94a17e624d873685991e8ae888e00eb9::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire94a17e624d873685991e8ae888e00eb9($fileIdentifier, $file);
}
return $loader;
}
}
/**
* @param string $fileIdentifier
* @param string $file
* @return void
*/
function composerRequire94a17e624d873685991e8ae888e00eb9($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
require $file;
}
}

View File

@ -10,26 +10,21 @@ class ComposerStaticInit94a17e624d873685991e8ae888e00eb9
'e8aa6e4b5a1db2f56ae794f1505391a8' => __DIR__ . '/..' . '/amphp/amp/lib/functions.php',
'76cd0796156622033397994f25b0d8fc' => __DIR__ . '/..' . '/amphp/amp/lib/Internal/functions.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
'6cd5651c4fef5ed6b63e8d8b8ffbf3cc' => __DIR__ . '/..' . '/amphp/byte-stream/lib/functions.php',
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
'6cd5651c4fef5ed6b63e8d8b8ffbf3cc' => __DIR__ . '/..' . '/amphp/byte-stream/lib/functions.php',
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
'3da389f428d8ee50333e4391c3f45046' => __DIR__ . '/..' . '/amphp/serialization/src/functions.php',
'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
'8dc56fe697ca93c4b40d876df1c94584' => __DIR__ . '/..' . '/amphp/process/lib/functions.php',
'bcb7d4fc55f4b1a7e10f5806723e9892' => __DIR__ . '/..' . '/amphp/sync/src/functions.php',
'e187e371b30897d6dc51cac6a8c94ff6' => __DIR__ . '/..' . '/amphp/sync/src/ConcurrentIterator/functions.php',
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
'430de19db8b7ee88fdbe5c545d82d33d' => __DIR__ . '/..' . '/amphp/parallel/lib/Context/functions.php',
'888e1afeed2e8d13ef5a662692091e6e' => __DIR__ . '/..' . '/amphp/parallel/lib/Sync/functions.php',
'384cf4f2eb4d2f896db72315a76066ad' => __DIR__ . '/..' . '/amphp/parallel/lib/Worker/functions.php',
'6124b4c8570aa390c21fafd04a26c69f' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
'253c157292f75eb38082b5acb06f3f01' => __DIR__ . '/..' . '/nikic/fast-route/src/functions.php',
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
'def43f6c87e4f8dfd0c9e1b1bab14fe8' => __DIR__ . '/..' . '/symfony/polyfill-iconv/bootstrap.php',
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
'861372841bb4b8ba9fdd215894666f40' => __DIR__ . '/..' . '/amphp/parallel-functions/src/functions.php',
'07d7f1a47144818725fd8d91a907ac57' => __DIR__ . '/..' . '/laminas/laminas-diactoros/src/functions/create_uploaded_file.php',
'da94ac5d3ca7d2dbab84ce561ce72bfd' => __DIR__ . '/..' . '/laminas/laminas-diactoros/src/functions/marshal_headers_from_sapi.php',
'3d97c8dcdfba8cb85d3b34f116bb248b' => __DIR__ . '/..' . '/laminas/laminas-diactoros/src/functions/marshal_method_from_sapi.php',
@ -37,6 +32,11 @@ class ComposerStaticInit94a17e624d873685991e8ae888e00eb9
'de95e0ac670b27c84ef8c5ac41fc1b34' => __DIR__ . '/..' . '/laminas/laminas-diactoros/src/functions/normalize_server.php',
'b6c2870932b0250c10334a86dcb33c7f' => __DIR__ . '/..' . '/laminas/laminas-diactoros/src/functions/normalize_uploaded_files.php',
'd02cf21124526632320d6f20b1bbf905' => __DIR__ . '/..' . '/laminas/laminas-diactoros/src/functions/parse_cookie_header.php',
'6124b4c8570aa390c21fafd04a26c69f' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
'253c157292f75eb38082b5acb06f3f01' => __DIR__ . '/..' . '/nikic/fast-route/src/functions.php',
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
'def43f6c87e4f8dfd0c9e1b1bab14fe8' => __DIR__ . '/..' . '/symfony/polyfill-iconv/bootstrap.php',
'861372841bb4b8ba9fdd215894666f40' => __DIR__ . '/..' . '/amphp/parallel-functions/src/functions.php',
'db356362850385d08a5381de2638b5fd' => __DIR__ . '/..' . '/mpdf/mpdf/src/functions.php',
'b33e3d135e5d9e47d845c576147bda89' => __DIR__ . '/..' . '/php-di/php-di/src/functions.php',
'e39a8b23c42d4e1452234d762b03835a' => __DIR__ . '/..' . '/ramsey/uuid/src/functions.php',
@ -90,6 +90,7 @@ class ComposerStaticInit94a17e624d873685991e8ae888e00eb9
'Psr\\Http\\Server\\' => 16,
'Psr\\Http\\Message\\' => 17,
'Psr\\Container\\' => 14,
'Psr\\Clock\\' => 10,
'Psr\\Cache\\' => 10,
'PandoraFMS\\Enterprise\\' => 22,
'PandoraFMS\\' => 11,
@ -113,6 +114,7 @@ class ComposerStaticInit94a17e624d873685991e8ae888e00eb9
),
'L' =>
array (
'Lcobucci\\JWT\\' => 13,
'Laravel\\SerializableClosure\\' => 28,
'Laminas\\Json\\' => 13,
'Laminas\\Diactoros\\' => 18,
@ -271,6 +273,10 @@ class ComposerStaticInit94a17e624d873685991e8ae888e00eb9
array (
0 => __DIR__ . '/..' . '/psr/container/src',
),
'Psr\\Clock\\' =>
array (
0 => __DIR__ . '/..' . '/psr/clock/src',
),
'Psr\\Cache\\' =>
array (
0 => __DIR__ . '/..' . '/psr/cache/src',
@ -315,6 +321,10 @@ class ComposerStaticInit94a17e624d873685991e8ae888e00eb9
array (
0 => __DIR__ . '/../..' . '/include/rest-api/models',
),
'Lcobucci\\JWT\\' =>
array (
0 => __DIR__ . '/..' . '/lcobucci/jwt/src',
),
'Laravel\\SerializableClosure\\' =>
array (
0 => __DIR__ . '/..' . '/laravel/serializable-closure/src',

View File

@ -1616,6 +1616,82 @@
},
"install-path": "../laravel/serializable-closure"
},
{
"name": "lcobucci/jwt",
"version": "5.2.0",
"version_normalized": "5.2.0.0",
"source": {
"type": "git",
"url": "https://github.com/lcobucci/jwt.git",
"reference": "0ba88aed12c04bd2ed9924f500673f32b67a6211"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/lcobucci/jwt/zipball/0ba88aed12c04bd2ed9924f500673f32b67a6211",
"reference": "0ba88aed12c04bd2ed9924f500673f32b67a6211",
"shasum": ""
},
"require": {
"ext-openssl": "*",
"ext-sodium": "*",
"php": "~8.1.0 || ~8.2.0 || ~8.3.0",
"psr/clock": "^1.0"
},
"require-dev": {
"infection/infection": "^0.27.0",
"lcobucci/clock": "^3.0",
"lcobucci/coding-standard": "^11.0",
"phpbench/phpbench": "^1.2.9",
"phpstan/extension-installer": "^1.2",
"phpstan/phpstan": "^1.10.7",
"phpstan/phpstan-deprecation-rules": "^1.1.3",
"phpstan/phpstan-phpunit": "^1.3.10",
"phpstan/phpstan-strict-rules": "^1.5.0",
"phpunit/phpunit": "^10.2.6"
},
"suggest": {
"lcobucci/clock": ">= 3.0"
},
"time": "2023-11-20T21:17:42+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"Lcobucci\\JWT\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Luís Cobucci",
"email": "lcobucci@gmail.com",
"role": "Developer"
}
],
"description": "A simple library to work with JSON Web Token and JSON Web Signature",
"keywords": [
"JWS",
"jwt"
],
"support": {
"issues": "https://github.com/lcobucci/jwt/issues",
"source": "https://github.com/lcobucci/jwt/tree/5.2.0"
},
"funding": [
{
"url": "https://github.com/lcobucci",
"type": "github"
},
{
"url": "https://www.patreon.com/lcobucci",
"type": "patreon"
}
],
"install-path": "../lcobucci/jwt"
},
{
"name": "monolog/monolog",
"version": "3.5.0",
@ -2406,6 +2482,57 @@
},
"install-path": "../psr/cache"
},
{
"name": "psr/clock",
"version": "1.0.0",
"version_normalized": "1.0.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/clock.git",
"reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d",
"reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d",
"shasum": ""
},
"require": {
"php": "^7.0 || ^8.0"
},
"time": "2022-11-25T14:36:26+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"Psr\\Clock\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for reading the clock.",
"homepage": "https://github.com/php-fig/clock",
"keywords": [
"clock",
"now",
"psr",
"psr-20",
"time"
],
"support": {
"issues": "https://github.com/php-fig/clock/issues",
"source": "https://github.com/php-fig/clock/tree/1.0.0"
},
"install-path": "../psr/clock"
},
{
"name": "psr/container",
"version": "2.0.2",

View File

@ -1,328 +1,337 @@
<?php return array(
'root' => array(
'name' => 'pandorafms/console',
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'reference' => '8bf414b51f3333555f84df8b23614ca4b68c7fd4',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => 'fbec8c0a05f72bfcc2044c0a4857d5e31f534ad5',
'name' => 'pandorafms/console',
'dev' => true,
),
'versions' => array(
'amphp/amp' => array(
'pretty_version' => 'v2.6.2',
'version' => '2.6.2.0',
'reference' => '9d5100cebffa729aaffecd3ad25dc5aeea4f13bb',
'type' => 'library',
'install_path' => __DIR__ . '/../amphp/amp',
'aliases' => array(),
'reference' => '9d5100cebffa729aaffecd3ad25dc5aeea4f13bb',
'dev_requirement' => false,
),
'amphp/byte-stream' => array(
'pretty_version' => 'v1.8.1',
'version' => '1.8.1.0',
'reference' => 'acbd8002b3536485c997c4e019206b3f10ca15bd',
'type' => 'library',
'install_path' => __DIR__ . '/../amphp/byte-stream',
'aliases' => array(),
'reference' => 'acbd8002b3536485c997c4e019206b3f10ca15bd',
'dev_requirement' => false,
),
'amphp/parallel' => array(
'pretty_version' => 'v1.4.3',
'version' => '1.4.3.0',
'reference' => '3aac213ba7858566fd83d38ccb85b91b2d652cb0',
'type' => 'library',
'install_path' => __DIR__ . '/../amphp/parallel',
'aliases' => array(),
'reference' => '3aac213ba7858566fd83d38ccb85b91b2d652cb0',
'dev_requirement' => false,
),
'amphp/parallel-functions' => array(
'pretty_version' => 'v1.1.0',
'version' => '1.1.0.0',
'reference' => '04e92fcacfc921a56dfe12c23b3265e62593a7cb',
'type' => 'library',
'install_path' => __DIR__ . '/../amphp/parallel-functions',
'aliases' => array(),
'reference' => '04e92fcacfc921a56dfe12c23b3265e62593a7cb',
'dev_requirement' => false,
),
'amphp/parser' => array(
'pretty_version' => 'v1.1.0',
'version' => '1.1.0.0',
'reference' => 'ff1de4144726c5dad5fab97f66692ebe8de3e151',
'type' => 'library',
'install_path' => __DIR__ . '/../amphp/parser',
'aliases' => array(),
'reference' => 'ff1de4144726c5dad5fab97f66692ebe8de3e151',
'dev_requirement' => false,
),
'amphp/process' => array(
'pretty_version' => 'v1.1.4',
'version' => '1.1.4.0',
'reference' => '76e9495fd6818b43a20167cb11d8a67f7744ee0f',
'type' => 'library',
'install_path' => __DIR__ . '/../amphp/process',
'aliases' => array(),
'reference' => '76e9495fd6818b43a20167cb11d8a67f7744ee0f',
'dev_requirement' => false,
),
'amphp/serialization' => array(
'pretty_version' => 'v1.0.0',
'version' => '1.0.0.0',
'reference' => '693e77b2fb0b266c3c7d622317f881de44ae94a1',
'type' => 'library',
'install_path' => __DIR__ . '/../amphp/serialization',
'aliases' => array(),
'reference' => '693e77b2fb0b266c3c7d622317f881de44ae94a1',
'dev_requirement' => false,
),
'amphp/sync' => array(
'pretty_version' => 'v1.4.2',
'version' => '1.4.2.0',
'reference' => '85ab06764f4f36d63b1356b466df6111cf4b89cf',
'type' => 'library',
'install_path' => __DIR__ . '/../amphp/sync',
'aliases' => array(),
'reference' => '85ab06764f4f36d63b1356b466df6111cf4b89cf',
'dev_requirement' => false,
),
'artica/phpchartjs' => array(
'pretty_version' => 'v1.0.2',
'version' => '1.0.2.0',
'reference' => '681980c084ad505f9dc811d3d1f02ffc9442ccee',
'type' => 'package',
'install_path' => __DIR__ . '/../artica/phpchartjs',
'aliases' => array(),
'reference' => '681980c084ad505f9dc811d3d1f02ffc9442ccee',
'dev_requirement' => false,
),
'brick/math' => array(
'pretty_version' => '0.11.0',
'version' => '0.11.0.0',
'reference' => '0ad82ce168c82ba30d1c01ec86116ab52f589478',
'type' => 'library',
'install_path' => __DIR__ . '/../brick/math',
'aliases' => array(),
'reference' => '0ad82ce168c82ba30d1c01ec86116ab52f589478',
'dev_requirement' => false,
),
'chrome-php/chrome' => array(
'pretty_version' => 'v1.10.0',
'version' => '1.10.0.0',
'reference' => '2b7cb13e618602d13bdede20b6b7ae478f3f6eaa',
'type' => 'library',
'install_path' => __DIR__ . '/../chrome-php/chrome',
'aliases' => array(),
'reference' => '2b7cb13e618602d13bdede20b6b7ae478f3f6eaa',
'dev_requirement' => false,
),
'chrome-php/wrench' => array(
'pretty_version' => 'v1.5.0',
'version' => '1.5.0.0',
'reference' => '725246324339e5fd5d798361b561e81004324f96',
'type' => 'library',
'install_path' => __DIR__ . '/../chrome-php/wrench',
'aliases' => array(),
'reference' => '725246324339e5fd5d798361b561e81004324f96',
'dev_requirement' => false,
),
'doctrine/annotations' => array(
'pretty_version' => '2.0.1',
'version' => '2.0.1.0',
'reference' => 'e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f',
'type' => 'library',
'install_path' => __DIR__ . '/../doctrine/annotations',
'aliases' => array(),
'reference' => 'e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f',
'dev_requirement' => false,
),
'doctrine/deprecations' => array(
'pretty_version' => '1.1.3',
'version' => '1.1.3.0',
'reference' => 'dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab',
'type' => 'library',
'install_path' => __DIR__ . '/../doctrine/deprecations',
'aliases' => array(),
'reference' => 'dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab',
'dev_requirement' => false,
),
'doctrine/lexer' => array(
'pretty_version' => '2.1.0',
'version' => '2.1.0.0',
'reference' => '39ab8fcf5a51ce4b85ca97c7a7d033eb12831124',
'type' => 'library',
'install_path' => __DIR__ . '/../doctrine/lexer',
'aliases' => array(),
'reference' => '39ab8fcf5a51ce4b85ca97c7a7d033eb12831124',
'dev_requirement' => false,
),
'egulias/email-validator' => array(
'pretty_version' => '3.2.6',
'version' => '3.2.6.0',
'reference' => 'e5997fa97e8790cdae03a9cbd5e78e45e3c7bda7',
'type' => 'library',
'install_path' => __DIR__ . '/../egulias/email-validator',
'aliases' => array(),
'reference' => 'e5997fa97e8790cdae03a9cbd5e78e45e3c7bda7',
'dev_requirement' => false,
),
'evenement/evenement' => array(
'pretty_version' => 'v3.0.2',
'version' => '3.0.2.0',
'reference' => '0a16b0d71ab13284339abb99d9d2bd813640efbc',
'type' => 'library',
'install_path' => __DIR__ . '/../evenement/evenement',
'aliases' => array(),
'reference' => '0a16b0d71ab13284339abb99d9d2bd813640efbc',
'dev_requirement' => false,
),
'fig/http-message-util' => array(
'pretty_version' => '1.1.5',
'version' => '1.1.5.0',
'reference' => '9d94dc0154230ac39e5bf89398b324a86f63f765',
'type' => 'library',
'install_path' => __DIR__ . '/../fig/http-message-util',
'aliases' => array(),
'reference' => '9d94dc0154230ac39e5bf89398b324a86f63f765',
'dev_requirement' => false,
),
'guzzlehttp/psr7' => array(
'pretty_version' => '2.6.2',
'version' => '2.6.2.0',
'reference' => '45b30f99ac27b5ca93cb4831afe16285f57b8221',
'type' => 'library',
'install_path' => __DIR__ . '/../guzzlehttp/psr7',
'aliases' => array(),
'reference' => '45b30f99ac27b5ca93cb4831afe16285f57b8221',
'dev_requirement' => false,
),
'halfpastfouram/collection' => array(
'pretty_version' => 'v1.0.0',
'version' => '1.0.0.0',
'reference' => '0862d0b431fef9dc2245518dc06b86ff00dcd102',
'type' => 'package',
'install_path' => __DIR__ . '/../halfpastfouram/collection',
'aliases' => array(),
'reference' => '0862d0b431fef9dc2245518dc06b86ff00dcd102',
'dev_requirement' => false,
),
'laminas/laminas-diactoros' => array(
'pretty_version' => '3.3.0',
'version' => '3.3.0.0',
'reference' => '4db52734837c60259c9b2d7caf08eef8f7f9b9ac',
'type' => 'library',
'install_path' => __DIR__ . '/../laminas/laminas-diactoros',
'aliases' => array(),
'reference' => '4db52734837c60259c9b2d7caf08eef8f7f9b9ac',
'dev_requirement' => false,
),
'laminas/laminas-json' => array(
'pretty_version' => '3.6.0',
'version' => '3.6.0.0',
'reference' => '53ff787b20b77197f38680c737e8dfffa846b85b',
'type' => 'library',
'install_path' => __DIR__ . '/../laminas/laminas-json',
'aliases' => array(),
'reference' => '53ff787b20b77197f38680c737e8dfffa846b85b',
'dev_requirement' => false,
),
'laravel/serializable-closure' => array(
'pretty_version' => 'v1.3.3',
'version' => '1.3.3.0',
'reference' => '3dbf8a8e914634c48d389c1234552666b3d43754',
'type' => 'library',
'install_path' => __DIR__ . '/../laravel/serializable-closure',
'aliases' => array(),
'reference' => '3dbf8a8e914634c48d389c1234552666b3d43754',
'dev_requirement' => false,
),
'lcobucci/jwt' => array(
'pretty_version' => '5.2.0',
'version' => '5.2.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../lcobucci/jwt',
'aliases' => array(),
'reference' => '0ba88aed12c04bd2ed9924f500673f32b67a6211',
'dev_requirement' => false,
),
'monolog/monolog' => array(
'pretty_version' => '3.5.0',
'version' => '3.5.0.0',
'reference' => 'c915e2634718dbc8a4a15c61b0e62e7a44e14448',
'type' => 'library',
'install_path' => __DIR__ . '/../monolog/monolog',
'aliases' => array(),
'reference' => 'c915e2634718dbc8a4a15c61b0e62e7a44e14448',
'dev_requirement' => false,
),
'mpdf/mpdf' => array(
'pretty_version' => 'v8.2.2',
'version' => '8.2.2.0',
'reference' => '596a87b876d7793be7be060a8ac13424de120dd5',
'type' => 'library',
'install_path' => __DIR__ . '/../mpdf/mpdf',
'aliases' => array(),
'reference' => '596a87b876d7793be7be060a8ac13424de120dd5',
'dev_requirement' => false,
),
'mpdf/psr-http-message-shim' => array(
'pretty_version' => '1.0.0',
'version' => '1.0.0.0',
'reference' => '3206e6b80b6d2479e148ee497e9f2bebadc919db',
'type' => 'library',
'install_path' => __DIR__ . '/../mpdf/psr-http-message-shim',
'aliases' => array(),
'reference' => '3206e6b80b6d2479e148ee497e9f2bebadc919db',
'dev_requirement' => false,
),
'mpdf/psr-log-aware-trait' => array(
'pretty_version' => 'v3.0.0',
'version' => '3.0.0.0',
'reference' => 'a633da6065e946cc491e1c962850344bb0bf3e78',
'type' => 'library',
'install_path' => __DIR__ . '/../mpdf/psr-log-aware-trait',
'aliases' => array(),
'reference' => 'a633da6065e946cc491e1c962850344bb0bf3e78',
'dev_requirement' => false,
),
'myclabs/deep-copy' => array(
'pretty_version' => '1.11.1',
'version' => '1.11.1.0',
'reference' => '7284c22080590fb39f2ffa3e9057f10a4ddd0e0c',
'type' => 'library',
'install_path' => __DIR__ . '/../myclabs/deep-copy',
'aliases' => array(),
'reference' => '7284c22080590fb39f2ffa3e9057f10a4ddd0e0c',
'dev_requirement' => false,
),
'nikic/fast-route' => array(
'pretty_version' => 'v1.3.0',
'version' => '1.3.0.0',
'reference' => '181d480e08d9476e61381e04a71b34dc0432e812',
'type' => 'library',
'install_path' => __DIR__ . '/../nikic/fast-route',
'aliases' => array(),
'reference' => '181d480e08d9476e61381e04a71b34dc0432e812',
'dev_requirement' => false,
),
'nyholm/psr7' => array(
'pretty_version' => '1.8.1',
'version' => '1.8.1.0',
'reference' => 'aa5fc277a4f5508013d571341ade0c3886d4d00e',
'type' => 'library',
'install_path' => __DIR__ . '/../nyholm/psr7',
'aliases' => array(),
'reference' => 'aa5fc277a4f5508013d571341ade0c3886d4d00e',
'dev_requirement' => false,
),
'nyholm/psr7-server' => array(
'pretty_version' => '1.1.0',
'version' => '1.1.0.0',
'reference' => '4335801d851f554ca43fa6e7d2602141538854dc',
'type' => 'library',
'install_path' => __DIR__ . '/../nyholm/psr7-server',
'aliases' => array(),
'reference' => '4335801d851f554ca43fa6e7d2602141538854dc',
'dev_requirement' => false,
),
'pandorafms/console' => array(
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'reference' => '8bf414b51f3333555f84df8b23614ca4b68c7fd4',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => 'fbec8c0a05f72bfcc2044c0a4857d5e31f534ad5',
'dev_requirement' => false,
),
'paragonie/random_compat' => array(
'pretty_version' => 'v9.99.100',
'version' => '9.99.100.0',
'reference' => '996434e5492cb4c3edcb9168db6fbb1359ef965a',
'type' => 'library',
'install_path' => __DIR__ . '/../paragonie/random_compat',
'aliases' => array(),
'reference' => '996434e5492cb4c3edcb9168db6fbb1359ef965a',
'dev_requirement' => false,
),
'php-di/invoker' => array(
'pretty_version' => '2.3.4',
'version' => '2.3.4.0',
'reference' => '33234b32dafa8eb69202f950a1fc92055ed76a86',
'type' => 'library',
'install_path' => __DIR__ . '/../php-di/invoker',
'aliases' => array(),
'reference' => '33234b32dafa8eb69202f950a1fc92055ed76a86',
'dev_requirement' => false,
),
'php-di/php-di' => array(
'pretty_version' => '7.0.6',
'version' => '7.0.6.0',
'reference' => '8097948a89f6ec782839b3e958432f427cac37fd',
'type' => 'library',
'install_path' => __DIR__ . '/../php-di/php-di',
'aliases' => array(),
'reference' => '8097948a89f6ec782839b3e958432f427cac37fd',
'dev_requirement' => false,
),
'php-http/message-factory-implementation' => array(
@ -334,19 +343,28 @@
'psr/cache' => array(
'pretty_version' => '3.0.0',
'version' => '3.0.0.0',
'reference' => 'aa5030cfa5405eccfdcb1083ce040c2cb8d253bf',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/cache',
'aliases' => array(),
'reference' => 'aa5030cfa5405eccfdcb1083ce040c2cb8d253bf',
'dev_requirement' => false,
),
'psr/clock' => array(
'pretty_version' => '1.0.0',
'version' => '1.0.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/clock',
'aliases' => array(),
'reference' => 'e41a24703d4560fd0acb709162f73b8adfc3aa0d',
'dev_requirement' => false,
),
'psr/container' => array(
'pretty_version' => '2.0.2',
'version' => '2.0.2.0',
'reference' => 'c71ecc56dfe541dbd90c5360474fbc405f8d5963',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/container',
'aliases' => array(),
'reference' => 'c71ecc56dfe541dbd90c5360474fbc405f8d5963',
'dev_requirement' => false,
),
'psr/container-implementation' => array(
@ -358,10 +376,10 @@
'psr/http-factory' => array(
'pretty_version' => '1.0.2',
'version' => '1.0.2.0',
'reference' => 'e616d01114759c4c489f93b099585439f795fe35',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/http-factory',
'aliases' => array(),
'reference' => 'e616d01114759c4c489f93b099585439f795fe35',
'dev_requirement' => false,
),
'psr/http-factory-implementation' => array(
@ -374,10 +392,10 @@
'psr/http-message' => array(
'pretty_version' => '1.1',
'version' => '1.1.0.0',
'reference' => 'cb6ce4845ce34a8ad9e68117c10ee90a29919eba',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/http-message',
'aliases' => array(),
'reference' => 'cb6ce4845ce34a8ad9e68117c10ee90a29919eba',
'dev_requirement' => false,
),
'psr/http-message-implementation' => array(
@ -390,28 +408,28 @@
'psr/http-server-handler' => array(
'pretty_version' => '1.0.2',
'version' => '1.0.2.0',
'reference' => '84c4fb66179be4caaf8e97bd239203245302e7d4',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/http-server-handler',
'aliases' => array(),
'reference' => '84c4fb66179be4caaf8e97bd239203245302e7d4',
'dev_requirement' => false,
),
'psr/http-server-middleware' => array(
'pretty_version' => '1.0.2',
'version' => '1.0.2.0',
'reference' => 'c1481f747daaa6a0782775cd6a8c26a1bf4a3829',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/http-server-middleware',
'aliases' => array(),
'reference' => 'c1481f747daaa6a0782775cd6a8c26a1bf4a3829',
'dev_requirement' => false,
),
'psr/log' => array(
'pretty_version' => '3.0.0',
'version' => '3.0.0.0',
'reference' => 'fe5ea303b0887d5caefd3d431c3e61ad47037001',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/log',
'aliases' => array(),
'reference' => 'fe5ea303b0887d5caefd3d431c3e61ad47037001',
'dev_requirement' => false,
),
'psr/log-implementation' => array(
@ -423,28 +441,28 @@
'ralouphie/getallheaders' => array(
'pretty_version' => '3.0.3',
'version' => '3.0.3.0',
'reference' => '120b605dfeb996808c31b6477290a714d356e822',
'type' => 'library',
'install_path' => __DIR__ . '/../ralouphie/getallheaders',
'aliases' => array(),
'reference' => '120b605dfeb996808c31b6477290a714d356e822',
'dev_requirement' => false,
),
'ramsey/collection' => array(
'pretty_version' => '2.0.0',
'version' => '2.0.0.0',
'reference' => 'a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5',
'type' => 'library',
'install_path' => __DIR__ . '/../ramsey/collection',
'aliases' => array(),
'reference' => 'a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5',
'dev_requirement' => false,
),
'ramsey/uuid' => array(
'pretty_version' => '4.7.5',
'version' => '4.7.5.0',
'reference' => '5f0df49ae5ad6efb7afa69e6bfab4e5b1e080d8e',
'type' => 'library',
'install_path' => __DIR__ . '/../ramsey/uuid',
'aliases' => array(),
'reference' => '5f0df49ae5ad6efb7afa69e6bfab4e5b1e080d8e',
'dev_requirement' => false,
),
'rhumsaa/uuid' => array(
@ -456,172 +474,172 @@
'setasign/fpdi' => array(
'pretty_version' => 'v2.6.0',
'version' => '2.6.0.0',
'reference' => 'a6db878129ec6c7e141316ee71872923e7f1b7ad',
'type' => 'library',
'install_path' => __DIR__ . '/../setasign/fpdi',
'aliases' => array(),
'reference' => 'a6db878129ec6c7e141316ee71872923e7f1b7ad',
'dev_requirement' => false,
),
'slim/psr7' => array(
'pretty_version' => '1.6.1',
'version' => '1.6.1.0',
'reference' => '72d2b2bac94ab4575d369f605dbfafbe168d3163',
'type' => 'library',
'install_path' => __DIR__ . '/../slim/psr7',
'aliases' => array(),
'reference' => '72d2b2bac94ab4575d369f605dbfafbe168d3163',
'dev_requirement' => false,
),
'slim/slim' => array(
'pretty_version' => '4.12.0',
'version' => '4.12.0.0',
'reference' => 'e9e99c2b24398b967841c6c4c3048622cc7e2b18',
'type' => 'library',
'install_path' => __DIR__ . '/../slim/slim',
'aliases' => array(),
'reference' => 'e9e99c2b24398b967841c6c4c3048622cc7e2b18',
'dev_requirement' => false,
),
'swiftmailer/swiftmailer' => array(
'pretty_version' => 'v6.3.0',
'version' => '6.3.0.0',
'reference' => '8a5d5072dca8f48460fce2f4131fcc495eec654c',
'type' => 'library',
'install_path' => __DIR__ . '/../swiftmailer/swiftmailer',
'aliases' => array(),
'reference' => '8a5d5072dca8f48460fce2f4131fcc495eec654c',
'dev_requirement' => false,
),
'symfony/deprecation-contracts' => array(
'pretty_version' => 'v3.4.0',
'version' => '3.4.0.0',
'reference' => '7c3aff79d10325257a001fcf92d991f24fc967cf',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
'aliases' => array(),
'reference' => '7c3aff79d10325257a001fcf92d991f24fc967cf',
'dev_requirement' => false,
),
'symfony/filesystem' => array(
'pretty_version' => 'v7.0.3',
'version' => '7.0.3.0',
'reference' => '2890e3a825bc0c0558526c04499c13f83e1b6b12',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/filesystem',
'aliases' => array(),
'reference' => '2890e3a825bc0c0558526c04499c13f83e1b6b12',
'dev_requirement' => false,
),
'symfony/finder' => array(
'pretty_version' => 'v7.0.0',
'version' => '7.0.0.0',
'reference' => '6e5688d69f7cfc4ed4a511e96007e06c2d34ce56',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/finder',
'aliases' => array(),
'reference' => '6e5688d69f7cfc4ed4a511e96007e06c2d34ce56',
'dev_requirement' => false,
),
'symfony/polyfill-ctype' => array(
'pretty_version' => 'v1.28.0',
'version' => '1.28.0.0',
'reference' => 'ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-ctype',
'aliases' => array(),
'reference' => 'ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb',
'dev_requirement' => false,
),
'symfony/polyfill-iconv' => array(
'pretty_version' => 'v1.28.0',
'version' => '1.28.0.0',
'reference' => '6de50471469b8c9afc38164452ab2b6170ee71c1',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-iconv',
'aliases' => array(),
'reference' => '6de50471469b8c9afc38164452ab2b6170ee71c1',
'dev_requirement' => false,
),
'symfony/polyfill-intl-idn' => array(
'pretty_version' => 'v1.28.0',
'version' => '1.28.0.0',
'reference' => 'ecaafce9f77234a6a449d29e49267ba10499116d',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-intl-idn',
'aliases' => array(),
'reference' => 'ecaafce9f77234a6a449d29e49267ba10499116d',
'dev_requirement' => false,
),
'symfony/polyfill-intl-normalizer' => array(
'pretty_version' => 'v1.28.0',
'version' => '1.28.0.0',
'reference' => '8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-intl-normalizer',
'aliases' => array(),
'reference' => '8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92',
'dev_requirement' => false,
),
'symfony/polyfill-mbstring' => array(
'pretty_version' => 'v1.28.0',
'version' => '1.28.0.0',
'reference' => '42292d99c55abe617799667f454222c54c60e229',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
'aliases' => array(),
'reference' => '42292d99c55abe617799667f454222c54c60e229',
'dev_requirement' => false,
),
'symfony/polyfill-php72' => array(
'pretty_version' => 'v1.28.0',
'version' => '1.28.0.0',
'reference' => '70f4aebd92afca2f865444d30a4d2151c13c3179',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php72',
'aliases' => array(),
'reference' => '70f4aebd92afca2f865444d30a4d2151c13c3179',
'dev_requirement' => false,
),
'symfony/polyfill-php80' => array(
'pretty_version' => 'v1.28.0',
'version' => '1.28.0.0',
'reference' => '6caa57379c4aec19c0a12a38b59b26487dcfe4b5',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
'aliases' => array(),
'reference' => '6caa57379c4aec19c0a12a38b59b26487dcfe4b5',
'dev_requirement' => false,
),
'symfony/process' => array(
'pretty_version' => 'v7.0.3',
'version' => '7.0.3.0',
'reference' => '937a195147e0c27b2759ade834169ed006d0bc74',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/process',
'aliases' => array(),
'reference' => '937a195147e0c27b2759ade834169ed006d0bc74',
'dev_requirement' => false,
),
'symfony/var-dumper' => array(
'pretty_version' => 'v3.4.47',
'version' => '3.4.47.0',
'reference' => '0719f6cf4633a38b2c1585140998579ce23b4b7d',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/var-dumper',
'aliases' => array(),
'reference' => '0719f6cf4633a38b2c1585140998579ce23b4b7d',
'dev_requirement' => false,
),
'symfony/yaml' => array(
'pretty_version' => 'v7.0.3',
'version' => '7.0.3.0',
'reference' => '2d4fca631c00700597e9442a0b2451ce234513d3',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/yaml',
'aliases' => array(),
'reference' => '2d4fca631c00700597e9442a0b2451ce234513d3',
'dev_requirement' => false,
),
'tinymce/tinymce' => array(
'pretty_version' => '6.8.2',
'version' => '6.8.2.0',
'reference' => 'b0073db409746748af4fc06fbee337bb99f462d9',
'type' => 'component',
'install_path' => __DIR__ . '/../tinymce/tinymce',
'aliases' => array(),
'reference' => 'b0073db409746748af4fc06fbee337bb99f462d9',
'dev_requirement' => false,
),
'zircote/swagger-php' => array(
'pretty_version' => '4.8.3',
'version' => '4.8.3.0',
'reference' => '598958d8a83cfbd44ba36388b2f9ed69e8b86ed4',
'type' => 'library',
'install_path' => __DIR__ . '/../zircote/swagger-php',
'aliases' => array(),
'reference' => '598958d8a83cfbd44ba36388b2f9ed69e8b86ed4',
'dev_requirement' => false,
),
),

View File

@ -16,35 +16,7 @@ if ($issues) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
} elseif (!headers_sent()) {
require_once __DIR__.'/../../include/functions_ui.php';
$url = str_replace('/var/www/html/', '', __DIR__);
$url = str_replace('/vendor/composer', '', $url);
echo '<link rel="stylesheet" type="text/css" href="include/styles/pandora.css">';
?>
<style>
body {
display: block;
}
</style>
<?php
$random_backgrounds = scandir('/var/www/html/'.$url. '/images/backgrounds/random_backgrounds');
unset($random_backgrounds[0], $random_backgrounds[1]);
$random_background = array_rand($random_backgrounds);
echo '<div style="width:100%;min-height: 100%;position: absolute; display:flex;align-items:center;justify-content:center;background-size:cover !important; background-position: center: !important; background: linear-gradient(rgba(255, 255, 255, .20), rgba(255, 255, 255, .20)), url('.$url. '/../images/backgrounds/random_backgrounds/'.$random_backgrounds[$random_background].');">';
echo '<center><div align="middle" class="license_databox w600px pdd_10px" style="background-color: white;">';
echo '<img style="width: 500px; margin-top: 30px;" src="images/custom_logo/logo-default-pandorafms.png"><h2> Composer detected issues in your platform:</h2>';
echo '<div class="w80p height_80px" style="text-align:left;font-size: larger">';
echo sprintf(
'PandoraFMS requires PHP 8.2 to work properly and the version %s has been detected. Please update the PHP version of the system.',
PHP_VERSION,
);
echo '</div></div></center></div>';
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
}
}
trigger_error(

View File

@ -0,0 +1,9 @@
version: 2
build:
os: ubuntu-22.04
tools:
python: "3"
mkdocs:
configuration: mkdocs.yml

View File

@ -0,0 +1,27 @@
Copyright (c) 2014, Luís Cobucci
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the {organization} nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,63 @@
{
"name": "lcobucci/jwt",
"description": "A simple library to work with JSON Web Token and JSON Web Signature",
"license": [
"BSD-3-Clause"
],
"type": "library",
"keywords": [
"JWT",
"JWS"
],
"authors": [
{
"name": "Luís Cobucci",
"email": "lcobucci@gmail.com",
"role": "Developer"
}
],
"require": {
"php": "~8.1.0 || ~8.2.0 || ~8.3.0",
"ext-openssl": "*",
"ext-sodium": "*",
"psr/clock": "^1.0"
},
"require-dev": {
"infection/infection": "^0.27.0",
"lcobucci/clock": "^3.0",
"lcobucci/coding-standard": "^11.0",
"phpbench/phpbench": "^1.2.9",
"phpstan/extension-installer": "^1.2",
"phpstan/phpstan": "^1.10.7",
"phpstan/phpstan-deprecation-rules": "^1.1.3",
"phpstan/phpstan-phpunit": "^1.3.10",
"phpstan/phpstan-strict-rules": "^1.5.0",
"phpunit/phpunit": "^10.2.6"
},
"suggest": {
"lcobucci/clock": ">= 3.0"
},
"autoload": {
"psr-4": {
"Lcobucci\\JWT\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"Lcobucci\\JWT\\Tests\\": "tests"
}
},
"config": {
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true,
"infection/extension-installer": true,
"ocramius/package-versions": true,
"phpstan/extension-installer": true
},
"platform": {
"php": "8.1.99"
},
"preferred-install": "dist",
"sort-packages": true
}
}

View File

@ -0,0 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"local>lcobucci/.github:renovate-config"
]
}

View File

@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use DateTimeImmutable;
use Lcobucci\JWT\Encoding\CannotEncodeContent;
use Lcobucci\JWT\Signer\CannotSignPayload;
use Lcobucci\JWT\Signer\Ecdsa\ConversionFailed;
use Lcobucci\JWT\Signer\InvalidKeyProvided;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Token\RegisteredClaimGiven;
/** @immutable */
interface Builder
{
/**
* Appends new items to audience
*
* @param non-empty-string ...$audiences
*/
public function permittedFor(string ...$audiences): Builder;
/**
* Configures the expiration time
*/
public function expiresAt(DateTimeImmutable $expiration): Builder;
/**
* Configures the token id
*
* @param non-empty-string $id
*/
public function identifiedBy(string $id): Builder;
/**
* Configures the time that the token was issued
*/
public function issuedAt(DateTimeImmutable $issuedAt): Builder;
/**
* Configures the issuer
*
* @param non-empty-string $issuer
*/
public function issuedBy(string $issuer): Builder;
/**
* Configures the time before which the token cannot be accepted
*/
public function canOnlyBeUsedAfter(DateTimeImmutable $notBefore): Builder;
/**
* Configures the subject
*
* @param non-empty-string $subject
*/
public function relatedTo(string $subject): Builder;
/**
* Configures a header item
*
* @param non-empty-string $name
*/
public function withHeader(string $name, mixed $value): Builder;
/**
* Configures a claim item
*
* @param non-empty-string $name
*
* @throws RegisteredClaimGiven When trying to set a registered claim.
*/
public function withClaim(string $name, mixed $value): Builder;
/**
* Returns a signed token to be used
*
* @throws CannotEncodeContent When data cannot be converted to JSON.
* @throws CannotSignPayload When payload signing fails.
* @throws InvalidKeyProvided When issue key is invalid/incompatible.
* @throws ConversionFailed When signature could not be converted.
*/
public function getToken(Signer $signer, Key $key): UnencryptedToken;
}

View File

@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
interface ClaimsFormatter
{
/**
* @param array<non-empty-string, mixed> $claims
*
* @return array<non-empty-string, mixed>
*/
public function formatClaims(array $claims): array;
}

View File

@ -0,0 +1,131 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use Closure;
use Lcobucci\JWT\Encoding\ChainedFormatter;
use Lcobucci\JWT\Encoding\JoseEncoder;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Validation\Constraint;
/**
* Configuration container for the JWT Builder and Parser
*
* Serves like a small DI container to simplify the creation and usage
* of the objects.
*/
final class Configuration
{
private Parser $parser;
private Validator $validator;
/** @var Closure(ClaimsFormatter $claimFormatter): Builder */
private Closure $builderFactory;
/** @var Constraint[] */
private array $validationConstraints = [];
private function __construct(
private readonly Signer $signer,
private readonly Key $signingKey,
private readonly Key $verificationKey,
Encoder $encoder,
Decoder $decoder,
) {
$this->parser = new Token\Parser($decoder);
$this->validator = new Validation\Validator();
$this->builderFactory = static function (ClaimsFormatter $claimFormatter) use ($encoder): Builder {
return new Token\Builder($encoder, $claimFormatter);
};
}
public static function forAsymmetricSigner(
Signer $signer,
Key $signingKey,
Key $verificationKey,
Encoder $encoder = new JoseEncoder(),
Decoder $decoder = new JoseEncoder(),
): self {
return new self(
$signer,
$signingKey,
$verificationKey,
$encoder,
$decoder,
);
}
public static function forSymmetricSigner(
Signer $signer,
Key $key,
Encoder $encoder = new JoseEncoder(),
Decoder $decoder = new JoseEncoder(),
): self {
return new self(
$signer,
$key,
$key,
$encoder,
$decoder,
);
}
/** @param callable(ClaimsFormatter): Builder $builderFactory */
public function setBuilderFactory(callable $builderFactory): void
{
$this->builderFactory = $builderFactory(...);
}
public function builder(?ClaimsFormatter $claimFormatter = null): Builder
{
return ($this->builderFactory)($claimFormatter ?? ChainedFormatter::default());
}
public function parser(): Parser
{
return $this->parser;
}
public function setParser(Parser $parser): void
{
$this->parser = $parser;
}
public function signer(): Signer
{
return $this->signer;
}
public function signingKey(): Key
{
return $this->signingKey;
}
public function verificationKey(): Key
{
return $this->verificationKey;
}
public function validator(): Validator
{
return $this->validator;
}
public function setValidator(Validator $validator): void
{
$this->validator = $validator;
}
/** @return Constraint[] */
public function validationConstraints(): array
{
return $this->validationConstraints;
}
public function setValidationConstraints(Constraint ...$validationConstraints): void
{
$this->validationConstraints = $validationConstraints;
}
}

View File

@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use Lcobucci\JWT\Encoding\CannotDecodeContent;
interface Decoder
{
/**
* Decodes from JSON, validating the errors
*
* @param non-empty-string $json
*
* @throws CannotDecodeContent When something goes wrong while decoding.
*/
public function jsonDecode(string $json): mixed;
/**
* Decodes from Base64URL
*
* @link http://tools.ietf.org/html/rfc4648#section-5
*
* @return ($data is non-empty-string ? non-empty-string : string)
*
* @throws CannotDecodeContent When something goes wrong while decoding.
*/
public function base64UrlDecode(string $data): string;
}

View File

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use Lcobucci\JWT\Encoding\CannotEncodeContent;
interface Encoder
{
/**
* Encodes to JSON, validating the errors
*
* @return non-empty-string
*
* @throws CannotEncodeContent When something goes wrong while encoding.
*/
public function jsonEncode(mixed $data): string;
/**
* Encodes to base64url
*
* @link http://tools.ietf.org/html/rfc4648#section-5
*
* @return ($data is non-empty-string ? non-empty-string : string)
*/
public function base64UrlEncode(string $data): string;
}

View File

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Encoding;
use JsonException;
use Lcobucci\JWT\Exception;
use RuntimeException;
final class CannotDecodeContent extends RuntimeException implements Exception
{
public static function jsonIssues(JsonException $previous): self
{
return new self(message: 'Error while decoding from JSON', previous: $previous);
}
public static function invalidBase64String(): self
{
return new self('Error while decoding from Base64Url, invalid base64 characters detected');
}
}

View File

@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Encoding;
use JsonException;
use Lcobucci\JWT\Exception;
use RuntimeException;
final class CannotEncodeContent extends RuntimeException implements Exception
{
public static function jsonIssues(JsonException $previous): self
{
return new self(message: 'Error while encoding to JSON', previous: $previous);
}
}

View File

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Encoding;
use Lcobucci\JWT\ClaimsFormatter;
final class ChainedFormatter implements ClaimsFormatter
{
/** @var array<ClaimsFormatter> */
private array $formatters;
public function __construct(ClaimsFormatter ...$formatters)
{
$this->formatters = $formatters;
}
public static function default(): self
{
return new self(new UnifyAudience(), new MicrosecondBasedDateConversion());
}
public static function withUnixTimestampDates(): self
{
return new self(new UnifyAudience(), new UnixTimestampDates());
}
/** @inheritdoc */
public function formatClaims(array $claims): array
{
foreach ($this->formatters as $formatter) {
$claims = $formatter->formatClaims($claims);
}
return $claims;
}
}

View File

@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Encoding;
use JsonException;
use Lcobucci\JWT\Decoder;
use Lcobucci\JWT\Encoder;
use Lcobucci\JWT\SodiumBase64Polyfill;
use function json_decode;
use function json_encode;
use const JSON_THROW_ON_ERROR;
use const JSON_UNESCAPED_SLASHES;
use const JSON_UNESCAPED_UNICODE;
/**
* A utilitarian class that encodes and decodes data according to JOSE specifications
*/
final class JoseEncoder implements Encoder, Decoder
{
public function jsonEncode(mixed $data): string
{
try {
return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
} catch (JsonException $exception) {
throw CannotEncodeContent::jsonIssues($exception);
}
}
public function jsonDecode(string $json): mixed
{
try {
return json_decode(json: $json, associative: true, flags: JSON_THROW_ON_ERROR);
} catch (JsonException $exception) {
throw CannotDecodeContent::jsonIssues($exception);
}
}
public function base64UrlEncode(string $data): string
{
return SodiumBase64Polyfill::bin2base64(
$data,
SodiumBase64Polyfill::SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING,
);
}
public function base64UrlDecode(string $data): string
{
return SodiumBase64Polyfill::base642bin(
$data,
SodiumBase64Polyfill::SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING,
);
}
}

View File

@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Encoding;
use DateTimeImmutable;
use Lcobucci\JWT\ClaimsFormatter;
use Lcobucci\JWT\Token\RegisteredClaims;
use function array_key_exists;
final class MicrosecondBasedDateConversion implements ClaimsFormatter
{
/** @inheritdoc */
public function formatClaims(array $claims): array
{
foreach (RegisteredClaims::DATE_CLAIMS as $claim) {
if (! array_key_exists($claim, $claims)) {
continue;
}
$claims[$claim] = $this->convertDate($claims[$claim]);
}
return $claims;
}
private function convertDate(DateTimeImmutable $date): int|float
{
if ($date->format('u') === '000000') {
return (int) $date->format('U');
}
return (float) $date->format('U.u');
}
}

View File

@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Encoding;
use Lcobucci\JWT\ClaimsFormatter;
use Lcobucci\JWT\Token\RegisteredClaims;
use function array_key_exists;
use function count;
use function current;
final class UnifyAudience implements ClaimsFormatter
{
/** @inheritdoc */
public function formatClaims(array $claims): array
{
if (
! array_key_exists(RegisteredClaims::AUDIENCE, $claims)
|| count($claims[RegisteredClaims::AUDIENCE]) !== 1
) {
return $claims;
}
$claims[RegisteredClaims::AUDIENCE] = current($claims[RegisteredClaims::AUDIENCE]);
return $claims;
}
}

View File

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Encoding;
use DateTimeImmutable;
use Lcobucci\JWT\ClaimsFormatter;
use Lcobucci\JWT\Token\RegisteredClaims;
use function array_key_exists;
final class UnixTimestampDates implements ClaimsFormatter
{
/** @inheritdoc */
public function formatClaims(array $claims): array
{
foreach (RegisteredClaims::DATE_CLAIMS as $claim) {
if (! array_key_exists($claim, $claims)) {
continue;
}
$claims[$claim] = $this->convertDate($claims[$claim]);
}
return $claims;
}
private function convertDate(DateTimeImmutable $date): int
{
return $date->getTimestamp();
}
}

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use Throwable;
interface Exception extends Throwable
{
}

View File

@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use Closure;
use DateTimeImmutable;
use Lcobucci\JWT\Encoding\ChainedFormatter;
use Lcobucci\JWT\Encoding\JoseEncoder;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\SignedWith;
use Lcobucci\JWT\Validation\ValidAt;
use Lcobucci\JWT\Validation\Validator;
use Psr\Clock\ClockInterface as Clock;
use function assert;
final class JwtFacade
{
private readonly Clock $clock;
public function __construct(
private readonly Parser $parser = new Token\Parser(new JoseEncoder()),
?Clock $clock = null,
) {
$this->clock = $clock ?? new class implements Clock {
public function now(): DateTimeImmutable
{
return new DateTimeImmutable();
}
};
}
/** @param Closure(Builder, DateTimeImmutable):Builder $customiseBuilder */
public function issue(
Signer $signer,
Key $signingKey,
Closure $customiseBuilder,
): UnencryptedToken {
$builder = new Token\Builder(new JoseEncoder(), ChainedFormatter::withUnixTimestampDates());
$now = $this->clock->now();
$builder = $builder
->issuedAt($now)
->canOnlyBeUsedAfter($now)
->expiresAt($now->modify('+5 minutes'));
return $customiseBuilder($builder, $now)->getToken($signer, $signingKey);
}
/** @param non-empty-string $jwt */
public function parse(
string $jwt,
SignedWith $signedWith,
ValidAt $validAt,
Constraint ...$constraints,
): UnencryptedToken {
$token = $this->parser->parse($jwt);
assert($token instanceof UnencryptedToken);
(new Validator())->assert(
$token,
$signedWith,
$validAt,
...$constraints,
);
return $token;
}
}

View File

@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use Lcobucci\JWT\Encoding\CannotDecodeContent;
use Lcobucci\JWT\Token\InvalidTokenStructure;
use Lcobucci\JWT\Token\UnsupportedHeaderFound;
interface Parser
{
/**
* Parses the JWT and returns a token
*
* @param non-empty-string $jwt
*
* @throws CannotDecodeContent When something goes wrong while decoding.
* @throws InvalidTokenStructure When token string structure is invalid.
* @throws UnsupportedHeaderFound When parsed token has an unsupported header.
*/
public function parse(string $jwt): Token;
}

View File

@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use Lcobucci\JWT\Signer\CannotSignPayload;
use Lcobucci\JWT\Signer\Ecdsa\ConversionFailed;
use Lcobucci\JWT\Signer\InvalidKeyProvided;
use Lcobucci\JWT\Signer\Key;
interface Signer
{
/**
* Returns the algorithm id
*
* @return non-empty-string
*/
public function algorithmId(): string;
/**
* Creates a hash for the given payload
*
* @param non-empty-string $payload
*
* @return non-empty-string
*
* @throws CannotSignPayload When payload signing fails.
* @throws InvalidKeyProvided When issue key is invalid/incompatible.
* @throws ConversionFailed When signature could not be converted.
*/
public function sign(string $payload, Key $key): string;
/**
* Returns if the expected hash matches with the data and key
*
* @param non-empty-string $expected
* @param non-empty-string $payload
*
* @throws InvalidKeyProvided When issue key is invalid/incompatible.
* @throws ConversionFailed When signature could not be converted.
*/
public function verify(string $expected, string $payload, Key $key): bool;
}

View File

@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer;
use function hash_equals;
use function sodium_crypto_generichash;
use function strlen;
final class Blake2b implements Signer
{
private const MINIMUM_KEY_LENGTH_IN_BITS = 256;
public function algorithmId(): string
{
return 'BLAKE2B';
}
public function sign(string $payload, Key $key): string
{
$actualKeyLength = 8 * strlen($key->contents());
if ($actualKeyLength < self::MINIMUM_KEY_LENGTH_IN_BITS) {
throw InvalidKeyProvided::tooShort(self::MINIMUM_KEY_LENGTH_IN_BITS, $actualKeyLength);
}
return sodium_crypto_generichash($payload, $key->contents());
}
public function verify(string $expected, string $payload, Key $key): bool
{
return hash_equals($expected, $this->sign($payload, $key));
}
}

View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer;
use InvalidArgumentException;
use Lcobucci\JWT\Exception;
final class CannotSignPayload extends InvalidArgumentException implements Exception
{
public static function errorHappened(string $error): self
{
return new self('There was an error while creating the signature:' . $error);
}
}

View File

@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Ecdsa\MultibyteStringConverter;
use Lcobucci\JWT\Signer\Ecdsa\SignatureConverter;
use const OPENSSL_KEYTYPE_EC;
abstract class Ecdsa extends OpenSSL
{
public function __construct(
private readonly SignatureConverter $converter = new MultibyteStringConverter(),
) {
}
final public function sign(string $payload, Key $key): string
{
return $this->converter->fromAsn1(
$this->createSignature($key->contents(), $key->passphrase(), $payload),
$this->pointLength(),
);
}
final public function verify(string $expected, string $payload, Key $key): bool
{
return $this->verifySignature(
$this->converter->toAsn1($expected, $this->pointLength()),
$payload,
$key->contents(),
);
}
/** {@inheritDoc} */
final protected function guardAgainstIncompatibleKey(int $type, int $lengthInBits): void
{
if ($type !== OPENSSL_KEYTYPE_EC) {
throw InvalidKeyProvided::incompatibleKeyType(
self::KEY_TYPE_MAP[OPENSSL_KEYTYPE_EC],
self::KEY_TYPE_MAP[$type],
);
}
$expectedKeyLength = $this->expectedKeyLength();
if ($lengthInBits !== $expectedKeyLength) {
throw InvalidKeyProvided::incompatibleKeyLength($expectedKeyLength, $lengthInBits);
}
}
/**
* @internal
*
* @return positive-int
*/
abstract public function expectedKeyLength(): int;
/**
* Returns the length of each point in the signature, so that we can calculate and verify R and S points properly
*
* @internal
*
* @return positive-int
*/
abstract public function pointLength(): int;
}

View File

@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Ecdsa;
use InvalidArgumentException;
use Lcobucci\JWT\Exception;
final class ConversionFailed extends InvalidArgumentException implements Exception
{
public static function invalidLength(): self
{
return new self('Invalid signature length.');
}
public static function incorrectStartSequence(): self
{
return new self('Invalid data. Should start with a sequence.');
}
public static function integerExpected(): self
{
return new self('Invalid data. Should contain an integer.');
}
}

View File

@ -0,0 +1,148 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2018 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*
* @link https://github.com/web-token/jwt-framework/blob/v1.2/src/Component/Core/Util/ECSignature.php
*/
namespace Lcobucci\JWT\Signer\Ecdsa;
use function assert;
use function bin2hex;
use function dechex;
use function hex2bin;
use function hexdec;
use function is_string;
use function str_pad;
use function strlen;
use function substr;
use const STR_PAD_LEFT;
/**
* ECDSA signature converter using ext-mbstring
*
* @internal
*/
final class MultibyteStringConverter implements SignatureConverter
{
private const ASN1_SEQUENCE = '30';
private const ASN1_INTEGER = '02';
private const ASN1_MAX_SINGLE_BYTE = 128;
private const ASN1_LENGTH_2BYTES = '81';
private const ASN1_BIG_INTEGER_LIMIT = '7f';
private const ASN1_NEGATIVE_INTEGER = '00';
private const BYTE_SIZE = 2;
public function toAsn1(string $points, int $length): string
{
$points = bin2hex($points);
if (self::octetLength($points) !== $length) {
throw ConversionFailed::invalidLength();
}
$pointR = self::preparePositiveInteger(substr($points, 0, $length));
$pointS = self::preparePositiveInteger(substr($points, $length, null));
$lengthR = self::octetLength($pointR);
$lengthS = self::octetLength($pointS);
$totalLength = $lengthR + $lengthS + self::BYTE_SIZE + self::BYTE_SIZE;
$lengthPrefix = $totalLength > self::ASN1_MAX_SINGLE_BYTE ? self::ASN1_LENGTH_2BYTES : '';
$asn1 = hex2bin(
self::ASN1_SEQUENCE
. $lengthPrefix . dechex($totalLength)
. self::ASN1_INTEGER . dechex($lengthR) . $pointR
. self::ASN1_INTEGER . dechex($lengthS) . $pointS,
);
assert(is_string($asn1));
assert($asn1 !== '');
return $asn1;
}
private static function octetLength(string $data): int
{
return (int) (strlen($data) / self::BYTE_SIZE);
}
private static function preparePositiveInteger(string $data): string
{
if (substr($data, 0, self::BYTE_SIZE) > self::ASN1_BIG_INTEGER_LIMIT) {
return self::ASN1_NEGATIVE_INTEGER . $data;
}
while (
substr($data, 0, self::BYTE_SIZE) === self::ASN1_NEGATIVE_INTEGER
&& substr($data, 2, self::BYTE_SIZE) <= self::ASN1_BIG_INTEGER_LIMIT
) {
$data = substr($data, 2, null);
}
return $data;
}
public function fromAsn1(string $signature, int $length): string
{
$message = bin2hex($signature);
$position = 0;
if (self::readAsn1Content($message, $position, self::BYTE_SIZE) !== self::ASN1_SEQUENCE) {
throw ConversionFailed::incorrectStartSequence();
}
// @phpstan-ignore-next-line
if (self::readAsn1Content($message, $position, self::BYTE_SIZE) === self::ASN1_LENGTH_2BYTES) {
$position += self::BYTE_SIZE;
}
$pointR = self::retrievePositiveInteger(self::readAsn1Integer($message, $position));
$pointS = self::retrievePositiveInteger(self::readAsn1Integer($message, $position));
$points = hex2bin(str_pad($pointR, $length, '0', STR_PAD_LEFT) . str_pad($pointS, $length, '0', STR_PAD_LEFT));
assert(is_string($points));
assert($points !== '');
return $points;
}
private static function readAsn1Content(string $message, int &$position, int $length): string
{
$content = substr($message, $position, $length);
$position += $length;
return $content;
}
private static function readAsn1Integer(string $message, int &$position): string
{
if (self::readAsn1Content($message, $position, self::BYTE_SIZE) !== self::ASN1_INTEGER) {
throw ConversionFailed::integerExpected();
}
$length = (int) hexdec(self::readAsn1Content($message, $position, self::BYTE_SIZE));
return self::readAsn1Content($message, $position, $length * self::BYTE_SIZE);
}
private static function retrievePositiveInteger(string $data): string
{
while (
substr($data, 0, self::BYTE_SIZE) === self::ASN1_NEGATIVE_INTEGER
&& substr($data, 2, self::BYTE_SIZE) > self::ASN1_BIG_INTEGER_LIMIT
) {
$data = substr($data, 2, null);
}
return $data;
}
}

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Ecdsa;
use Lcobucci\JWT\Signer\Ecdsa;
use const OPENSSL_ALGO_SHA256;
final class Sha256 extends Ecdsa
{
public function algorithmId(): string
{
return 'ES256';
}
public function algorithm(): int
{
return OPENSSL_ALGO_SHA256;
}
public function pointLength(): int
{
return 64;
}
public function expectedKeyLength(): int
{
return 256;
}
}

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Ecdsa;
use Lcobucci\JWT\Signer\Ecdsa;
use const OPENSSL_ALGO_SHA384;
final class Sha384 extends Ecdsa
{
public function algorithmId(): string
{
return 'ES384';
}
public function algorithm(): int
{
return OPENSSL_ALGO_SHA384;
}
public function pointLength(): int
{
return 96;
}
public function expectedKeyLength(): int
{
return 384;
}
}

View File

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Ecdsa;
use Lcobucci\JWT\Signer\Ecdsa;
use const OPENSSL_ALGO_SHA512;
final class Sha512 extends Ecdsa
{
public function algorithmId(): string
{
return 'ES512';
}
public function algorithm(): int
{
return OPENSSL_ALGO_SHA512;
}
public function pointLength(): int
{
return 132;
}
public function expectedKeyLength(): int
{
// ES512 means ECDSA using P-521 and SHA-512.
// The key size is indeed 521 bits.
return 521;
}
}

View File

@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Ecdsa;
/**
* Manipulates the result of a ECDSA signature (points R and S) according to the
* JWA specs.
*
* OpenSSL creates a signature using the ASN.1 format and, according the JWA specs,
* the signature for JWTs must be the concatenated values of points R and S (in
* big-endian octet order).
*
* @internal
*
* @see https://tools.ietf.org/html/rfc7518#page-9
* @see https://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One
*/
interface SignatureConverter
{
/**
* Converts the signature generated by OpenSSL into what JWA defines
*
* @return non-empty-string
*
* @throws ConversionFailed When there was an issue during the format conversion.
*/
public function fromAsn1(string $signature, int $length): string;
/**
* Converts the JWA signature into something OpenSSL understands
*
* @param non-empty-string $points
*
* @return non-empty-string
*
* @throws ConversionFailed When there was an issue during the format conversion.
*/
public function toAsn1(string $points, int $length): string;
}

View File

@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer;
use SodiumException;
use function sodium_crypto_sign_detached;
use function sodium_crypto_sign_verify_detached;
final class Eddsa implements Signer
{
public function algorithmId(): string
{
return 'EdDSA';
}
public function sign(string $payload, Key $key): string
{
try {
return sodium_crypto_sign_detached($payload, $key->contents());
} catch (SodiumException $sodiumException) {
throw new InvalidKeyProvided($sodiumException->getMessage(), 0, $sodiumException);
}
}
public function verify(string $expected, string $payload, Key $key): bool
{
try {
return sodium_crypto_sign_verify_detached($expected, $payload, $key->contents());
} catch (SodiumException $sodiumException) {
throw new InvalidKeyProvided($sodiumException->getMessage(), 0, $sodiumException);
}
}
}

View File

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer;
use function hash_equals;
use function hash_hmac;
use function strlen;
abstract class Hmac implements Signer
{
final public function sign(string $payload, Key $key): string
{
$actualKeyLength = 8 * strlen($key->contents());
$expectedKeyLength = $this->minimumBitsLengthForKey();
if ($actualKeyLength < $expectedKeyLength) {
throw InvalidKeyProvided::tooShort($expectedKeyLength, $actualKeyLength);
}
return hash_hmac($this->algorithm(), $payload, $key->contents(), true);
}
final public function verify(string $expected, string $payload, Key $key): bool
{
return hash_equals($expected, $this->sign($payload, $key));
}
/**
* @internal
*
* @return non-empty-string
*/
abstract public function algorithm(): string;
/**
* @internal
*
* @return positive-int
*/
abstract public function minimumBitsLengthForKey(): int;
}

View File

@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Hmac;
use Lcobucci\JWT\Signer\Hmac;
final class Sha256 extends Hmac
{
public function algorithmId(): string
{
return 'HS256';
}
public function algorithm(): string
{
return 'sha256';
}
public function minimumBitsLengthForKey(): int
{
return 256;
}
}

View File

@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Hmac;
use Lcobucci\JWT\Signer\Hmac;
final class Sha384 extends Hmac
{
public function algorithmId(): string
{
return 'HS384';
}
public function algorithm(): string
{
return 'sha384';
}
public function minimumBitsLengthForKey(): int
{
return 384;
}
}

View File

@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Hmac;
use Lcobucci\JWT\Signer\Hmac;
final class Sha512 extends Hmac
{
public function algorithmId(): string
{
return 'HS512';
}
public function algorithm(): string
{
return 'sha512';
}
public function minimumBitsLengthForKey(): int
{
return 512;
}
}

View File

@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer;
use InvalidArgumentException;
use Lcobucci\JWT\Exception;
final class InvalidKeyProvided extends InvalidArgumentException implements Exception
{
public static function cannotBeParsed(string $details): self
{
return new self('It was not possible to parse your key, reason:' . $details);
}
/**
* @param non-empty-string $expectedType
* @param non-empty-string $actualType
*/
public static function incompatibleKeyType(string $expectedType, string $actualType): self
{
return new self(
'The type of the provided key is not "' . $expectedType
. '", "' . $actualType . '" provided',
);
}
/** @param positive-int $expectedLength */
public static function incompatibleKeyLength(int $expectedLength, int $actualLength): self
{
return new self(
'The length of the provided key is different than ' . $expectedLength . ' bits, '
. $actualLength . ' bits provided',
);
}
public static function cannotBeEmpty(): self
{
return new self('Key cannot be empty');
}
public static function tooShort(int $expectedLength, int $actualLength): self
{
return new self('Key provided is shorter than ' . $expectedLength . ' bits,'
. ' only ' . $actualLength . ' bits provided');
}
}

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer;
interface Key
{
/** @return non-empty-string */
public function contents(): string;
public function passphrase(): string;
}

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Key;
use InvalidArgumentException;
use Lcobucci\JWT\Exception;
use Throwable;
final class FileCouldNotBeRead extends InvalidArgumentException implements Exception
{
/** @param non-empty-string $path */
public static function onPath(string $path, ?Throwable $cause = null): self
{
return new self(
message: 'The path "' . $path . '" does not contain a valid key file',
previous: $cause,
);
}
}

View File

@ -0,0 +1,82 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Signer\InvalidKeyProvided;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\SodiumBase64Polyfill;
use SplFileObject;
use Throwable;
use function assert;
use function is_string;
final class InMemory implements Key
{
/** @param non-empty-string $contents */
private function __construct(public readonly string $contents, public readonly string $passphrase)
{
}
/** @param non-empty-string $contents */
public static function plainText(string $contents, string $passphrase = ''): self
{
self::guardAgainstEmptyKey($contents);
return new self($contents, $passphrase);
}
/** @param non-empty-string $contents */
public static function base64Encoded(string $contents, string $passphrase = ''): self
{
$decoded = SodiumBase64Polyfill::base642bin(
$contents,
SodiumBase64Polyfill::SODIUM_BASE64_VARIANT_ORIGINAL,
);
self::guardAgainstEmptyKey($decoded);
return new self($decoded, $passphrase);
}
/**
* @param non-empty-string $path
*
* @throws FileCouldNotBeRead
*/
public static function file(string $path, string $passphrase = ''): self
{
try {
$file = new SplFileObject($path);
} catch (Throwable $exception) {
throw FileCouldNotBeRead::onPath($path, $exception);
}
$fileSize = $file->getSize();
$contents = $fileSize > 0 ? $file->fread($file->getSize()) : '';
assert(is_string($contents));
self::guardAgainstEmptyKey($contents);
return new self($contents, $passphrase);
}
/** @phpstan-assert non-empty-string $contents */
private static function guardAgainstEmptyKey(string $contents): void
{
if ($contents === '') {
throw InvalidKeyProvided::cannotBeEmpty();
}
}
public function contents(): string
{
return $this->contents;
}
public function passphrase(): string
{
return $this->passphrase;
}
}

View File

@ -0,0 +1,126 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer;
use OpenSSLAsymmetricKey;
use function array_key_exists;
use function assert;
use function is_array;
use function is_bool;
use function is_int;
use function openssl_error_string;
use function openssl_pkey_get_details;
use function openssl_pkey_get_private;
use function openssl_pkey_get_public;
use function openssl_sign;
use function openssl_verify;
use const OPENSSL_KEYTYPE_DH;
use const OPENSSL_KEYTYPE_DSA;
use const OPENSSL_KEYTYPE_EC;
use const OPENSSL_KEYTYPE_RSA;
use const PHP_EOL;
abstract class OpenSSL implements Signer
{
protected const KEY_TYPE_MAP = [
OPENSSL_KEYTYPE_RSA => 'RSA',
OPENSSL_KEYTYPE_DSA => 'DSA',
OPENSSL_KEYTYPE_DH => 'DH',
OPENSSL_KEYTYPE_EC => 'EC',
];
/**
* @return non-empty-string
*
* @throws CannotSignPayload
* @throws InvalidKeyProvided
*/
final protected function createSignature(
string $pem,
string $passphrase,
string $payload,
): string {
$key = $this->getPrivateKey($pem, $passphrase);
$signature = '';
if (! openssl_sign($payload, $signature, $key, $this->algorithm())) {
throw CannotSignPayload::errorHappened($this->fullOpenSSLErrorString());
}
return $signature;
}
/** @throws CannotSignPayload */
private function getPrivateKey(string $pem, string $passphrase): OpenSSLAsymmetricKey
{
return $this->validateKey(openssl_pkey_get_private($pem, $passphrase));
}
/** @throws InvalidKeyProvided */
final protected function verifySignature(
string $expected,
string $payload,
string $pem,
): bool {
$key = $this->getPublicKey($pem);
$result = openssl_verify($payload, $expected, $key, $this->algorithm());
return $result === 1;
}
/** @throws InvalidKeyProvided */
private function getPublicKey(string $pem): OpenSSLAsymmetricKey
{
return $this->validateKey(openssl_pkey_get_public($pem));
}
/**
* Raises an exception when the key type is not the expected type
*
* @throws InvalidKeyProvided
*/
private function validateKey(OpenSSLAsymmetricKey|bool $key): OpenSSLAsymmetricKey
{
if (is_bool($key)) {
throw InvalidKeyProvided::cannotBeParsed($this->fullOpenSSLErrorString());
}
$details = openssl_pkey_get_details($key);
assert(is_array($details));
assert(array_key_exists('bits', $details));
assert(is_int($details['bits']));
assert(array_key_exists('type', $details));
assert(is_int($details['type']));
$this->guardAgainstIncompatibleKey($details['type'], $details['bits']);
return $key;
}
private function fullOpenSSLErrorString(): string
{
$error = '';
while ($msg = openssl_error_string()) {
$error .= PHP_EOL . '* ' . $msg;
}
return $error;
}
/** @throws InvalidKeyProvided */
abstract protected function guardAgainstIncompatibleKey(int $type, int $lengthInBits): void;
/**
* Returns which algorithm to be used to create/verify the signature (using OpenSSL constants)
*
* @internal
*/
abstract public function algorithm(): int;
}

View File

@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer;
use const OPENSSL_KEYTYPE_RSA;
abstract class Rsa extends OpenSSL
{
private const MINIMUM_KEY_LENGTH = 2048;
final public function sign(string $payload, Key $key): string
{
return $this->createSignature($key->contents(), $key->passphrase(), $payload);
}
final public function verify(string $expected, string $payload, Key $key): bool
{
return $this->verifySignature($expected, $payload, $key->contents());
}
final protected function guardAgainstIncompatibleKey(int $type, int $lengthInBits): void
{
if ($type !== OPENSSL_KEYTYPE_RSA) {
throw InvalidKeyProvided::incompatibleKeyType(
self::KEY_TYPE_MAP[OPENSSL_KEYTYPE_RSA],
self::KEY_TYPE_MAP[$type],
);
}
if ($lengthInBits < self::MINIMUM_KEY_LENGTH) {
throw InvalidKeyProvided::tooShort(self::MINIMUM_KEY_LENGTH, $lengthInBits);
}
}
}

View File

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Rsa;
use Lcobucci\JWT\Signer\Rsa;
use const OPENSSL_ALGO_SHA256;
final class Sha256 extends Rsa
{
public function algorithmId(): string
{
return 'RS256';
}
public function algorithm(): int
{
return OPENSSL_ALGO_SHA256;
}
}

View File

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Rsa;
use Lcobucci\JWT\Signer\Rsa;
use const OPENSSL_ALGO_SHA384;
final class Sha384 extends Rsa
{
public function algorithmId(): string
{
return 'RS384';
}
public function algorithm(): int
{
return OPENSSL_ALGO_SHA384;
}
}

View File

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Rsa;
use Lcobucci\JWT\Signer\Rsa;
use const OPENSSL_ALGO_SHA512;
final class Sha512 extends Rsa
{
public function algorithmId(): string
{
return 'RS512';
}
public function algorithm(): int
{
return OPENSSL_ALGO_SHA512;
}
}

View File

@ -0,0 +1,98 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use Lcobucci\JWT\Encoding\CannotDecodeContent;
use SodiumException;
use function base64_decode;
use function base64_encode;
use function function_exists;
use function is_string;
use function rtrim;
use function sodium_base642bin;
use function sodium_bin2base64;
use function strtr;
/** @internal */
final class SodiumBase64Polyfill
{
public const SODIUM_BASE64_VARIANT_ORIGINAL = 1;
public const SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING = 3;
public const SODIUM_BASE64_VARIANT_URLSAFE = 5;
public const SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING = 7;
/** @return ($decoded is non-empty-string ? non-empty-string : string) */
public static function bin2base64(string $decoded, int $variant): string
{
if (! function_exists('sodium_bin2base64')) {
return self::bin2base64Fallback($decoded, $variant); // @codeCoverageIgnore
}
return sodium_bin2base64($decoded, $variant);
}
/** @return ($decoded is non-empty-string ? non-empty-string : string) */
public static function bin2base64Fallback(string $decoded, int $variant): string
{
$encoded = base64_encode($decoded);
if (
$variant === self::SODIUM_BASE64_VARIANT_URLSAFE
|| $variant === self::SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING
) {
$encoded = strtr($encoded, '+/', '-_');
}
if (
$variant === self::SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING
|| $variant === self::SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING
) {
$encoded = rtrim($encoded, '=');
}
return $encoded;
}
/**
* @return ($encoded is non-empty-string ? non-empty-string : string)
*
* @throws CannotDecodeContent
*/
public static function base642bin(string $encoded, int $variant): string
{
if (! function_exists('sodium_base642bin')) {
return self::base642binFallback($encoded, $variant); // @codeCoverageIgnore
}
try {
return sodium_base642bin($encoded, $variant, '');
} catch (SodiumException) {
throw CannotDecodeContent::invalidBase64String();
}
}
/**
* @return ($encoded is non-empty-string ? non-empty-string : string)
*
* @throws CannotDecodeContent
*/
public static function base642binFallback(string $encoded, int $variant): string
{
if (
$variant === self::SODIUM_BASE64_VARIANT_URLSAFE
|| $variant === self::SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING
) {
$encoded = strtr($encoded, '-_', '+/');
}
$decoded = base64_decode($encoded, true);
if (! is_string($decoded)) {
throw CannotDecodeContent::invalidBase64String();
}
return $decoded;
}
}

View File

@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use DateTimeInterface;
use Lcobucci\JWT\Token\DataSet;
interface Token
{
/**
* Returns the token headers
*/
public function headers(): DataSet;
/**
* Returns if the token is allowed to be used by the audience
*
* @param non-empty-string $audience
*/
public function isPermittedFor(string $audience): bool;
/**
* Returns if the token has the given id
*
* @param non-empty-string $id
*/
public function isIdentifiedBy(string $id): bool;
/**
* Returns if the token has the given subject
*
* @param non-empty-string $subject
*/
public function isRelatedTo(string $subject): bool;
/**
* Returns if the token was issued by any of given issuers
*
* @param non-empty-string ...$issuers
*/
public function hasBeenIssuedBy(string ...$issuers): bool;
/**
* Returns if the token was issued before of given time
*/
public function hasBeenIssuedBefore(DateTimeInterface $now): bool;
/**
* Returns if the token minimum time is before than given time
*/
public function isMinimumTimeBefore(DateTimeInterface $now): bool;
/**
* Returns if the token is expired
*/
public function isExpired(DateTimeInterface $now): bool;
/**
* Returns an encoded representation of the token
*
* @return non-empty-string
*/
public function toString(): string;
}

View File

@ -0,0 +1,125 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Token;
use DateTimeImmutable;
use Lcobucci\JWT\Builder as BuilderInterface;
use Lcobucci\JWT\ClaimsFormatter;
use Lcobucci\JWT\Encoder;
use Lcobucci\JWT\Encoding\CannotEncodeContent;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\UnencryptedToken;
use function array_diff;
use function array_merge;
use function in_array;
/** @immutable */
final class Builder implements BuilderInterface
{
/** @var array<non-empty-string, mixed> */
private array $headers = ['typ' => 'JWT', 'alg' => null];
/** @var array<non-empty-string, mixed> */
private array $claims = [];
public function __construct(private readonly Encoder $encoder, private readonly ClaimsFormatter $claimFormatter)
{
}
public function permittedFor(string ...$audiences): BuilderInterface
{
$configured = $this->claims[RegisteredClaims::AUDIENCE] ?? [];
$toAppend = array_diff($audiences, $configured);
return $this->setClaim(RegisteredClaims::AUDIENCE, array_merge($configured, $toAppend));
}
public function expiresAt(DateTimeImmutable $expiration): BuilderInterface
{
return $this->setClaim(RegisteredClaims::EXPIRATION_TIME, $expiration);
}
public function identifiedBy(string $id): BuilderInterface
{
return $this->setClaim(RegisteredClaims::ID, $id);
}
public function issuedAt(DateTimeImmutable $issuedAt): BuilderInterface
{
return $this->setClaim(RegisteredClaims::ISSUED_AT, $issuedAt);
}
public function issuedBy(string $issuer): BuilderInterface
{
return $this->setClaim(RegisteredClaims::ISSUER, $issuer);
}
public function canOnlyBeUsedAfter(DateTimeImmutable $notBefore): BuilderInterface
{
return $this->setClaim(RegisteredClaims::NOT_BEFORE, $notBefore);
}
public function relatedTo(string $subject): BuilderInterface
{
return $this->setClaim(RegisteredClaims::SUBJECT, $subject);
}
public function withHeader(string $name, mixed $value): BuilderInterface
{
$new = clone $this;
$new->headers[$name] = $value;
return $new;
}
public function withClaim(string $name, mixed $value): BuilderInterface
{
if (in_array($name, RegisteredClaims::ALL, true)) {
throw RegisteredClaimGiven::forClaim($name);
}
return $this->setClaim($name, $value);
}
/** @param non-empty-string $name */
private function setClaim(string $name, mixed $value): BuilderInterface
{
$new = clone $this;
$new->claims[$name] = $value;
return $new;
}
/**
* @param array<non-empty-string, mixed> $items
*
* @throws CannotEncodeContent When data cannot be converted to JSON.
*/
private function encode(array $items): string
{
return $this->encoder->base64UrlEncode(
$this->encoder->jsonEncode($items),
);
}
public function getToken(Signer $signer, Key $key): UnencryptedToken
{
$headers = $this->headers;
$headers['alg'] = $signer->algorithmId();
$encodedHeaders = $this->encode($headers);
$encodedClaims = $this->encode($this->claimFormatter->formatClaims($this->claims));
$signature = $signer->sign($encodedHeaders . '.' . $encodedClaims, $key);
$encodedSignature = $this->encoder->base64UrlEncode($signature);
return new Plain(
new DataSet($headers, $encodedHeaders),
new DataSet($this->claims, $encodedClaims),
new Signature($signature, $encodedSignature),
);
}
}

View File

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Token;
use function array_key_exists;
final class DataSet
{
/** @param array<non-empty-string, mixed> $data */
public function __construct(private readonly array $data, private readonly string $encoded)
{
}
/** @param non-empty-string $name */
public function get(string $name, mixed $default = null): mixed
{
return $this->data[$name] ?? $default;
}
/** @param non-empty-string $name */
public function has(string $name): bool
{
return array_key_exists($name, $this->data);
}
/** @return array<non-empty-string, mixed> */
public function all(): array
{
return $this->data;
}
public function toString(): string
{
return $this->encoded;
}
}

View File

@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Token;
use InvalidArgumentException;
use Lcobucci\JWT\Exception;
final class InvalidTokenStructure extends InvalidArgumentException implements Exception
{
public static function missingOrNotEnoughSeparators(): self
{
return new self('The JWT string must have two dots');
}
public static function missingHeaderPart(): self
{
return new self('The JWT string is missing the Header part');
}
public static function missingClaimsPart(): self
{
return new self('The JWT string is missing the Claim part');
}
public static function missingSignaturePart(): self
{
return new self('The JWT string is missing the Signature part');
}
/** @param non-empty-string $part */
public static function arrayExpected(string $part): self
{
return new self($part . ' must be an array with non-empty-string keys');
}
public static function dateIsNotParseable(string $value): self
{
return new self('Value is not in the allowed date format: ' . $value);
}
}

View File

@ -0,0 +1,180 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Token;
use DateTimeImmutable;
use Lcobucci\JWT\Decoder;
use Lcobucci\JWT\Parser as ParserInterface;
use Lcobucci\JWT\Token as TokenInterface;
use function array_key_exists;
use function count;
use function explode;
use function is_array;
use function is_numeric;
use function number_format;
final class Parser implements ParserInterface
{
private const MICROSECOND_PRECISION = 6;
public function __construct(private readonly Decoder $decoder)
{
}
public function parse(string $jwt): TokenInterface
{
[$encodedHeaders, $encodedClaims, $encodedSignature] = $this->splitJwt($jwt);
if ($encodedHeaders === '') {
throw InvalidTokenStructure::missingHeaderPart();
}
if ($encodedClaims === '') {
throw InvalidTokenStructure::missingClaimsPart();
}
if ($encodedSignature === '') {
throw InvalidTokenStructure::missingSignaturePart();
}
$header = $this->parseHeader($encodedHeaders);
return new Plain(
new DataSet($header, $encodedHeaders),
new DataSet($this->parseClaims($encodedClaims), $encodedClaims),
$this->parseSignature($encodedSignature),
);
}
/**
* Splits the JWT string into an array
*
* @param non-empty-string $jwt
*
* @return string[]
*
* @throws InvalidTokenStructure When JWT doesn't have all parts.
*/
private function splitJwt(string $jwt): array
{
$data = explode('.', $jwt);
if (count($data) !== 3) {
throw InvalidTokenStructure::missingOrNotEnoughSeparators();
}
return $data;
}
/**
* Parses the header from a string
*
* @param non-empty-string $data
*
* @return array<non-empty-string, mixed>
*
* @throws UnsupportedHeaderFound When an invalid header is informed.
* @throws InvalidTokenStructure When parsed content isn't an array.
*/
private function parseHeader(string $data): array
{
$header = $this->decoder->jsonDecode($this->decoder->base64UrlDecode($data));
if (! is_array($header)) {
throw InvalidTokenStructure::arrayExpected('headers');
}
$this->guardAgainstEmptyStringKeys($header, 'headers');
if (array_key_exists('enc', $header)) {
throw UnsupportedHeaderFound::encryption();
}
if (! array_key_exists('typ', $header)) {
$header['typ'] = 'JWT';
}
return $header;
}
/**
* Parses the claim set from a string
*
* @param non-empty-string $data
*
* @return array<non-empty-string, mixed>
*
* @throws InvalidTokenStructure When parsed content isn't an array or contains non-parseable dates.
*/
private function parseClaims(string $data): array
{
$claims = $this->decoder->jsonDecode($this->decoder->base64UrlDecode($data));
if (! is_array($claims)) {
throw InvalidTokenStructure::arrayExpected('claims');
}
$this->guardAgainstEmptyStringKeys($claims, 'claims');
if (array_key_exists(RegisteredClaims::AUDIENCE, $claims)) {
$claims[RegisteredClaims::AUDIENCE] = (array) $claims[RegisteredClaims::AUDIENCE];
}
foreach (RegisteredClaims::DATE_CLAIMS as $claim) {
if (! array_key_exists($claim, $claims)) {
continue;
}
$claims[$claim] = $this->convertDate($claims[$claim]);
}
return $claims;
}
/**
* @param array<string, mixed> $array
* @param non-empty-string $part
*
* @phpstan-assert array<non-empty-string, mixed> $array
*/
private function guardAgainstEmptyStringKeys(array $array, string $part): void
{
foreach ($array as $key => $value) {
if ($key === '') {
throw InvalidTokenStructure::arrayExpected($part);
}
}
}
/** @throws InvalidTokenStructure */
private function convertDate(int|float|string $timestamp): DateTimeImmutable
{
if (! is_numeric($timestamp)) {
throw InvalidTokenStructure::dateIsNotParseable($timestamp);
}
$normalizedTimestamp = number_format((float) $timestamp, self::MICROSECOND_PRECISION, '.', '');
$date = DateTimeImmutable::createFromFormat('U.u', $normalizedTimestamp);
if ($date === false) {
throw InvalidTokenStructure::dateIsNotParseable($normalizedTimestamp);
}
return $date;
}
/**
* Returns the signature from given data
*
* @param non-empty-string $data
*/
private function parseSignature(string $data): Signature
{
$hash = $this->decoder->base64UrlDecode($data);
return new Signature($hash, $data);
}
}

View File

@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Token;
use DateTimeInterface;
use Lcobucci\JWT\UnencryptedToken;
use function in_array;
final class Plain implements UnencryptedToken
{
public function __construct(
private readonly DataSet $headers,
private readonly DataSet $claims,
private readonly Signature $signature,
) {
}
public function headers(): DataSet
{
return $this->headers;
}
public function claims(): DataSet
{
return $this->claims;
}
public function signature(): Signature
{
return $this->signature;
}
public function payload(): string
{
return $this->headers->toString() . '.' . $this->claims->toString();
}
public function isPermittedFor(string $audience): bool
{
return in_array($audience, $this->claims->get(RegisteredClaims::AUDIENCE, []), true);
}
public function isIdentifiedBy(string $id): bool
{
return $this->claims->get(RegisteredClaims::ID) === $id;
}
public function isRelatedTo(string $subject): bool
{
return $this->claims->get(RegisteredClaims::SUBJECT) === $subject;
}
public function hasBeenIssuedBy(string ...$issuers): bool
{
return in_array($this->claims->get(RegisteredClaims::ISSUER), $issuers, true);
}
public function hasBeenIssuedBefore(DateTimeInterface $now): bool
{
return $now >= $this->claims->get(RegisteredClaims::ISSUED_AT);
}
public function isMinimumTimeBefore(DateTimeInterface $now): bool
{
return $now >= $this->claims->get(RegisteredClaims::NOT_BEFORE);
}
public function isExpired(DateTimeInterface $now): bool
{
if (! $this->claims->has(RegisteredClaims::EXPIRATION_TIME)) {
return false;
}
return $now >= $this->claims->get(RegisteredClaims::EXPIRATION_TIME);
}
public function toString(): string
{
return $this->headers->toString() . '.'
. $this->claims->toString() . '.'
. $this->signature->toString();
}
}

View File

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Token;
use InvalidArgumentException;
use Lcobucci\JWT\Exception;
use function sprintf;
final class RegisteredClaimGiven extends InvalidArgumentException implements Exception
{
private const DEFAULT_MESSAGE = 'Builder#withClaim() is meant to be used for non-registered claims, '
. 'check the documentation on how to set claim "%s"';
/** @param non-empty-string $name */
public static function forClaim(string $name): self
{
return new self(sprintf(self::DEFAULT_MESSAGE, $name));
}
}

View File

@ -0,0 +1,77 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Token;
/**
* Defines the list of claims that are registered in the IANA "JSON Web Token Claims" registry
*
* @see https://tools.ietf.org/html/rfc7519#section-4.1
*/
interface RegisteredClaims
{
public const ALL = [
self::AUDIENCE,
self::EXPIRATION_TIME,
self::ID,
self::ISSUED_AT,
self::ISSUER,
self::NOT_BEFORE,
self::SUBJECT,
];
public const DATE_CLAIMS = [
self::ISSUED_AT,
self::NOT_BEFORE,
self::EXPIRATION_TIME,
];
/**
* Identifies the recipients that the JWT is intended for
*
* @see https://tools.ietf.org/html/rfc7519#section-4.1.3
*/
public const AUDIENCE = 'aud';
/**
* Identifies the expiration time on or after which the JWT MUST NOT be accepted for processing
*
* @see https://tools.ietf.org/html/rfc7519#section-4.1.4
*/
public const EXPIRATION_TIME = 'exp';
/**
* Provides a unique identifier for the JWT
*
* @see https://tools.ietf.org/html/rfc7519#section-4.1.7
*/
public const ID = 'jti';
/**
* Identifies the time at which the JWT was issued
*
* @see https://tools.ietf.org/html/rfc7519#section-4.1.6
*/
public const ISSUED_AT = 'iat';
/**
* Identifies the principal that issued the JWT
*
* @see https://tools.ietf.org/html/rfc7519#section-4.1.1
*/
public const ISSUER = 'iss';
/**
* Identifies the time before which the JWT MUST NOT be accepted for processing
*
* https://tools.ietf.org/html/rfc7519#section-4.1.5
*/
public const NOT_BEFORE = 'nbf';
/**
* Identifies the principal that is the subject of the JWT.
*
* https://tools.ietf.org/html/rfc7519#section-4.1.2
*/
public const SUBJECT = 'sub';
}

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Token;
final class Signature
{
/**
* @param non-empty-string $hash
* @param non-empty-string $encoded
*/
public function __construct(private readonly string $hash, private readonly string $encoded)
{
}
/** @return non-empty-string */
public function hash(): string
{
return $this->hash;
}
/**
* Returns the encoded version of the signature
*
* @return non-empty-string
*/
public function toString(): string
{
return $this->encoded;
}
}

View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Token;
use InvalidArgumentException;
use Lcobucci\JWT\Exception;
final class UnsupportedHeaderFound extends InvalidArgumentException implements Exception
{
public static function encryption(): self
{
return new self('Encryption is not supported yet');
}
}

View File

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use Lcobucci\JWT\Token\DataSet;
use Lcobucci\JWT\Token\Signature;
interface UnencryptedToken extends Token
{
/**
* Returns the token claims
*/
public function claims(): DataSet;
/**
* Returns the token signature
*/
public function signature(): Signature;
/**
* Returns the token payload
*
* @return non-empty-string
*/
public function payload(): string;
}

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation;
use Lcobucci\JWT\Token;
interface Constraint
{
/** @throws ConstraintViolation */
public function assert(Token $token): void;
}

View File

@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use InvalidArgumentException;
use Lcobucci\JWT\Exception;
final class CannotValidateARegisteredClaim extends InvalidArgumentException implements Exception
{
/** @param non-empty-string $claim */
public static function create(string $claim): self
{
return new self(
'The claim "' . $claim . '" is a registered claim, another constraint must be used to validate its value',
);
}
}

View File

@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\UnencryptedToken;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\ConstraintViolation;
use function in_array;
final class HasClaimWithValue implements Constraint
{
/** @param non-empty-string $claim */
public function __construct(private readonly string $claim, private readonly mixed $expectedValue)
{
if (in_array($claim, Token\RegisteredClaims::ALL, true)) {
throw CannotValidateARegisteredClaim::create($claim);
}
}
public function assert(Token $token): void
{
if (! $token instanceof UnencryptedToken) {
throw ConstraintViolation::error('You should pass a plain token', $this);
}
$claims = $token->claims();
if (! $claims->has($this->claim)) {
throw ConstraintViolation::error('The token does not have the claim "' . $this->claim . '"', $this);
}
if ($claims->get($this->claim) !== $this->expectedValue) {
throw ConstraintViolation::error(
'The claim "' . $this->claim . '" does not have the expected value',
$this,
);
}
}
}

View File

@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\ConstraintViolation;
final class IdentifiedBy implements Constraint
{
/** @param non-empty-string $id */
public function __construct(private readonly string $id)
{
}
public function assert(Token $token): void
{
if (! $token->isIdentifiedBy($this->id)) {
throw ConstraintViolation::error(
'The token is not identified with the expected ID',
$this,
);
}
}
}

View File

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\ConstraintViolation;
final class IssuedBy implements Constraint
{
/** @var non-empty-string[] */
private readonly array $issuers;
/** @param non-empty-string ...$issuers */
public function __construct(string ...$issuers)
{
$this->issuers = $issuers;
}
public function assert(Token $token): void
{
if (! $token->hasBeenIssuedBy(...$this->issuers)) {
throw ConstraintViolation::error(
'The token was not issued by the given issuers',
$this,
);
}
}
}

View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use InvalidArgumentException;
use Lcobucci\JWT\Exception;
final class LeewayCannotBeNegative extends InvalidArgumentException implements Exception
{
public static function create(): self
{
return new self('Leeway cannot be negative');
}
}

View File

@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use DateInterval;
use DateTimeInterface;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\ConstraintViolation;
use Lcobucci\JWT\Validation\ValidAt as ValidAtInterface;
use Psr\Clock\ClockInterface as Clock;
final class LooseValidAt implements ValidAtInterface
{
private readonly DateInterval $leeway;
public function __construct(private readonly Clock $clock, ?DateInterval $leeway = null)
{
$this->leeway = $this->guardLeeway($leeway);
}
private function guardLeeway(?DateInterval $leeway): DateInterval
{
if ($leeway === null) {
return new DateInterval('PT0S');
}
if ($leeway->invert === 1) {
throw LeewayCannotBeNegative::create();
}
return $leeway;
}
public function assert(Token $token): void
{
$now = $this->clock->now();
$this->assertIssueTime($token, $now->add($this->leeway));
$this->assertMinimumTime($token, $now->add($this->leeway));
$this->assertExpiration($token, $now->sub($this->leeway));
}
/** @throws ConstraintViolation */
private function assertExpiration(Token $token, DateTimeInterface $now): void
{
if ($token->isExpired($now)) {
throw ConstraintViolation::error('The token is expired', $this);
}
}
/** @throws ConstraintViolation */
private function assertMinimumTime(Token $token, DateTimeInterface $now): void
{
if (! $token->isMinimumTimeBefore($now)) {
throw ConstraintViolation::error('The token cannot be used yet', $this);
}
}
/** @throws ConstraintViolation */
private function assertIssueTime(Token $token, DateTimeInterface $now): void
{
if (! $token->hasBeenIssuedBefore($now)) {
throw ConstraintViolation::error('The token was issued in the future', $this);
}
}
}

View File

@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\ConstraintViolation;
final class PermittedFor implements Constraint
{
/** @param non-empty-string $audience */
public function __construct(private readonly string $audience)
{
}
public function assert(Token $token): void
{
if (! $token->isPermittedFor($this->audience)) {
throw ConstraintViolation::error(
'The token is not allowed to be used by this audience',
$this,
);
}
}
}

View File

@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\ConstraintViolation;
final class RelatedTo implements Constraint
{
/** @param non-empty-string $subject */
public function __construct(private readonly string $subject)
{
}
public function assert(Token $token): void
{
if (! $token->isRelatedTo($this->subject)) {
throw ConstraintViolation::error(
'The token is not related to the expected subject',
$this,
);
}
}
}

View File

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\UnencryptedToken;
use Lcobucci\JWT\Validation\ConstraintViolation;
use Lcobucci\JWT\Validation\SignedWith as SignedWithInterface;
final class SignedWith implements SignedWithInterface
{
public function __construct(private readonly Signer $signer, private readonly Signer\Key $key)
{
}
public function assert(Token $token): void
{
if (! $token instanceof UnencryptedToken) {
throw ConstraintViolation::error('You should pass a plain token', $this);
}
if ($token->headers()->get('alg') !== $this->signer->algorithmId()) {
throw ConstraintViolation::error('Token signer mismatch', $this);
}
if (! $this->signer->verify($token->signature()->hash(), $token->payload(), $this->key)) {
throw ConstraintViolation::error('Token signature mismatch', $this);
}
}
}

View File

@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\ConstraintViolation;
use Lcobucci\JWT\Validation\SignedWith as SignedWithInterface;
use const PHP_EOL;
final class SignedWithOneInSet implements SignedWithInterface
{
/** @var array<SignedWithUntilDate> */
private readonly array $constraints;
public function __construct(SignedWithUntilDate ...$constraints)
{
$this->constraints = $constraints;
}
public function assert(Token $token): void
{
$errorMessage = 'It was not possible to verify the signature of the token, reasons:';
foreach ($this->constraints as $constraint) {
try {
$constraint->assert($token);
return;
} catch (ConstraintViolation $violation) {
$errorMessage .= PHP_EOL . '- ' . $violation->getMessage();
}
}
throw ConstraintViolation::error($errorMessage, $this);
}
}

View File

@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use DateTimeImmutable;
use DateTimeInterface;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\ConstraintViolation;
use Lcobucci\JWT\Validation\SignedWith as SignedWithInterface;
use Psr\Clock\ClockInterface;
final class SignedWithUntilDate implements SignedWithInterface
{
private readonly SignedWith $verifySignature;
private readonly ClockInterface $clock;
public function __construct(
Signer $signer,
Signer\Key $key,
private readonly DateTimeImmutable $validUntil,
?ClockInterface $clock = null,
) {
$this->verifySignature = new SignedWith($signer, $key);
$this->clock = $clock ?? new class () implements ClockInterface {
public function now(): DateTimeImmutable
{
return new DateTimeImmutable();
}
};
}
public function assert(Token $token): void
{
if ($this->validUntil < $this->clock->now()) {
throw ConstraintViolation::error(
'This constraint was only usable until '
. $this->validUntil->format(DateTimeInterface::RFC3339),
$this,
);
}
$this->verifySignature->assert($token);
}
}

View File

@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use DateInterval;
use DateTimeInterface;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\UnencryptedToken;
use Lcobucci\JWT\Validation\ConstraintViolation;
use Lcobucci\JWT\Validation\ValidAt as ValidAtInterface;
use Psr\Clock\ClockInterface as Clock;
final class StrictValidAt implements ValidAtInterface
{
private readonly DateInterval $leeway;
public function __construct(private readonly Clock $clock, ?DateInterval $leeway = null)
{
$this->leeway = $this->guardLeeway($leeway);
}
private function guardLeeway(?DateInterval $leeway): DateInterval
{
if ($leeway === null) {
return new DateInterval('PT0S');
}
if ($leeway->invert === 1) {
throw LeewayCannotBeNegative::create();
}
return $leeway;
}
public function assert(Token $token): void
{
if (! $token instanceof UnencryptedToken) {
throw ConstraintViolation::error('You should pass a plain token', $this);
}
$now = $this->clock->now();
$this->assertIssueTime($token, $now->add($this->leeway));
$this->assertMinimumTime($token, $now->add($this->leeway));
$this->assertExpiration($token, $now->sub($this->leeway));
}
/** @throws ConstraintViolation */
private function assertExpiration(UnencryptedToken $token, DateTimeInterface $now): void
{
if (! $token->claims()->has(Token\RegisteredClaims::EXPIRATION_TIME)) {
throw ConstraintViolation::error('"Expiration Time" claim missing', $this);
}
if ($token->isExpired($now)) {
throw ConstraintViolation::error('The token is expired', $this);
}
}
/** @throws ConstraintViolation */
private function assertMinimumTime(UnencryptedToken $token, DateTimeInterface $now): void
{
if (! $token->claims()->has(Token\RegisteredClaims::NOT_BEFORE)) {
throw ConstraintViolation::error('"Not Before" claim missing', $this);
}
if (! $token->isMinimumTimeBefore($now)) {
throw ConstraintViolation::error('The token cannot be used yet', $this);
}
}
/** @throws ConstraintViolation */
private function assertIssueTime(UnencryptedToken $token, DateTimeInterface $now): void
{
if (! $token->claims()->has(Token\RegisteredClaims::ISSUED_AT)) {
throw ConstraintViolation::error('"Issued At" claim missing', $this);
}
if (! $token->hasBeenIssuedBefore($now)) {
throw ConstraintViolation::error('The token was issued in the future', $this);
}
}
}

View File

@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation;
use Lcobucci\JWT\Exception;
use RuntimeException;
final class ConstraintViolation extends RuntimeException implements Exception
{
/** @param class-string<Constraint>|null $constraint */
public function __construct(
string $message = '',
public readonly ?string $constraint = null,
) {
parent::__construct($message);
}
/** @param non-empty-string $message */
public static function error(string $message, Constraint $constraint): self
{
return new self(message: $message, constraint: $constraint::class);
}
}

View File

@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation;
use Lcobucci\JWT\Exception;
use RuntimeException;
final class NoConstraintsGiven extends RuntimeException implements Exception
{
}

View File

@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation;
use Lcobucci\JWT\Exception;
use RuntimeException;
use function array_map;
use function implode;
final class RequiredConstraintsViolated extends RuntimeException implements Exception
{
/** @param ConstraintViolation[] $violations */
public function __construct(
string $message = '',
public readonly array $violations = [],
) {
parent::__construct($message);
}
public static function fromViolations(ConstraintViolation ...$violations): self
{
return new self(message: self::buildMessage($violations), violations: $violations);
}
/** @param ConstraintViolation[] $violations */
private static function buildMessage(array $violations): string
{
$violations = array_map(
static function (ConstraintViolation $violation): string {
return '- ' . $violation->getMessage();
},
$violations,
);
$message = "The token violates some mandatory constraints, details:\n";
$message .= implode("\n", $violations);
return $message;
}
/** @return ConstraintViolation[] */
public function violations(): array
{
return $this->violations;
}
}

View File

@ -0,0 +1,8 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation;
interface SignedWith extends Constraint
{
}

View File

@ -0,0 +1,8 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation;
interface ValidAt extends Constraint
{
}

View File

@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation;
use Lcobucci\JWT\Token;
final class Validator implements \Lcobucci\JWT\Validator
{
public function assert(Token $token, Constraint ...$constraints): void
{
if ($constraints === []) {
throw new NoConstraintsGiven('No constraint given.');
}
$violations = [];
foreach ($constraints as $constraint) {
$this->checkConstraint($constraint, $token, $violations);
}
if ($violations) {
throw RequiredConstraintsViolated::fromViolations(...$violations);
}
}
/** @param ConstraintViolation[] $violations */
private function checkConstraint(
Constraint $constraint,
Token $token,
array &$violations,
): void {
try {
$constraint->assert($token);
} catch (ConstraintViolation $e) {
$violations[] = $e;
}
}
public function validate(Token $token, Constraint ...$constraints): bool
{
if ($constraints === []) {
throw new NoConstraintsGiven('No constraint given.');
}
try {
foreach ($constraints as $constraint) {
$constraint->assert($token);
}
return true;
} catch (ConstraintViolation) {
return false;
}
}
}

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\NoConstraintsGiven;
use Lcobucci\JWT\Validation\RequiredConstraintsViolated;
interface Validator
{
/**
* @throws RequiredConstraintsViolated
* @throws NoConstraintsGiven
*/
public function assert(Token $token, Constraint ...$constraints): void;
/** @throws NoConstraintsGiven */
public function validate(Token $token, Constraint ...$constraints): bool;
}

View File

@ -0,0 +1,11 @@
# Changelog
All notable changes to this project will be documented in this file, in reverse chronological order by release.
## 1.0.0
First stable release after PSR-20 acceptance
## 0.1.0
First release

View File

@ -0,0 +1,19 @@
Copyright (c) 2017 PHP Framework Interoperability Group
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,61 @@
# PSR Clock
This repository holds the interface for [PSR-20][psr-url].
Note that this is not a clock of its own. It is merely an interface that
describes a clock. See the specification for more details.
## Installation
```bash
composer require psr/clock
```
## Usage
If you need a clock, you can use the interface like this:
```php
<?php
use Psr\Clock\ClockInterface;
class Foo
{
private ClockInterface $clock;
public function __construct(ClockInterface $clock)
{
$this->clock = $clock;
}
public function doSomething()
{
/** @var DateTimeImmutable $currentDateAndTime */
$currentDateAndTime = $this->clock->now();
// do something useful with that information
}
}
```
You can then pick one of the [implementations][implementation-url] of the interface to get a clock.
If you want to implement the interface, you can require this package and
implement `Psr\Clock\ClockInterface` in your code.
Don't forget to add `psr/clock-implementation` to your `composer.json`s `provides`-section like this:
```json
{
"provides": {
"psr/clock-implementation": "1.0"
}
}
```
And please read the [specification text][specification-url] for details on the interface.
[psr-url]: https://www.php-fig.org/psr/psr-20
[package-url]: https://packagist.org/packages/psr/clock
[implementation-url]: https://packagist.org/providers/psr/clock-implementation
[specification-url]: https://github.com/php-fig/fig-standards/blob/master/proposed/clock.md

View File

@ -0,0 +1,21 @@
{
"name": "psr/clock",
"description": "Common interface for reading the clock.",
"keywords": ["psr", "psr-20", "time", "clock", "now"],
"homepage": "https://github.com/php-fig/clock",
"license": "MIT",
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"require": {
"php": "^7.0 || ^8.0"
},
"autoload": {
"psr-4": {
"Psr\\Clock\\": "src/"
}
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace Psr\Clock;
use DateTimeImmutable;
interface ClockInterface
{
/**
* Returns the current time as a DateTimeImmutable Object
*/
public function now(): DateTimeImmutable;
}