Merge pull request #7568 from mailcow/feat/compose-update

[Web] Update composer libraries
This commit is contained in:
FreddleSpl0it authored and GitHub committed 2026-10-28 12:01:04 +01:00
commit aa7388810c
1296 files changed
+69434 -22307

No files matched your search

+3 -1
View File
@@ -1604,7 +1604,9 @@ function init_db_schema()
"redirect_uri" => "VARCHAR(2000)", "redirect_uri" => "VARCHAR(2000)",
"expires" => "TIMESTAMP NOT NULL", "expires" => "TIMESTAMP NOT NULL",
"scope" => "VARCHAR(4000)", "scope" => "VARCHAR(4000)",
"id_token" => "VARCHAR(1000)" "id_token" => "VARCHAR(1000)",
"code_challenge" => "VARCHAR(1000)",
"code_challenge_method" => "VARCHAR(20)"
), ),
"keys" => array( "keys" => array(
"primary" => array( "primary" => array(
+1 -1
View File
@@ -10,7 +10,7 @@
"mustangostang/spyc": "^0.6.3", "mustangostang/spyc": "^0.6.3",
"directorytree/ldaprecord": "^3.3", "directorytree/ldaprecord": "^3.3",
"twig/twig": "^3.0", "twig/twig": "^3.0",
"stevenmaguire/oauth2-keycloak": "^4.0", "stevenmaguire/oauth2-keycloak": "^6.1",
"league/oauth2-client": "^2.7", "league/oauth2-client": "^2.7",
"bacon/bacon-qr-code": "^2.0" "bacon/bacon-qr-code": "^2.0"
}, },
+825 -472
View File
File diff suppressed because it is too large. Load diff
+1 -4
View File
@@ -14,10 +14,7 @@ if (PHP_VERSION_ID < 50600) {
echo $err; echo $err;
} }
} }
trigger_error( throw new RuntimeException($err);
$err,
E_USER_ERROR
);
} }
require_once __DIR__ . '/composer/autoload_real.php'; require_once __DIR__ . '/composer/autoload_real.php';
+34 -12
View File
@@ -12,6 +12,7 @@
namespace Composer; namespace Composer;
$GLOBALS['_composer_bin_dir'] = __DIR__;
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php'; $GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
if (PHP_VERSION_ID < 80000) { if (PHP_VERSION_ID < 80000) {
@@ -23,18 +24,17 @@ if (PHP_VERSION_ID < 80000) {
{ {
private $handle; private $handle;
private $position; private $position;
private $realpath;
public function stream_open($path, $mode, $options, &$opened_path) public function stream_open($path, $mode, $options, &$opened_path)
{ {
// get rid of composer-bin-proxy:// prefix for __FILE__ & __DIR__ resolution // get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
$opened_path = substr($path, 21); $opened_path = substr($path, 17);
$opened_path = realpath($opened_path) ?: $opened_path; $this->realpath = realpath($opened_path) ?: $opened_path;
$this->handle = fopen($opened_path, $mode); $opened_path = $this->realpath;
$this->handle = fopen($this->realpath, $mode);
$this->position = 0; $this->position = 0;
// remove all traces of this stream wrapper once it has been used
stream_wrapper_unregister('composer-bin-proxy');
return (bool) $this->handle; return (bool) $this->handle;
} }
@@ -66,6 +66,16 @@ if (PHP_VERSION_ID < 80000) {
return $operation ? flock($this->handle, $operation) : true; return $operation ? flock($this->handle, $operation) : true;
} }
public function stream_seek($offset, $whence)
{
if (0 === fseek($this->handle, $offset, $whence)) {
$this->position = ftell($this->handle);
return true;
}
return false;
}
public function stream_tell() public function stream_tell()
{ {
return $this->position; return $this->position;
@@ -78,20 +88,32 @@ if (PHP_VERSION_ID < 80000) {
public function stream_stat() public function stream_stat()
{ {
return fstat($this->handle); return array();
} }
public function stream_set_option($option, $arg1, $arg2) public function stream_set_option($option, $arg1, $arg2)
{ {
return true; return true;
} }
public function url_stat($path, $flags)
{
$path = substr($path, 17);
if (file_exists($path)) {
return stat($path);
}
return false;
}
} }
} }
if (function_exists('stream_wrapper_register') && stream_wrapper_register('composer-bin-proxy', 'Composer\BinProxyWrapper')) { if (
include("composer-bin-proxy://" . __DIR__ . '/..'.'/matthiasmullie/minify/bin/minifycss'); (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
exit(0); || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
) {
return include("phpvfscomposer://" . __DIR__ . '/..'.'/matthiasmullie/minify/bin/minifycss');
} }
} }
include __DIR__ . '/..'.'/matthiasmullie/minify/bin/minifycss'; return include __DIR__ . '/..'.'/matthiasmullie/minify/bin/minifycss';
+34 -12
View File
@@ -12,6 +12,7 @@
namespace Composer; namespace Composer;
$GLOBALS['_composer_bin_dir'] = __DIR__;
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php'; $GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
if (PHP_VERSION_ID < 80000) { if (PHP_VERSION_ID < 80000) {
@@ -23,18 +24,17 @@ if (PHP_VERSION_ID < 80000) {
{ {
private $handle; private $handle;
private $position; private $position;
private $realpath;
public function stream_open($path, $mode, $options, &$opened_path) public function stream_open($path, $mode, $options, &$opened_path)
{ {
// get rid of composer-bin-proxy:// prefix for __FILE__ & __DIR__ resolution // get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
$opened_path = substr($path, 21); $opened_path = substr($path, 17);
$opened_path = realpath($opened_path) ?: $opened_path; $this->realpath = realpath($opened_path) ?: $opened_path;
$this->handle = fopen($opened_path, $mode); $opened_path = $this->realpath;
$this->handle = fopen($this->realpath, $mode);
$this->position = 0; $this->position = 0;
// remove all traces of this stream wrapper once it has been used
stream_wrapper_unregister('composer-bin-proxy');
return (bool) $this->handle; return (bool) $this->handle;
} }
@@ -66,6 +66,16 @@ if (PHP_VERSION_ID < 80000) {
return $operation ? flock($this->handle, $operation) : true; return $operation ? flock($this->handle, $operation) : true;
} }
public function stream_seek($offset, $whence)
{
if (0 === fseek($this->handle, $offset, $whence)) {
$this->position = ftell($this->handle);
return true;
}
return false;
}
public function stream_tell() public function stream_tell()
{ {
return $this->position; return $this->position;
@@ -78,20 +88,32 @@ if (PHP_VERSION_ID < 80000) {
public function stream_stat() public function stream_stat()
{ {
return fstat($this->handle); return array();
} }
public function stream_set_option($option, $arg1, $arg2) public function stream_set_option($option, $arg1, $arg2)
{ {
return true; return true;
} }
public function url_stat($path, $flags)
{
$path = substr($path, 17);
if (file_exists($path)) {
return stat($path);
}
return false;
}
} }
} }
if (function_exists('stream_wrapper_register') && stream_wrapper_register('composer-bin-proxy', 'Composer\BinProxyWrapper')) { if (
include("composer-bin-proxy://" . __DIR__ . '/..'.'/matthiasmullie/minify/bin/minifyjs'); (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
exit(0); || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
) {
return include("phpvfscomposer://" . __DIR__ . '/..'.'/matthiasmullie/minify/bin/minifyjs');
} }
} }
include __DIR__ . '/..'.'/matthiasmullie/minify/bin/minifyjs'; return include __DIR__ . '/..'.'/matthiasmullie/minify/bin/minifyjs';
@@ -0,0 +1,74 @@
name: Test Suite
on:
push:
branches:
- main
pull_request:
jobs:
test:
runs-on: ubuntu-latest
services:
redis:
image: redis
ports:
- 6379:6379
options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3
mongodb:
image: mongo
ports:
- 27017:27017
myriadb:
image: mariadb
env:
MYSQL_ROOT_PASSWORD: root
ports:
- 3808:3808
- 3306:3306
postgres:
image: postgres
env:
POSTGRES_DB: oauth2_server_php
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
options: --health-cmd="pg_isready -h localhost" --health-interval=10s --health-timeout=5s --health-retries=5
strategy:
matrix:
php: [ 7.2, 7.3, 7.4, "8.0", 8.1, 8.2 ]
name: "PHP ${{ matrix.php }} Unit Test"
steps:
- uses: actions/checkout@v2
- uses: codecov/codecov-action@v1
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: mongodb, mbstring, intl, redis, pdo_mysql
- name: Install composer dependencies
uses: nick-invision/retry@v1
with:
timeout_minutes: 10
max_attempts: 3
command: composer install
- name: Run PHPUnit
run: vendor/bin/phpunit -v
phpstan:
name: "PHPStan"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.1
- name: Install composer dependencies
uses: nick-invision/retry@v1
with:
timeout_minutes: 10
max_attempts: 3
command: composer install
- name: Run PHPStan
run: |
composer require phpstan/phpstan
vendor/bin/phpstan analyse --level=0 src/
@@ -1,8 +1,6 @@
oauth2-server-php oauth2-server-php
================= =================
[![Build Status](https://travis-ci.org/bshaffer/oauth2-server-php.svg?branch=master)](https://travis-ci.org/bshaffer/oauth2-server-php) [![Total Downloads](https://poser.pugx.org/bshaffer/oauth2-server-php/downloads)](https://packagist.org/packages/bshaffer/oauth2-server-php)
[![Total Downloads](https://poser.pugx.org/bshaffer/oauth2-server-php/downloads.png)](https://packagist.org/packages/bshaffer/oauth2-server-php)
View the [complete documentation](https://bshaffer.github.io/oauth2-server-php-docs/) View the [complete documentation](https://bshaffer.github.io/oauth2-server-php-docs/)
@@ -16,21 +16,21 @@
"psr-0": { "OAuth2": "src/" } "psr-0": { "OAuth2": "src/" }
}, },
"require":{ "require":{
"php":">=5.3.9" "php":">=7.2"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^4.0", "phpunit/phpunit": "^7.5||^8.0",
"aws/aws-sdk-php": "~2.8", "aws/aws-sdk-php": "^2.8",
"firebase/php-jwt": "~2.2", "firebase/php-jwt": "^6.4",
"predis/predis": "dev-master", "predis/predis": "^1.1",
"thobbs/phpcassa": "dev-master", "thobbs/phpcassa": "dev-master",
"mongodb/mongodb": "^1.1" "yoast/phpunit-polyfills": "^1.0"
}, },
"suggest": { "suggest": {
"predis/predis": "Required to use Redis storage", "predis/predis": "Required to use Redis storage",
"thobbs/phpcassa": "Required to use Cassandra storage", "thobbs/phpcassa": "Required to use Cassandra storage",
"aws/aws-sdk-php": "~2.8 is required to use DynamoDB storage", "aws/aws-sdk-php": "~2.8 is required to use DynamoDB storage",
"firebase/php-jwt": "~2.2 is required to use JWT features", "firebase/php-jwt": "~v6.4 is required to use JWT features",
"mongodb/mongodb": "^1.1 is required to use MongoDB storage" "mongodb/mongodb": "^1.1 is required to use MongoDB storage"
} }
} }
@@ -39,7 +39,7 @@ class Autoloader
* Handles autoloading of classes. * Handles autoloading of classes.
* *
* @param string $class - A class name. * @param string $class - A class name.
* @return boolean - Returns true if the class has been loaded * @return void
*/ */
public function autoload($class) public function autoload($class)
{ {
@@ -112,7 +112,7 @@ class HttpBasic implements ClientAssertionTypeInterface
* *
* @ingroup oauth2_section_2 * @ingroup oauth2_section_2
*/ */
public function getClientCredentials(RequestInterface $request, ResponseInterface $response = null) public function getClientCredentials(RequestInterface $request, ?ResponseInterface $response = null)
{ {
if (!is_null($request->headers('PHP_AUTH_USER')) && !is_null($request->headers('PHP_AUTH_PW'))) { if (!is_null($request->headers('PHP_AUTH_USER')) && !is_null($request->headers('PHP_AUTH_PW'))) {
return array('client_id' => $request->headers('PHP_AUTH_USER'), 'client_secret' => $request->headers('PHP_AUTH_PW')); return array('client_id' => $request->headers('PHP_AUTH_USER'), 'client_secret' => $request->headers('PHP_AUTH_PW'));
@@ -78,7 +78,7 @@ class AuthorizeController implements AuthorizeControllerInterface
* ); * );
* @endcode * @endcode
*/ */
public function __construct(ClientInterface $clientStorage, array $responseTypes = array(), array $config = array(), ScopeInterface $scopeUtil = null) public function __construct(ClientInterface $clientStorage, array $responseTypes = array(), array $config = array(), ?ScopeInterface $scopeUtil = null)
{ {
$this->clientStorage = $clientStorage; $this->clientStorage = $clientStorage;
$this->responseTypes = $responseTypes; $this->responseTypes = $responseTypes;
@@ -87,6 +87,7 @@ class AuthorizeController implements AuthorizeControllerInterface
'enforce_state' => true, 'enforce_state' => true,
'require_exact_redirect_uri' => true, 'require_exact_redirect_uri' => true,
'redirect_status_code' => 302, 'redirect_status_code' => 302,
'enforce_pkce' => false,
), $config); ), $config);
if (is_null($scopeUtil)) { if (is_null($scopeUtil)) {
@@ -47,7 +47,7 @@ class ResourceController implements ResourceControllerInterface
* @param array $config * @param array $config
* @param ScopeInterface $scopeUtil * @param ScopeInterface $scopeUtil
*/ */
public function __construct(TokenTypeInterface $tokenType, AccessTokenInterface $tokenStorage, $config = array(), ScopeInterface $scopeUtil = null) public function __construct(TokenTypeInterface $tokenType, AccessTokenInterface $tokenStorage, $config = array(), ?ScopeInterface $scopeUtil = null)
{ {
$this->tokenType = $tokenType; $this->tokenType = $tokenType;
$this->tokenStorage = $tokenStorage; $this->tokenStorage = $tokenStorage;
@@ -13,7 +13,7 @@ use OAuth2\ResponseInterface;
* @code * @code
* if (!$resourceController->verifyResourceRequest(OAuth2\Request::createFromGlobals(), $response = new OAuth2\Response())) { * if (!$resourceController->verifyResourceRequest(OAuth2\Request::createFromGlobals(), $response = new OAuth2\Response())) {
* $response->send(); // authorization failed * $response->send(); // authorization failed
* exit(); * die();
* } * }
* return json_encode($resource); // valid token! Send the stuff! * return json_encode($resource); // valid token! Send the stuff!
* @endcode * @endcode
@@ -54,7 +54,7 @@ class TokenController implements TokenControllerInterface
* @param ScopeInterface $scopeUtil * @param ScopeInterface $scopeUtil
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function __construct(AccessTokenInterface $accessToken, ClientInterface $clientStorage, array $grantTypes = array(), ClientAssertionTypeInterface $clientAssertionType = null, ScopeInterface $scopeUtil = null) public function __construct(AccessTokenInterface $accessToken, ClientInterface $clientStorage, array $grantTypes = array(), ?ClientAssertionTypeInterface $clientAssertionType = null, ?ScopeInterface $scopeUtil = null)
{ {
if (is_null($clientAssertionType)) { if (is_null($clientAssertionType)) {
foreach ($grantTypes as $grantType) { foreach ($grantTypes as $grantType) {
@@ -2,6 +2,9 @@
namespace OAuth2\Encryption; namespace OAuth2\Encryption;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
/** /**
* Bridge file to use the firebase/php-jwt package for JWT encoding and decoding. * Bridge file to use the firebase/php-jwt package for JWT encoding and decoding.
* @author Francis Chuang <francis.chuang@gmail.com> * @author Francis Chuang <francis.chuang@gmail.com>
@@ -10,26 +13,36 @@ class FirebaseJwt implements EncryptionInterface
{ {
public function __construct() public function __construct()
{ {
if (!class_exists('\JWT')) { if (!class_exists(JWT::class)) {
throw new \ErrorException('firebase/php-jwt must be installed to use this feature. You can do this by running "composer require firebase/php-jwt"'); throw new \ErrorException('firebase/php-jwt must be installed to use this feature. You can do this by running "composer require firebase/php-jwt"');
} }
} }
public function encode($payload, $key, $alg = 'HS256', $keyId = null) public function encode($payload, $key, $alg = 'HS256', $keyId = null)
{ {
return \JWT::encode($payload, $key, $alg, $keyId); return JWT::encode($payload, $key, $alg, $keyId);
} }
public function decode($jwt, $key = null, $allowedAlgorithms = null) public function decode($jwt, $key = null, $allowedAlgorithms = null)
{ {
try { try {
//Maintain BC: Do not verify if no algorithms are passed in. //Maintain BC: Do not verify if no algorithms are passed in.
if (!$allowedAlgorithms) { if (!$allowedAlgorithms) {
$key = null; $tks = \explode('.', $jwt);
if (\count($tks) === 3) {
[$headb64] = $tks;
$headerRaw = JWT::urlsafeB64Decode($headb64);
if (($header = JWT::jsonDecode($headerRaw))) {
$key = new Key($key, $header->alg);
}
}
} elseif(is_array($allowedAlgorithms)) {
$key = new Key($key, $allowedAlgorithms[0]);
} else {
$key = new Key($key, $allowedAlgorithms);
} }
return (array)\JWT::decode($jwt, $key, $allowedAlgorithms); return (array) JWT::decode($jwt, $key);
} catch (\Exception $e) { } catch (\Exception $e) {
return false; return false;
} }
@@ -37,11 +50,11 @@ class FirebaseJwt implements EncryptionInterface
public function urlSafeB64Encode($data) public function urlSafeB64Encode($data)
{ {
return \JWT::urlsafeB64Encode($data); return JWT::urlsafeB64Encode($data);
} }
public function urlSafeB64Decode($b64) public function urlSafeB64Decode($b64)
{ {
return \JWT::urlsafeB64Decode($b64); return JWT::urlsafeB64Decode($b64);
} }
} }
@@ -84,6 +84,41 @@ class AuthorizationCode implements GrantTypeInterface
return false; return false;
} }
if (isset($authCode['code_challenge']) && $authCode['code_challenge']) {
if (!($code_verifier = $request->request('code_verifier'))) {
$response->setError(400, 'code_verifier_missing', "The PKCE code verifier parameter is required.");
return false;
}
// Validate code_verifier according to RFC-7636
// @see: https://tools.ietf.org/html/rfc7636#section-4.1
if (preg_match('/^[A-Za-z0-9-._~]{43,128}$/', $code_verifier) !== 1) {
$response->setError(400, 'code_verifier_invalid', "The PKCE code verifier parameter is invalid.");
return false;
}
$code_verifier = $request->request('code_verifier');
switch ($authCode['code_challenge_method']) {
case 'S256':
$code_verifier_hashed = strtr(rtrim(base64_encode(hash('sha256', $code_verifier, true)), '='), '+/', '-_');
break;
case 'plain':
$code_verifier_hashed = $code_verifier;
break;
default:
$response->setError(400, 'code_challenge_method_invalid', "Unknown PKCE code challenge method.");
return FALSE;
}
if ($code_verifier_hashed !== $authCode['code_challenge']) {
$response->setError(400, 'code_verifier_mismatch', "The PKCE code verifier parameter does not match the code challenge.");
return FALSE;
}
}
if (!isset($authCode['code'])) { if (!isset($authCode['code'])) {
$authCode['code'] = $code; // used to expire the code after the access token is granted $authCode['code'] = $code; // used to expire the code after the access token is granted
} }
@@ -37,7 +37,7 @@ class JwtBearer implements GrantTypeInterface, ClientAssertionTypeInterface
* @param EncryptionInterface|JWT $jwtUtil - OPTONAL The class used to decode, encode and verify JWTs. * @param EncryptionInterface|JWT $jwtUtil - OPTONAL The class used to decode, encode and verify JWTs.
* @param array $config * @param array $config
*/ */
public function __construct(JwtBearerInterface $storage, $audience, EncryptionInterface $jwtUtil = null, array $config = array()) public function __construct(JwtBearerInterface $storage, $audience, ?EncryptionInterface $jwtUtil = null, array $config = array())
{ {
$this->storage = $storage; $this->storage = $storage;
$this->audience = $audience; $this->audience = $audience;
@@ -46,13 +46,13 @@ class JwtBearer implements GrantTypeInterface, ClientAssertionTypeInterface
$jwtUtil = new Jwt(); $jwtUtil = new Jwt();
} }
$this->config = array_merge(array( $config = array_merge(array(
'allowed_algorithms' => array('RS256', 'RS384', 'RS512') 'allowed_algorithms' => array('RS256', 'RS384', 'RS512')
), $config); ), $config);
$this->jwtUtil = $jwtUtil; $this->jwtUtil = $jwtUtil;
$this->allowedAlgorithms = $this->config['allowed_algorithms']; $this->allowedAlgorithms = $config['allowed_algorithms'];
} }
/** /**
@@ -127,7 +127,7 @@ class JwtBearer implements GrantTypeInterface, ClientAssertionTypeInterface
} }
// Check expiration // Check expiration
if (ctype_digit($jwt['exp'])) { if (ctype_digit((string)$jwt['exp'])) {
if ($jwt['exp'] <= time()) { if ($jwt['exp'] <= time()) {
$response->setError(400, 'invalid_grant', "JWT has expired"); $response->setError(400, 'invalid_grant', "JWT has expired");
@@ -141,7 +141,7 @@ class JwtBearer implements GrantTypeInterface, ClientAssertionTypeInterface
// Check the not before time // Check the not before time
if ($notBefore = $jwt['nbf']) { if ($notBefore = $jwt['nbf']) {
if (ctype_digit($notBefore)) { if (ctype_digit((string)$notBefore)) {
if ($notBefore > time()) { if ($notBefore > time()) {
$response->setError(400, 'invalid_grant', "JWT cannot be used before the Not Before (nbf) time"); $response->setError(400, 'invalid_grant', "JWT cannot be used before the Not Before (nbf) time");
@@ -16,6 +16,16 @@ class AuthorizeController extends BaseAuthorizeController implements AuthorizeCo
*/ */
private $nonce; private $nonce;
/**
* @var mixed
*/
protected $code_challenge;
/**
* @var mixed
*/
protected $code_challenge_method;
/** /**
* Set not authorized response * Set not authorized response
* *
@@ -65,6 +75,10 @@ class AuthorizeController extends BaseAuthorizeController implements AuthorizeCo
// add the nonce to return with the redirect URI // add the nonce to return with the redirect URI
$params['nonce'] = $this->nonce; $params['nonce'] = $this->nonce;
// Add PKCE code challenge.
$params['code_challenge'] = $this->code_challenge;
$params['code_challenge_method'] = $this->code_challenge_method;
return $params; return $params;
} }
@@ -90,6 +104,32 @@ class AuthorizeController extends BaseAuthorizeController implements AuthorizeCo
$this->nonce = $nonce; $this->nonce = $nonce;
$code_challenge = $request->query('code_challenge');
$code_challenge_method = $request->query('code_challenge_method');
if ($this->config['enforce_pkce']) {
if (!$code_challenge) {
$response->setError(400, 'missing_code_challenge', 'This application requires you provide a PKCE code challenge');
return false;
}
if (preg_match('/^[A-Za-z0-9-._~]{43,128}$/', $code_challenge) !== 1) {
$response->setError(400, 'invalid_code_challenge', 'The PKCE code challenge supplied is invalid');
return false;
}
if (!in_array($code_challenge_method, array('plain', 'S256'), true)) {
$response->setError(400, 'missing_code_challenge_method', 'This application requires you specify a PKCE code challenge method');
return false;
}
}
$this->code_challenge = $code_challenge;
$this->code_challenge_method = $code_challenge_method;
return true; return true;
} }
@@ -30,7 +30,7 @@ class UserInfoController extends ResourceController implements UserInfoControlle
* @param array $config * @param array $config
* @param ScopeInterface $scopeUtil * @param ScopeInterface $scopeUtil
*/ */
public function __construct(TokenTypeInterface $tokenType, AccessTokenInterface $tokenStorage, UserClaimsInterface $userClaimsStorage, $config = array(), ScopeInterface $scopeUtil = null) public function __construct(TokenTypeInterface $tokenType, AccessTokenInterface $tokenStorage, UserClaimsInterface $userClaimsStorage, $config = array(), ?ScopeInterface $scopeUtil = null)
{ {
parent::__construct($tokenType, $tokenStorage, $config, $scopeUtil); parent::__construct($tokenType, $tokenStorage, $config, $scopeUtil);
@@ -31,9 +31,9 @@ class AuthorizationCode extends BaseAuthorizationCode implements AuthorizationCo
// build the URL to redirect to // build the URL to redirect to
$result = array('query' => array()); $result = array('query' => array());
$params += array('scope' => null, 'state' => null, 'id_token' => null); $params += array('scope' => null, 'state' => null, 'id_token' => null, 'code_challenge' => null, 'code_challenge_method' => null);
$result['query']['code'] = $this->createAuthorizationCode($params['client_id'], $user_id, $params['redirect_uri'], $params['scope'], $params['id_token']); $result['query']['code'] = $this->createAuthorizationCode($params['client_id'], $user_id, $params['redirect_uri'], $params['scope'], $params['id_token'], $params['code_challenge'], $params['code_challenge_method']);
if (isset($params['state'])) { if (isset($params['state'])) {
$result['query']['state'] = $params['state']; $result['query']['state'] = $params['state'];
@@ -56,10 +56,10 @@ class AuthorizationCode extends BaseAuthorizationCode implements AuthorizationCo
* @see http://tools.ietf.org/html/rfc6749#section-4 * @see http://tools.ietf.org/html/rfc6749#section-4
* @ingroup oauth2_section_4 * @ingroup oauth2_section_4
*/ */
public function createAuthorizationCode($client_id, $user_id, $redirect_uri, $scope = null, $id_token = null) public function createAuthorizationCode($client_id, $user_id, $redirect_uri, $scope = null, $id_token = null, $code_challenge = null, $code_challenge_method = null)
{ {
$code = $this->generateAuthorizationCode(); $code = $this->generateAuthorizationCode();
$this->storage->setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, time() + $this->config['auth_code_lifetime'], $scope, $id_token); $this->storage->setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, time() + $this->config['auth_code_lifetime'], $scope, $id_token, $code_challenge, $code_challenge_method);
return $code; return $code;
} }
@@ -38,7 +38,7 @@ class IdToken implements IdTokenInterface
* @param EncryptionInterface $encryptionUtil * @param EncryptionInterface $encryptionUtil
* @throws LogicException * @throws LogicException
*/ */
public function __construct(UserClaimsInterface $userClaimsStorage, PublicKeyInterface $publicKeyStorage, array $config = array(), EncryptionInterface $encryptionUtil = null) public function __construct(UserClaimsInterface $userClaimsStorage, PublicKeyInterface $publicKeyStorage, array $config = array(), ?EncryptionInterface $encryptionUtil = null)
{ {
$this->userClaimsStorage = $userClaimsStorage; $this->userClaimsStorage = $userClaimsStorage;
$this->publicKeyStorage = $publicKeyStorage; $this->publicKeyStorage = $publicKeyStorage;
@@ -33,5 +33,5 @@ interface AuthorizationCodeInterface extends BaseAuthorizationCodeInterface
* *
* @ingroup oauth2_section_4 * @ingroup oauth2_section_4
*/ */
public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null); public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null, $code_challenge = null, $code_challenge_method = null);
} }
@@ -34,7 +34,7 @@ class Request implements RequestInterface
* *
* @api * @api
*/ */
public function __construct(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null, array $headers = null) public function __construct(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null, ?array $headers = null)
{ {
$this->initialize($query, $request, $attributes, $cookies, $files, $server, $content, $headers); $this->initialize($query, $request, $attributes, $cookies, $files, $server, $content, $headers);
} }
@@ -55,7 +55,7 @@ class Request implements RequestInterface
* *
* @api * @api
*/ */
public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null, array $headers = null) public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null, ?array $headers = null)
{ {
$this->request = $request; $this->request = $request;
$this->query = $query; $this->query = $query;
@@ -140,11 +140,10 @@ class Response implements ResponseInterface
} }
/** /**
* @param int $statusCode
* @param string $text * @param string $text
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function setStatusCode($statusCode, $text = null) public function setStatusCode(int $statusCode, $text = null)
{ {
$this->statusCode = (int) $statusCode; $this->statusCode = (int) $statusCode;
if ($this->isInvalid()) { if ($this->isInvalid()) {
@@ -20,10 +20,7 @@ interface ResponseInterface
*/ */
public function addHttpHeaders(array $httpHeaders); public function addHttpHeaders(array $httpHeaders);
/** public function setStatusCode(int $statusCode);
* @param int $statusCode
*/
public function setStatusCode($statusCode);
/** /**
* @param int $statusCode * @param int $statusCode
@@ -38,7 +38,7 @@ class AccessToken implements AccessTokenInterface
* ); * );
* @endcode * @endcode
*/ */
public function __construct(AccessTokenStorageInterface $tokenStorage, RefreshTokenInterface $refreshStorage = null, array $config = array()) public function __construct(AccessTokenStorageInterface $tokenStorage, ?RefreshTokenInterface $refreshStorage = null, array $config = array())
{ {
$this->tokenStorage = $tokenStorage; $this->tokenStorage = $tokenStorage;
$this->refreshStorage = $refreshStorage; $this->refreshStorage = $refreshStorage;
@@ -26,9 +26,9 @@ class AuthorizationCode implements AuthorizationCodeInterface
// build the URL to redirect to // build the URL to redirect to
$result = array('query' => array()); $result = array('query' => array());
$params += array('scope' => null, 'state' => null); $params += array('scope' => null, 'state' => null, 'code_challenge' => null, 'code_challenge_method' => null);
$result['query']['code'] = $this->createAuthorizationCode($params['client_id'], $user_id, $params['redirect_uri'], $params['scope']); $result['query']['code'] = $this->createAuthorizationCode($params['client_id'], $user_id, $params['redirect_uri'], $params['scope'], $params['code_challenge'], $params['code_challenge_method']);
if (isset($params['state'])) { if (isset($params['state'])) {
$result['query']['state'] = $params['state']; $result['query']['state'] = $params['state'];
@@ -53,10 +53,10 @@ class AuthorizationCode implements AuthorizationCodeInterface
* @see http://tools.ietf.org/html/rfc6749#section-4 * @see http://tools.ietf.org/html/rfc6749#section-4
* @ingroup oauth2_section_4 * @ingroup oauth2_section_4
*/ */
public function createAuthorizationCode($client_id, $user_id, $redirect_uri, $scope = null) public function createAuthorizationCode($client_id, $user_id, $redirect_uri, $scope = null, $code_challenge = null, $code_challenge_method = null)
{ {
$code = $this->generateAuthorizationCode(); $code = $this->generateAuthorizationCode();
$this->storage->setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, time() + $this->config['auth_code_lifetime'], $scope); $this->storage->setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, time() + $this->config['auth_code_lifetime'], $scope, null, $code_challenge, $code_challenge_method);
return $code; return $code;
} }
@@ -26,7 +26,7 @@ class JwtAccessToken extends AccessToken
* or just the token ID is stored * or just the token ID is stored
* @param EncryptionInterface $encryptionUtil - * @param EncryptionInterface $encryptionUtil -
*/ */
public function __construct(PublicKeyInterface $publicKeyStorage = null, AccessTokenStorageInterface $tokenStorage = null, RefreshTokenInterface $refreshStorage = null, array $config = array(), EncryptionInterface $encryptionUtil = null) public function __construct(?PublicKeyInterface $publicKeyStorage = null, ?AccessTokenStorageInterface $tokenStorage = null, ?RefreshTokenInterface $refreshStorage = null, array $config = array(), ?EncryptionInterface $encryptionUtil = null)
{ {
$this->publicKeyStorage = $publicKeyStorage; $this->publicKeyStorage = $publicKeyStorage;
$config = array_merge(array( $config = array_merge(array(
@@ -150,7 +150,7 @@ class Server implements ResourceControllerInterface,
* *
* @ingroup oauth2_section_7 * @ingroup oauth2_section_7
*/ */
public function __construct($storage = array(), array $config = array(), array $grantTypes = array(), array $responseTypes = array(), TokenTypeInterface $tokenType = null, ScopeInterface $scopeUtil = null, ClientAssertionTypeInterface $clientAssertionType = null) public function __construct($storage = array(), array $config = array(), array $grantTypes = array(), array $responseTypes = array(), ?TokenTypeInterface $tokenType = null, ?ScopeInterface $scopeUtil = null, ?ClientAssertionTypeInterface $clientAssertionType = null)
{ {
$storage = is_array($storage) ? $storage : array($storage); $storage = is_array($storage) ? $storage : array($storage);
$this->storages = array(); $this->storages = array();
@@ -172,6 +172,7 @@ class Server implements ResourceControllerInterface,
'enforce_state' => true, 'enforce_state' => true,
'require_exact_redirect_uri' => true, 'require_exact_redirect_uri' => true,
'allow_implicit' => false, 'allow_implicit' => false,
'enforce_pkce' => false,
'allow_credentials_in_request_body' => true, 'allow_credentials_in_request_body' => true,
'allow_public_clients' => true, 'allow_public_clients' => true,
'always_issue_new_refresh_token' => false, 'always_issue_new_refresh_token' => false,
@@ -288,7 +289,7 @@ class Server implements ResourceControllerInterface,
* *
* @see http://openid.net/specs/openid-connect-core-1_0.html#UserInfo * @see http://openid.net/specs/openid-connect-core-1_0.html#UserInfo
*/ */
public function handleUserInfoRequest(RequestInterface $request, ResponseInterface $response = null) public function handleUserInfoRequest(RequestInterface $request, ?ResponseInterface $response = null)
{ {
$this->response = is_null($response) ? new Response() : $response; $this->response = is_null($response) ? new Response() : $response;
$this->getUserInfoController()->handleUserInfoRequest($request, $this->response); $this->getUserInfoController()->handleUserInfoRequest($request, $this->response);
@@ -314,7 +315,7 @@ class Server implements ResourceControllerInterface,
* *
* @ingroup oauth2_section_4 * @ingroup oauth2_section_4
*/ */
public function handleTokenRequest(RequestInterface $request, ResponseInterface $response = null) public function handleTokenRequest(RequestInterface $request, ?ResponseInterface $response = null)
{ {
$this->response = is_null($response) ? new Response() : $response; $this->response = is_null($response) ? new Response() : $response;
$this->getTokenController()->handleTokenRequest($request, $this->response); $this->getTokenController()->handleTokenRequest($request, $this->response);
@@ -327,7 +328,7 @@ class Server implements ResourceControllerInterface,
* @param ResponseInterface $response - Response object * @param ResponseInterface $response - Response object
* @return mixed * @return mixed
*/ */
public function grantAccessToken(RequestInterface $request, ResponseInterface $response = null) public function grantAccessToken(RequestInterface $request, ?ResponseInterface $response = null)
{ {
$this->response = is_null($response) ? new Response() : $response; $this->response = is_null($response) ? new Response() : $response;
$value = $this->getTokenController()->grantAccessToken($request, $this->response); $value = $this->getTokenController()->grantAccessToken($request, $this->response);
@@ -345,7 +346,7 @@ class Server implements ResourceControllerInterface,
* @param ResponseInterface $response * @param ResponseInterface $response
* @return Response|ResponseInterface * @return Response|ResponseInterface
*/ */
public function handleRevokeRequest(RequestInterface $request, ResponseInterface $response = null) public function handleRevokeRequest(RequestInterface $request, ?ResponseInterface $response = null)
{ {
$this->response = is_null($response) ? new Response() : $response; $this->response = is_null($response) ? new Response() : $response;
$this->getTokenController()->handleRevokeRequest($request, $this->response); $this->getTokenController()->handleRevokeRequest($request, $this->response);
@@ -407,7 +408,7 @@ class Server implements ResourceControllerInterface,
* *
* @ingroup oauth2_section_3 * @ingroup oauth2_section_3
*/ */
public function validateAuthorizeRequest(RequestInterface $request, ResponseInterface $response = null) public function validateAuthorizeRequest(RequestInterface $request, ?ResponseInterface $response = null)
{ {
$this->response = is_null($response) ? new Response() : $response; $this->response = is_null($response) ? new Response() : $response;
$value = $this->getAuthorizeController()->validateAuthorizeRequest($request, $this->response); $value = $this->getAuthorizeController()->validateAuthorizeRequest($request, $this->response);
@@ -421,7 +422,7 @@ class Server implements ResourceControllerInterface,
* @param string $scope - Scope * @param string $scope - Scope
* @return mixed * @return mixed
*/ */
public function verifyResourceRequest(RequestInterface $request, ResponseInterface $response = null, $scope = null) public function verifyResourceRequest(RequestInterface $request, ?ResponseInterface $response = null, $scope = null)
{ {
$this->response = is_null($response) ? new Response() : $response; $this->response = is_null($response) ? new Response() : $response;
$value = $this->getResourceController()->verifyResourceRequest($request, $this->response, $scope); $value = $this->getResourceController()->verifyResourceRequest($request, $this->response, $scope);
@@ -434,7 +435,7 @@ class Server implements ResourceControllerInterface,
* @param ResponseInterface $response - Response object * @param ResponseInterface $response - Response object
* @return mixed * @return mixed
*/ */
public function getAccessTokenData(RequestInterface $request, ResponseInterface $response = null) public function getAccessTokenData(RequestInterface $request, ?ResponseInterface $response = null)
{ {
$this->response = is_null($response) ? new Response() : $response; $this->response = is_null($response) ? new Response() : $response;
$value = $this->getResourceController()->getAccessTokenData($request, $this->response); $value = $this->getResourceController()->getAccessTokenData($request, $this->response);
@@ -577,7 +578,7 @@ class Server implements ResourceControllerInterface,
} }
} }
$config = array_intersect_key($this->config, array_flip(explode(' ', 'allow_implicit enforce_state require_exact_redirect_uri'))); $config = array_intersect_key($this->config, array_flip(explode(' ', 'allow_implicit enforce_state require_exact_redirect_uri enforce_pkce')));
if ($this->config['use_openid_connect']) { if ($this->config['use_openid_connect']) {
return new OpenIDAuthorizeController($this->storages['client'], $this->responseTypes, $config, $this->getScopeUtil()); return new OpenIDAuthorizeController($this->storages['client'], $this->responseTypes, $config, $this->getScopeUtil());
@@ -191,11 +191,11 @@ class Cassandra implements AuthorizationCodeInterface,
* @param string $id_token * @param string $id_token
* @return bool * @return bool
*/ */
public function setAuthorizationCode($authorization_code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) public function setAuthorizationCode($authorization_code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null, $code_challenge = null, $code_challenge_method = null)
{ {
return $this->setValue( return $this->setValue(
$this->config['code_key'] . $authorization_code, $this->config['code_key'] . $authorization_code,
compact('authorization_code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token'), compact('authorization_code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token', 'code_challenge', 'code_challenge_method'),
$expires $expires
); );
} }
@@ -2,6 +2,7 @@
namespace OAuth2\Storage; namespace OAuth2\Storage;
use Couchbase;
use OAuth2\OpenID\Storage\AuthorizationCodeInterface as OpenIDAuthorizationCodeInterface; use OAuth2\OpenID\Storage\AuthorizationCodeInterface as OpenIDAuthorizationCodeInterface;
/** /**
@@ -27,14 +28,18 @@ class CouchbaseDB implements AuthorizationCodeInterface,
public function __construct($connection, $config = array()) public function __construct($connection, $config = array())
{ {
if ($connection instanceof \Couchbase) { if (!class_exists(Couchbase::class)) {
throw new \RuntimeException('Missing Couchbase');
}
if ($connection instanceof Couchbase) {
$this->db = $connection; $this->db = $connection;
} else { } else {
if (!is_array($connection) || !is_array($connection['servers'])) { if (!is_array($connection) || !is_array($connection['servers'])) {
throw new \InvalidArgumentException('First argument to OAuth2\Storage\CouchbaseDB must be an instance of Couchbase or a configuration array containing a server array'); throw new \InvalidArgumentException('First argument to OAuth2\Storage\CouchbaseDB must be an instance of Couchbase or a configuration array containing a server array');
} }
$this->db = new \Couchbase($connection['servers'], (!isset($connection['username'])) ? '' : $connection['username'], (!isset($connection['password'])) ? '' : $connection['password'], $connection['bucket'], false); $this->db = new Couchbase($connection['servers'], (!isset($connection['username'])) ? '' : $connection['username'], (!isset($connection['password'])) ? '' : $connection['password'], $connection['bucket'], false);
} }
$this->config = array_merge(array( $this->config = array_merge(array(
@@ -173,7 +178,7 @@ class CouchbaseDB implements AuthorizationCodeInterface,
return is_null($code) ? false : $code; return is_null($code) ? false : $code;
} }
public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null, $code_challenge = null, $code_challenge_method = null)
{ {
// if it exists, update it. // if it exists, update it.
if ($this->getAuthorizationCode($code)) { if ($this->getAuthorizationCode($code)) {
@@ -185,6 +190,8 @@ class CouchbaseDB implements AuthorizationCodeInterface,
'expires' => $expires, 'expires' => $expires,
'scope' => $scope, 'scope' => $scope,
'id_token' => $id_token, 'id_token' => $id_token,
'code_challenge' => $code_challenge,
'code_challenge_method' => $code_challenge_method,
)); ));
} else { } else {
$this->setObjectByType('code_table',$code,array( $this->setObjectByType('code_table',$code,array(
@@ -195,6 +202,8 @@ class CouchbaseDB implements AuthorizationCodeInterface,
'expires' => $expires, 'expires' => $expires,
'scope' => $scope, 'scope' => $scope,
'id_token' => $id_token, 'id_token' => $id_token,
'code_challenge' => $code_challenge,
'code_challenge_method' => $code_challenge_method,
)); ));
} }
@@ -213,12 +213,12 @@ class DynamoDB implements
} }
public function setAuthorizationCode($authorization_code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) public function setAuthorizationCode($authorization_code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null, $code_challenge = null, $code_challenge_method = null)
{ {
// convert expires to datestring // convert expires to datestring
$expires = date('Y-m-d H:i:s', $expires); $expires = date('Y-m-d H:i:s', $expires);
$clientData = compact('authorization_code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'id_token', 'scope'); $clientData = compact('authorization_code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token', 'code_challenge', 'code_challenge_method');
$clientData = array_filter($clientData, 'self::isNotEmpty'); $clientData = array_filter($clientData, 'self::isNotEmpty');
$result = $this->client->putItem(array( $result = $this->client->putItem(array(
@@ -21,7 +21,7 @@ class JwtAccessToken implements JwtAccessTokenInterface
* is not necessary when using this grant type. * is not necessary when using this grant type.
* @param OAuth2\Encryption\EncryptionInterface $encryptionUtil OPTIONAL class to use for "encode" and "decode" functions. * @param OAuth2\Encryption\EncryptionInterface $encryptionUtil OPTIONAL class to use for "encode" and "decode" functions.
*/ */
public function __construct(PublicKeyInterface $publicKeyStorage, AccessTokenInterface $tokenStorage = null, EncryptionInterface $encryptionUtil = null) public function __construct(PublicKeyInterface $publicKeyStorage, ?AccessTokenInterface $tokenStorage = null, ?EncryptionInterface $encryptionUtil = null)
{ {
$this->publicKeyStorage = $publicKeyStorage; $this->publicKeyStorage = $publicKeyStorage;
$this->tokenStorage = $tokenStorage; $this->tokenStorage = $tokenStorage;
@@ -74,9 +74,9 @@ class Memory implements AuthorizationCodeInterface,
), $this->authorizationCodes[$code]); ), $this->authorizationCodes[$code]);
} }
public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null, $code_challenge = null, $code_challenge_method = null)
{ {
$this->authorizationCodes[$code] = compact('code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token'); $this->authorizationCodes[$code] = compact('code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token', 'code_challenge', 'code_challenge_method');
return true; return true;
} }
@@ -179,7 +179,7 @@ class Mongo implements AuthorizationCodeInterface,
return is_null($code) ? false : $code; return is_null($code) ? false : $code;
} }
public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null, $code_challenge = null, $code_challenge_method = null)
{ {
// if it exists, update it. // if it exists, update it.
if ($this->getAuthorizationCode($code)) { if ($this->getAuthorizationCode($code)) {
@@ -192,6 +192,8 @@ class Mongo implements AuthorizationCodeInterface,
'expires' => $expires, 'expires' => $expires,
'scope' => $scope, 'scope' => $scope,
'id_token' => $id_token, 'id_token' => $id_token,
'code_challenge' => $code_challenge,
'code_challenge_method' => $code_challenge_method,
)) ))
); );
} else { } else {
@@ -203,6 +205,8 @@ class Mongo implements AuthorizationCodeInterface,
'expires' => $expires, 'expires' => $expires,
'scope' => $scope, 'scope' => $scope,
'id_token' => $id_token, 'id_token' => $id_token,
'code_challenge' => $code_challenge,
'code_challenge_method' => $code_challenge_method,
); );
$this->collection('code_table')->insert($token); $this->collection('code_table')->insert($token);
} }
@@ -32,6 +32,9 @@ class MongoDB implements AuthorizationCodeInterface,
public function __construct($connection, $config = array()) public function __construct($connection, $config = array())
{ {
if (!class_exists(Database::class) || !class_exists(Client::class)) {
throw new \LogicException('Missing MongoDB php extension. Please install mongodb.so');
}
if ($connection instanceof Database) { if ($connection instanceof Database) {
$this->db = $connection; $this->db = $connection;
} else { } else {
@@ -167,7 +170,7 @@ class MongoDB implements AuthorizationCodeInterface,
return is_null($code) ? false : $code; return is_null($code) ? false : $code;
} }
public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null, $code_challenge = null, $code_challenge_method = null)
{ {
// if it exists, update it. // if it exists, update it.
if ($this->getAuthorizationCode($code)) { if ($this->getAuthorizationCode($code)) {
@@ -180,6 +183,8 @@ class MongoDB implements AuthorizationCodeInterface,
'expires' => $expires, 'expires' => $expires,
'scope' => $scope, 'scope' => $scope,
'id_token' => $id_token, 'id_token' => $id_token,
'code_challenge' => $code_challenge,
'code_challenge_method' => $code_challenge_method,
)) ))
); );
return $result->getMatchedCount() > 0; return $result->getMatchedCount() > 0;
@@ -192,6 +197,8 @@ class MongoDB implements AuthorizationCodeInterface,
'expires' => $expires, 'expires' => $expires,
'scope' => $scope, 'scope' => $scope,
'id_token' => $id_token, 'id_token' => $id_token,
'code_challenge' => $code_challenge,
'code_challenge_method' => $code_challenge_method,
); );
$result = $this->collection('code_table')->insertOne($token); $result = $this->collection('code_table')->insertOne($token);
return $result->getInsertedCount() > 0; return $result->getInsertedCount() > 0;
@@ -247,7 +247,7 @@ class Pdo implements
* @param string $id_token * @param string $id_token
* @return bool|mixed * @return bool|mixed
*/ */
public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null, $code_challenge = null, $code_challenge_method = null)
{ {
if (func_num_args() > 6) { if (func_num_args() > 6) {
// we are calling with an id token // we are calling with an id token
@@ -259,12 +259,12 @@ class Pdo implements
// if it exists, update it. // if it exists, update it.
if ($this->getAuthorizationCode($code)) { if ($this->getAuthorizationCode($code)) {
$stmt = $this->db->prepare($sql = sprintf('UPDATE %s SET client_id=:client_id, user_id=:user_id, redirect_uri=:redirect_uri, expires=:expires, scope=:scope where authorization_code=:code', $this->config['code_table'])); $stmt = $this->db->prepare($sql = sprintf('UPDATE %s SET client_id=:client_id, user_id=:user_id, redirect_uri=:redirect_uri, expires=:expires, scope=:scope, code_challenge=:code_challenge, code_challenge_method=:code_challenge_method where authorization_code=:code', $this->config['code_table']));
} else { } else {
$stmt = $this->db->prepare(sprintf('INSERT INTO %s (authorization_code, client_id, user_id, redirect_uri, expires, scope) VALUES (:code, :client_id, :user_id, :redirect_uri, :expires, :scope)', $this->config['code_table'])); $stmt = $this->db->prepare(sprintf('INSERT INTO %s (authorization_code, client_id, user_id, redirect_uri, expires, scope, code_challenge, code_challenge_method) VALUES (:code, :client_id, :user_id, :redirect_uri, :expires, :scope, :code_challenge, :code_challenge_method)', $this->config['code_table']));
} }
return $stmt->execute(compact('code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope')); return $stmt->execute(compact('code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'code_challenge', 'code_challenge_method'));
} }
/** /**
@@ -277,19 +277,19 @@ class Pdo implements
* @param string $id_token * @param string $id_token
* @return bool * @return bool
*/ */
private function setAuthorizationCodeWithIdToken($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) private function setAuthorizationCodeWithIdToken($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null, $code_challenge = null, $code_challenge_method = null)
{ {
// convert expires to datestring // convert expires to datestring
$expires = date('Y-m-d H:i:s', $expires); $expires = date('Y-m-d H:i:s', $expires);
// if it exists, update it. // if it exists, update it.
if ($this->getAuthorizationCode($code)) { if ($this->getAuthorizationCode($code)) {
$stmt = $this->db->prepare($sql = sprintf('UPDATE %s SET client_id=:client_id, user_id=:user_id, redirect_uri=:redirect_uri, expires=:expires, scope=:scope, id_token =:id_token where authorization_code=:code', $this->config['code_table'])); $stmt = $this->db->prepare($sql = sprintf('UPDATE %s SET client_id=:client_id, user_id=:user_id, redirect_uri=:redirect_uri, expires=:expires, scope=:scope, id_token =:id_token, code_challenge=:code_challenge, code_challenge_method=:code_challenge_method where authorization_code=:code', $this->config['code_table']));
} else { } else {
$stmt = $this->db->prepare(sprintf('INSERT INTO %s (authorization_code, client_id, user_id, redirect_uri, expires, scope, id_token) VALUES (:code, :client_id, :user_id, :redirect_uri, :expires, :scope, :id_token)', $this->config['code_table'])); $stmt = $this->db->prepare(sprintf('INSERT INTO %s (authorization_code, client_id, user_id, redirect_uri, expires, scope, id_token, code_challenge, code_challenge_method) VALUES (:code, :client_id, :user_id, :redirect_uri, :expires, :scope, :id_token, :code_challenge, :code_challenge_method)', $this->config['code_table']));
} }
return $stmt->execute(compact('code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token')); return $stmt->execute(compact('code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token', 'code_challenge', 'code_challenge_method'));
} }
/** /**
@@ -676,6 +676,8 @@ class Pdo implements
expires TIMESTAMP NOT NULL, expires TIMESTAMP NOT NULL,
scope VARCHAR(4000), scope VARCHAR(4000),
id_token VARCHAR(1000), id_token VARCHAR(1000),
code_challenge VARCHAR(1000),
code_challenge_method VARCHAR(20),
PRIMARY KEY (authorization_code) PRIMARY KEY (authorization_code)
); );
@@ -95,11 +95,11 @@ class Redis implements AuthorizationCodeInterface,
return $this->getValue($this->config['code_key'] . $code); return $this->getValue($this->config['code_key'] . $code);
} }
public function setAuthorizationCode($authorization_code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) public function setAuthorizationCode($authorization_code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null, $code_challenge = null, $code_challenge_method = null)
{ {
return $this->setValue( return $this->setValue(
$this->config['code_key'] . $authorization_code, $this->config['code_key'] . $authorization_code,
compact('authorization_code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token'), compact('authorization_code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token', 'code_challenge', 'code_challenge_method'),
$expires $expires
); );
} }
@@ -109,7 +109,7 @@ class Bearer implements TokenTypeInterface
} }
$contentType = $request->server('CONTENT_TYPE'); $contentType = $request->server('CONTENT_TYPE');
if (false !== $pos = strpos($contentType, ';')) { if (false !== $pos = strpos((string) $contentType, ';')) {
$contentType = substr($contentType, 0, $pos); $contentType = substr($contentType, 0, $pos);
} }
@@ -133,7 +133,7 @@ class AuthorizeControllerTest extends TestCase
$server->handleAuthorizeRequest($request, $response = new Response(), true); $server->handleAuthorizeRequest($request, $response = new Response(), true);
$this->assertEquals($response->getStatusCode(), 302); $this->assertEquals($response->getStatusCode(), 302);
$this->assertNotContains('error', $response->getHttpHeader('Location')); $this->assertStringNotContainsString('error', $response->getHttpHeader('Location'));
} }
public function testEnforceScope() public function testEnforceScope()
@@ -161,7 +161,7 @@ class AuthorizeControllerTest extends TestCase
$server->handleAuthorizeRequest($request, $response = new Response(), true); $server->handleAuthorizeRequest($request, $response = new Response(), true);
$this->assertEquals($response->getStatusCode(), 302); $this->assertEquals($response->getStatusCode(), 302);
$this->assertNotContains('error', $response->getHttpHeader('Location')); $this->assertStringNotContainsString('error', $response->getHttpHeader('Location'));
} }
public function testInvalidRedirectUri() public function testInvalidRedirectUri()
@@ -227,7 +227,7 @@ class AuthorizeControllerTest extends TestCase
$server->handleAuthorizeRequest($request, $response = new Response(), true); $server->handleAuthorizeRequest($request, $response = new Response(), true);
$this->assertEquals($response->getStatusCode(), 302); $this->assertEquals($response->getStatusCode(), 302);
$this->assertContains('code', $response->getHttpHeader('Location')); $this->assertStringContainsString('code', $response->getHttpHeader('Location'));
} }
public function testRedirectUriWithDifferentQueryAndExactMatchRequired() public function testRedirectUriWithDifferentQueryAndExactMatchRequired()
@@ -263,7 +263,7 @@ class AuthorizeControllerTest extends TestCase
$server->handleAuthorizeRequest($request, $response = new Response(), true); $server->handleAuthorizeRequest($request, $response = new Response(), true);
$this->assertEquals($response->getStatusCode(), 302); $this->assertEquals($response->getStatusCode(), 302);
$this->assertContains('code', $response->getHttpHeader('Location')); $this->assertStringContainsString('code', $response->getHttpHeader('Location'));
} }
public function testMultipleRedirectUris() public function testMultipleRedirectUris()
@@ -278,14 +278,14 @@ class AuthorizeControllerTest extends TestCase
$server->handleAuthorizeRequest($request, $response = new Response(), true); $server->handleAuthorizeRequest($request, $response = new Response(), true);
$this->assertEquals($response->getStatusCode(), 302); $this->assertEquals($response->getStatusCode(), 302);
$this->assertContains('code', $response->getHttpHeader('Location')); $this->assertStringContainsString('code', $response->getHttpHeader('Location'));
// call again with different (but still valid) redirect URI // call again with different (but still valid) redirect URI
$request->query['redirect_uri'] = 'http://morehazards.com'; $request->query['redirect_uri'] = 'http://morehazards.com';
$server->handleAuthorizeRequest($request, $response = new Response(), true); $server->handleAuthorizeRequest($request, $response = new Response(), true);
$this->assertEquals($response->getStatusCode(), 302); $this->assertEquals($response->getStatusCode(), 302);
$this->assertContains('code', $response->getHttpHeader('Location')); $this->assertStringContainsString('code', $response->getHttpHeader('Location'));
} }
/** /**
@@ -303,7 +303,7 @@ class AuthorizeControllerTest extends TestCase
$server->handleAuthorizeRequest($request, $response = new Response(), true); $server->handleAuthorizeRequest($request, $response = new Response(), true);
$this->assertEquals($response->getStatusCode(), 302); $this->assertEquals($response->getStatusCode(), 302);
$this->assertContains('state', $response->getHttpHeader('Location')); $this->assertStringContainsString('state', $response->getHttpHeader('Location'));
$this->assertStringStartsWith('http://brentertainment.com?code=', $response->getHttpHeader('Location')); $this->assertStringStartsWith('http://brentertainment.com?code=', $response->getHttpHeader('Location'));
$parts = parse_url($response->getHttpHeader('Location')); $parts = parse_url($response->getHttpHeader('Location'));
@@ -421,7 +421,7 @@ class AuthorizeControllerTest extends TestCase
$this->assertEquals($response->getStatusCode(), 302); $this->assertEquals($response->getStatusCode(), 302);
$location = $response->getHttpHeader('Location'); $location = $response->getHttpHeader('Location');
$this->assertNotContains('error', $location); $this->assertStringNotContainsString('error', $location);
$parts = parse_url($location); $parts = parse_url($location);
$this->assertFalse(isset($parts['fake'])); $this->assertFalse(isset($parts['fake']));
@@ -9,7 +9,7 @@ class FirebaseJwtTest extends TestCase
{ {
private $privateKey; private $privateKey;
public function setUp() public function setUp(): void
{ {
$this->privateKey = <<<EOD $this->privateKey = <<<EOD
-----BEGIN RSA PRIVATE KEY----- -----BEGIN RSA PRIVATE KEY-----
@@ -9,7 +9,7 @@ class JwtTest extends TestCase
{ {
private $privateKey; private $privateKey;
public function setUp() public function setUp(): void
{ {
$this->privateKey = <<<EOD $this->privateKey = <<<EOD
-----BEGIN RSA PRIVATE KEY----- -----BEGIN RSA PRIVATE KEY-----
@@ -13,7 +13,7 @@ class JwtBearerTest extends TestCase
{ {
private $privateKey; private $privateKey;
public function setUp() public function setUp(): void
{ {
$this->privateKey = <<<EOD $this->privateKey = <<<EOD
-----BEGIN RSA PRIVATE KEY----- -----BEGIN RSA PRIVATE KEY-----
@@ -29,7 +29,7 @@ class CodeIdTokenTest extends TestCase
$this->assertEquals($response->getStatusCode(), 302); $this->assertEquals($response->getStatusCode(), 302);
$location = $response->getHttpHeader('Location'); $location = $response->getHttpHeader('Location');
$this->assertNotContains('error', $location); $this->assertStringNotContainsString('error', $location);
$parts = parse_url($location); $parts = parse_url($location);
$this->assertArrayHasKey('query', $parts); $this->assertArrayHasKey('query', $parts);
@@ -87,7 +87,7 @@ class CodeIdTokenTest extends TestCase
$this->assertEquals($response->getStatusCode(), 302); $this->assertEquals($response->getStatusCode(), 302);
$location = $response->getHttpHeader('Location'); $location = $response->getHttpHeader('Location');
$this->assertNotContains('error', $location); $this->assertStringNotContainsString('error', $location);
$parts = parse_url($location); $parts = parse_url($location);
$this->assertArrayHasKey('query', $parts); $this->assertArrayHasKey('query', $parts);
@@ -133,7 +133,7 @@ class CodeIdTokenTest extends TestCase
$this->assertEquals($response->getStatusCode(), 302); $this->assertEquals($response->getStatusCode(), 302);
$location = $response->getHttpHeader('Location'); $location = $response->getHttpHeader('Location');
$this->assertNotContains('error', $location); $this->assertStringNotContainsString('error', $location);
$parts = parse_url($location); $parts = parse_url($location);
$this->assertArrayHasKey('query', $parts); $this->assertArrayHasKey('query', $parts);
@@ -52,7 +52,7 @@ class IdTokenTest extends TestCase
$this->assertEquals($response->getStatusCode(), 302); $this->assertEquals($response->getStatusCode(), 302);
$location = $response->getHttpHeader('Location'); $location = $response->getHttpHeader('Location');
$this->assertNotContains('error', $location); $this->assertStringNotContainsString('error', $location);
$parts = parse_url($location); $parts = parse_url($location);
$this->assertArrayHasKey('fragment', $parts); $this->assertArrayHasKey('fragment', $parts);
@@ -110,7 +110,7 @@ class IdTokenTest extends TestCase
{ {
$this->assertEquals($response->getStatusCode(), 302); $this->assertEquals($response->getStatusCode(), 302);
$location = $response->getHttpHeader('Location'); $location = $response->getHttpHeader('Location');
$this->assertNotContains('error', $location); $this->assertStringNotContainsString('error', $location);
$parts = parse_url($location); $parts = parse_url($location);
$this->assertArrayHasKey('fragment', $parts); $this->assertArrayHasKey('fragment', $parts);
@@ -31,7 +31,7 @@ class IdTokenTokenTest extends TestCase
$this->assertEquals($response->getStatusCode(), 302); $this->assertEquals($response->getStatusCode(), 302);
$location = $response->getHttpHeader('Location'); $location = $response->getHttpHeader('Location');
$this->assertNotContains('error', $location); $this->assertStringNotContainsString('error', $location);
$parts = parse_url($location); $parts = parse_url($location);
$this->assertArrayHasKey('fragment', $parts); $this->assertArrayHasKey('fragment', $parts);
@@ -14,7 +14,7 @@ class ResponseTest extends TestCase
)); ));
$string = $response->getResponseBody('xml'); $string = $response->getResponseBody('xml');
$this->assertContains('<response><foo>bar</foo><halland>oates</halland></response>', $string); $this->assertStringContainsString('<response><foo>bar</foo><halland>oates</halland></response>', $string);
} }
public function testSetRedirect() public function testSetRedirect()
@@ -162,7 +162,7 @@ class JwtAccessTokenTest extends TestCase
$memoryStorage = Bootstrap::getInstance()->getMemoryStorage(); $memoryStorage = Bootstrap::getInstance()->getMemoryStorage();
$storage = array( $storage = array(
'access_token' => new JwtAccessTokenStorage($memoryStorage), 'access_token' => new JwtAccessTokenStorage($memoryStorage, $memoryStorage),
'client' => $memoryStorage, 'client' => $memoryStorage,
'client_credentials' => $memoryStorage, 'client_credentials' => $memoryStorage,
); );
@@ -6,27 +6,26 @@ use OAuth2\Request\TestRequest;
use OAuth2\ResponseType\AuthorizationCode; use OAuth2\ResponseType\AuthorizationCode;
use OAuth2\Storage\Bootstrap; use OAuth2\Storage\Bootstrap;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Yoast\PHPUnitPolyfills\Polyfills\ExpectPHPException;
class ServerTest extends TestCase class ServerTest extends TestCase
{ {
/** use ExpectPHPException;
* @expectedException LogicException OAuth2\Storage\ClientInterface
**/
public function testGetAuthorizeControllerWithNoClientStorageThrowsException() public function testGetAuthorizeControllerWithNoClientStorageThrowsException()
{ {
$this->expectExceptionMessage('OAuth2\Storage\ClientInterface');
// must set Client Storage // must set Client Storage
$server = new Server(); $server = new Server();
$server->getAuthorizeController(); $server->getAuthorizeController();
} }
/**
* @expectedException LogicException OAuth2\Storage\AccessTokenInterface
**/
public function testGetAuthorizeControllerWithNoAccessTokenStorageThrowsException() public function testGetAuthorizeControllerWithNoAccessTokenStorageThrowsException()
{ {
$this->expectExceptionMessage('OAuth2\Storage\AccessTokenInterface');
// must set AccessToken or AuthorizationCode // must set AccessToken or AuthorizationCode
$server = new Server(); $server = new Server();
$server->addStorage($this->getMock('OAuth2\Storage\ClientInterface')); $server->addStorage($this->createMock('OAuth2\Storage\ClientInterface'));
$server->getAuthorizeController(); $server->getAuthorizeController();
} }
@@ -34,8 +33,8 @@ class ServerTest extends TestCase
{ {
// must set AccessToken or AuthorizationCode // must set AccessToken or AuthorizationCode
$server = new Server(); $server = new Server();
$server->addStorage($this->getMock('OAuth2\Storage\ClientInterface')); $server->addStorage($this->createMock('OAuth2\Storage\ClientInterface'));
$server->addResponseType($this->getMock('OAuth2\ResponseType\AccessTokenInterface')); $server->addResponseType($this->createMock('OAuth2\ResponseType\AccessTokenInterface'));
$this->assertNotNull($server->getAuthorizeController()); $this->assertNotNull($server->getAuthorizeController());
} }
@@ -44,21 +43,19 @@ class ServerTest extends TestCase
{ {
// must set AccessToken or AuthorizationCode // must set AccessToken or AuthorizationCode
$server = new Server(); $server = new Server();
$server->addStorage($this->getMock('OAuth2\Storage\ClientInterface')); $server->addStorage($this->createMock('OAuth2\Storage\ClientInterface'));
$server->addResponseType($this->getMock('OAuth2\ResponseType\AuthorizationCodeInterface')); $server->addResponseType($this->createMock('OAuth2\ResponseType\AuthorizationCodeInterface'));
$this->assertNotNull($server->getAuthorizeController()); $this->assertNotNull($server->getAuthorizeController());
} }
/**
* @expectedException LogicException allow_implicit
**/
public function testGetAuthorizeControllerWithClientStorageAndAccessTokenStorageThrowsException() public function testGetAuthorizeControllerWithClientStorageAndAccessTokenStorageThrowsException()
{ {
// must set AuthorizationCode or AccessToken / implicit // must set AuthorizationCode or AccessToken / implicit
$this->expectExceptionMessage('allow_implicit');
$server = new Server(); $server = new Server();
$server->addStorage($this->getMock('OAuth2\Storage\ClientInterface')); $server->addStorage($this->createMock('OAuth2\Storage\ClientInterface'));
$server->addStorage($this->getMock('OAuth2\Storage\AccessTokenInterface')); $server->addStorage($this->createMock('OAuth2\Storage\AccessTokenInterface'));
$this->assertNotNull($server->getAuthorizeController()); $this->assertNotNull($server->getAuthorizeController());
} }
@@ -67,8 +64,8 @@ class ServerTest extends TestCase
{ {
// must set AuthorizationCode or AccessToken / implicit // must set AuthorizationCode or AccessToken / implicit
$server = new Server(array(), array('allow_implicit' => true)); $server = new Server(array(), array('allow_implicit' => true));
$server->addStorage($this->getMock('OAuth2\Storage\ClientInterface')); $server->addStorage($this->createMock('OAuth2\Storage\ClientInterface'));
$server->addStorage($this->getMock('OAuth2\Storage\AccessTokenInterface')); $server->addStorage($this->createMock('OAuth2\Storage\AccessTokenInterface'));
$this->assertNotNull($server->getAuthorizeController()); $this->assertNotNull($server->getAuthorizeController());
} }
@@ -77,63 +74,55 @@ class ServerTest extends TestCase
{ {
// must set AccessToken or AuthorizationCode // must set AccessToken or AuthorizationCode
$server = new Server(); $server = new Server();
$server->addStorage($this->getMock('OAuth2\Storage\ClientInterface')); $server->addStorage($this->createMock('OAuth2\Storage\ClientInterface'));
$server->addStorage($this->getMock('OAuth2\Storage\AuthorizationCodeInterface')); $server->addStorage($this->createMock('OAuth2\Storage\AuthorizationCodeInterface'));
$this->assertNotNull($server->getAuthorizeController()); $this->assertNotNull($server->getAuthorizeController());
} }
/**
* @expectedException LogicException grant_types
**/
public function testGetTokenControllerWithGrantTypeStorageThrowsException() public function testGetTokenControllerWithGrantTypeStorageThrowsException()
{ {
$this->expectExceptionMessage('grant_types');
$server = new Server(); $server = new Server();
$server->getTokenController(); $server->getTokenController();
} }
/**
* @expectedException LogicException OAuth2\Storage\ClientCredentialsInterface
**/
public function testGetTokenControllerWithNoClientCredentialsStorageThrowsException() public function testGetTokenControllerWithNoClientCredentialsStorageThrowsException()
{ {
$this->expectExceptionMessage('OAuth2\Storage\ClientCredentialsInterface');
$server = new Server(); $server = new Server();
$server->addStorage($this->getMock('OAuth2\Storage\UserCredentialsInterface')); $server->addStorage($this->createMock('OAuth2\Storage\UserCredentialsInterface'));
$server->getTokenController(); $server->getTokenController();
} }
/**
* @expectedException LogicException OAuth2\Storage\AccessTokenInterface
**/
public function testGetTokenControllerWithNoAccessTokenStorageThrowsException() public function testGetTokenControllerWithNoAccessTokenStorageThrowsException()
{ {
$this->expectExceptionMessage('OAuth2\Storage\AccessTokenInterface');
$server = new Server(); $server = new Server();
$server->addStorage($this->getMock('OAuth2\Storage\ClientCredentialsInterface')); $server->addStorage($this->createMock('OAuth2\Storage\ClientCredentialsInterface'));
$server->getTokenController(); $server->getTokenController();
} }
public function testGetTokenControllerWithAccessTokenAndClientCredentialsStorage() public function testGetTokenControllerWithAccessTokenAndClientCredentialsStorage()
{ {
$server = new Server(); $server = new Server();
$server->addStorage($this->getMock('OAuth2\Storage\AccessTokenInterface')); $server->addStorage($this->createMock('OAuth2\Storage\AccessTokenInterface'));
$server->addStorage($this->getMock('OAuth2\Storage\ClientCredentialsInterface')); $server->addStorage($this->createMock('OAuth2\Storage\ClientCredentialsInterface'));
$server->getTokenController(); $server->getTokenController();
} }
public function testGetTokenControllerAccessTokenStorageAndClientCredentialsStorageAndGrantTypes() public function testGetTokenControllerAccessTokenStorageAndClientCredentialsStorageAndGrantTypes()
{ {
$server = new Server(); $server = new Server();
$server->addStorage($this->getMock('OAuth2\Storage\AccessTokenInterface')); $server->addStorage($this->createMock('OAuth2\Storage\AccessTokenInterface'));
$server->addStorage($this->getMock('OAuth2\Storage\ClientCredentialsInterface')); $server->addStorage($this->createMock('OAuth2\Storage\ClientCredentialsInterface'));
$server->addGrantType($this->getMockBuilder('OAuth2\GrantType\AuthorizationCode')->disableOriginalConstructor()->getMock()); $server->addGrantType($this->createMock('OAuth2\GrantType\AuthorizationCode'));
$server->getTokenController(); $server->getTokenController();
} }
/**
* @expectedException LogicException OAuth2\Storage\AccessTokenInterface
**/
public function testGetResourceControllerWithNoAccessTokenStorageThrowsException() public function testGetResourceControllerWithNoAccessTokenStorageThrowsException()
{ {
$this->expectExceptionMessage('OAuth2\Storage\AccessTokenInterface');
$server = new Server(); $server = new Server();
$server->getResourceController(); $server->getResourceController();
} }
@@ -141,41 +130,35 @@ class ServerTest extends TestCase
public function testGetResourceControllerWithAccessTokenStorage() public function testGetResourceControllerWithAccessTokenStorage()
{ {
$server = new Server(); $server = new Server();
$server->addStorage($this->getMock('OAuth2\Storage\AccessTokenInterface')); $server->addStorage($this->createMock('OAuth2\Storage\AccessTokenInterface'));
$server->getResourceController(); $server->getResourceController();
} }
/**
* @expectedException InvalidArgumentException OAuth2\Storage\AccessTokenInterface
**/
public function testAddingStorageWithInvalidClass() public function testAddingStorageWithInvalidClass()
{ {
$this->expectExceptionMessage('OAuth2\Storage\AccessTokenInterface');
$server = new Server(); $server = new Server();
$server->addStorage(new \StdClass()); $server->addStorage(new \StdClass());
} }
/**
* @expectedException InvalidArgumentException access_token
**/
public function testAddingStorageWithInvalidKey() public function testAddingStorageWithInvalidKey()
{ {
$this->expectExceptionMessage('access_token');
$server = new Server(); $server = new Server();
$server->addStorage($this->getMock('OAuth2\Storage\AccessTokenInterface'), 'nonexistant_storage'); $server->addStorage($this->createMock('OAuth2\Storage\AccessTokenInterface'), 'nonexistant_storage');
} }
/**
* @expectedException InvalidArgumentException OAuth2\Storage\AuthorizationCodeInterface
**/
public function testAddingStorageWithInvalidKeyStorageCombination() public function testAddingStorageWithInvalidKeyStorageCombination()
{ {
$this->expectExceptionMessage('OAuth2\Storage\AuthorizationCodeInterface');
$server = new Server(); $server = new Server();
$server->addStorage($this->getMock('OAuth2\Storage\AccessTokenInterface'), 'authorization_code'); $server->addStorage($this->createMock('OAuth2\Storage\AccessTokenInterface'), 'authorization_code');
} }
public function testAddingStorageWithValidKeyOnlySetsThatKey() public function testAddingStorageWithValidKeyOnlySetsThatKey()
{ {
$server = new Server(); $server = new Server();
$server->addStorage($this->getMock('OAuth2\Storage\Memory'), 'access_token'); $server->addStorage($this->createMock('OAuth2\Storage\Memory'), 'access_token');
$reflection = new \ReflectionClass($server); $reflection = new \ReflectionClass($server);
$prop = $reflection->getProperty('storages'); $prop = $reflection->getProperty('storages');
@@ -191,7 +174,7 @@ class ServerTest extends TestCase
public function testAddingClientStorageSetsClientCredentialsStorageByDefault() public function testAddingClientStorageSetsClientCredentialsStorageByDefault()
{ {
$server = new Server(); $server = new Server();
$memory = $this->getMock('OAuth2\Storage\Memory'); $memory = $this->createMock('OAuth2\Storage\Memory');
$server->addStorage($memory, 'client'); $server->addStorage($memory, 'client');
$client_credentials = $server->getStorage('client_credentials'); $client_credentials = $server->getStorage('client_credentials');
@@ -202,7 +185,7 @@ class ServerTest extends TestCase
public function testAddStorageWithNullValue() public function testAddStorageWithNullValue()
{ {
$memory = $this->getMock('OAuth2\Storage\Memory'); $memory = $this->createMock('OAuth2\Storage\Memory');
$server = new Server($memory); $server = new Server($memory);
$server->addStorage(null, 'refresh_token'); $server->addStorage(null, 'refresh_token');
@@ -218,7 +201,7 @@ class ServerTest extends TestCase
public function testNewServerWithNullStorageValue() public function testNewServerWithNullStorageValue()
{ {
$memory = $this->getMock('OAuth2\Storage\Memory'); $memory = $this->createMock('OAuth2\Storage\Memory');
$server = new Server(array( $server = new Server(array(
'client_credentials' => $memory, 'client_credentials' => $memory,
'refresh_token' => null, 'refresh_token' => null,
@@ -237,7 +220,7 @@ class ServerTest extends TestCase
public function testAddingClientCredentialsStorageSetsClientStorageByDefault() public function testAddingClientCredentialsStorageSetsClientStorageByDefault()
{ {
$server = new Server(); $server = new Server();
$memory = $this->getMock('OAuth2\Storage\Memory'); $memory = $this->createMock('OAuth2\Storage\Memory');
$server->addStorage($memory, 'client_credentials'); $server->addStorage($memory, 'client_credentials');
$client = $server->getStorage('client'); $client = $server->getStorage('client');
@@ -249,10 +232,9 @@ class ServerTest extends TestCase
public function testSettingClientStorageByDefaultDoesNotOverrideSetStorage() public function testSettingClientStorageByDefaultDoesNotOverrideSetStorage()
{ {
$server = new Server(); $server = new Server();
$pdo = $this->getMockBuilder('OAuth2\Storage\Pdo') $pdo = $this->createMock('OAuth2\Storage\Pdo');
->disableOriginalConstructor()->getMock();
$memory = $this->getMock('OAuth2\Storage\Memory'); $memory = $this->createMock('OAuth2\Storage\Memory');
$server->addStorage($pdo, 'client'); $server->addStorage($pdo, 'client');
$server->addStorage($memory, 'client_credentials'); $server->addStorage($memory, 'client_credentials');
@@ -266,7 +248,7 @@ class ServerTest extends TestCase
public function testAddingResponseType() public function testAddingResponseType()
{ {
$storage = $this->getMock('OAuth2\Storage\Memory'); $storage = $this->createMock('OAuth2\Storage\Memory');
$storage $storage
->expects($this->any()) ->expects($this->any())
->method('getClientDetails') ->method('getClientDetails')
@@ -323,7 +305,7 @@ class ServerTest extends TestCase
'code' => 'testcode', 'code' => 'testcode',
)); ));
// verify the mock clientAssertionType was called as expected // verify the mock clientAssertionType was called as expected
$clientAssertionType = $this->getMock('OAuth2\ClientAssertionType\ClientAssertionTypeInterface', array('validateRequest', 'getClientId')); $clientAssertionType = $this->createMock('OAuth2\ClientAssertionType\ClientAssertionTypeInterface');
$clientAssertionType $clientAssertionType
->expects($this->once()) ->expects($this->once())
->method('validateRequest') ->method('validateRequest')
@@ -420,98 +402,84 @@ class ServerTest extends TestCase
$this->assertFalse($used_token, 'the refresh token used is no longer valid'); $this->assertFalse($used_token, 'the refresh token used is no longer valid');
} }
/**
* @expectedException InvalidArgumentException OAuth2\ResponseType\AuthorizationCodeInterface
**/
public function testAddingUnknownResponseTypeThrowsException() public function testAddingUnknownResponseTypeThrowsException()
{ {
$this->expectExceptionMessage('OAuth2\ResponseType\AuthorizationCodeInterface');
$server = new Server(); $server = new Server();
$server->addResponseType($this->getMock('OAuth2\ResponseType\ResponseTypeInterface')); $server->addResponseType($this->createMock('OAuth2\ResponseType\ResponseTypeInterface'));
} }
/**
* @expectedException LogicException OAuth2\Storage\PublicKeyInterface
**/
public function testUsingJwtAccessTokensWithoutPublicKeyStorageThrowsException() public function testUsingJwtAccessTokensWithoutPublicKeyStorageThrowsException()
{ {
$this->expectExceptionMessage('OAuth2\Storage\PublicKeyInterface');
$server = new Server(array(), array('use_jwt_access_tokens' => true)); $server = new Server(array(), array('use_jwt_access_tokens' => true));
$server->addGrantType($this->getMock('OAuth2\GrantType\GrantTypeInterface')); $server->addGrantType($this->createMock('OAuth2\GrantType\GrantTypeInterface'));
$server->addStorage($this->getMock('OAuth2\Storage\ClientCredentialsInterface')); $server->addStorage($this->createMock('OAuth2\Storage\ClientCredentialsInterface'));
$server->addStorage($this->getMock('OAuth2\Storage\ClientCredentialsInterface')); $server->addStorage($this->createMock('OAuth2\Storage\ClientCredentialsInterface'));
$server->getTokenController(); $server->getTokenController();
} }
public function testUsingJustJwtAccessTokenStorageWithResourceControllerIsOkay() public function testUsingJustJwtAccessTokenStorageWithResourceControllerIsOkay()
{ {
$pubkey = $this->getMock('OAuth2\Storage\PublicKeyInterface'); $pubkey = $this->createMock('OAuth2\Storage\PublicKeyInterface');
$server = new Server(array($pubkey), array('use_jwt_access_tokens' => true)); $server = new Server(array($pubkey), array('use_jwt_access_tokens' => true));
$this->assertNotNull($server->getResourceController()); $this->assertNotNull($server->getResourceController());
$this->assertInstanceOf('OAuth2\Storage\PublicKeyInterface', $server->getStorage('public_key')); $this->assertInstanceOf('OAuth2\Storage\PublicKeyInterface', $server->getStorage('public_key'));
} }
/**
* @expectedException LogicException OAuth2\Storage\ClientInterface
**/
public function testUsingJustJwtAccessTokenStorageWithAuthorizeControllerThrowsException() public function testUsingJustJwtAccessTokenStorageWithAuthorizeControllerThrowsException()
{ {
$pubkey = $this->getMock('OAuth2\Storage\PublicKeyInterface'); $this->expectExceptionMessage('OAuth2\Storage\ClientInterface');
$pubkey = $this->createMock('OAuth2\Storage\PublicKeyInterface');
$server = new Server(array($pubkey), array('use_jwt_access_tokens' => true)); $server = new Server(array($pubkey), array('use_jwt_access_tokens' => true));
$this->assertNotNull($server->getAuthorizeController()); $this->assertNotNull($server->getAuthorizeController());
} }
/**
* @expectedException LogicException grant_types
**/
public function testUsingJustJwtAccessTokenStorageWithTokenControllerThrowsException() public function testUsingJustJwtAccessTokenStorageWithTokenControllerThrowsException()
{ {
$pubkey = $this->getMock('OAuth2\Storage\PublicKeyInterface'); $this->expectExceptionMessage('grant_types');
$pubkey = $this->createMock('OAuth2\Storage\PublicKeyInterface');
$server = new Server(array($pubkey), array('use_jwt_access_tokens' => true)); $server = new Server(array($pubkey), array('use_jwt_access_tokens' => true));
$server->getTokenController(); $server->getTokenController();
} }
public function testUsingJwtAccessTokenAndClientStorageWithAuthorizeControllerIsOkay() public function testUsingJwtAccessTokenAndClientStorageWithAuthorizeControllerIsOkay()
{ {
$pubkey = $this->getMock('OAuth2\Storage\PublicKeyInterface'); $pubkey = $this->createMock('OAuth2\Storage\PublicKeyInterface');
$client = $this->getMock('OAuth2\Storage\ClientInterface'); $client = $this->createMock('OAuth2\Storage\ClientInterface');
$server = new Server(array($pubkey, $client), array('use_jwt_access_tokens' => true, 'allow_implicit' => true)); $server = new Server(array($pubkey, $client), array('use_jwt_access_tokens' => true, 'allow_implicit' => true));
$this->assertNotNull($server->getAuthorizeController()); $this->assertNotNull($server->getAuthorizeController());
$this->assertInstanceOf('OAuth2\ResponseType\JwtAccessToken', $server->getResponseType('token')); $this->assertInstanceOf('OAuth2\ResponseType\JwtAccessToken', $server->getResponseType('token'));
} }
/**
* @expectedException LogicException UserClaims
**/
public function testUsingOpenIDConnectWithoutUserClaimsThrowsException() public function testUsingOpenIDConnectWithoutUserClaimsThrowsException()
{ {
$client = $this->getMock('OAuth2\Storage\ClientInterface'); $this->expectExceptionMessage('UserClaims');
$client = $this->createMock('OAuth2\Storage\ClientInterface');
$server = new Server($client, array('use_openid_connect' => true)); $server = new Server($client, array('use_openid_connect' => true));
$server->getAuthorizeController(); $server->getAuthorizeController();
} }
/**
* @expectedException LogicException PublicKeyInterface
**/
public function testUsingOpenIDConnectWithoutPublicKeyThrowsException() public function testUsingOpenIDConnectWithoutPublicKeyThrowsException()
{ {
$client = $this->getMock('OAuth2\Storage\ClientInterface'); $this->expectExceptionMessage('PublicKeyInterface');
$userclaims = $this->getMock('OAuth2\OPenID\Storage\UserClaimsInterface'); $client = $this->createMock('OAuth2\Storage\ClientInterface');
$userclaims = $this->createMock('OAuth2\OPenID\Storage\UserClaimsInterface');
$server = new Server(array($client, $userclaims), array('use_openid_connect' => true)); $server = new Server(array($client, $userclaims), array('use_openid_connect' => true));
$server->getAuthorizeController(); $server->getAuthorizeController();
} }
/**
* @expectedException LogicException issuer
**/
public function testUsingOpenIDConnectWithoutIssuerThrowsException() public function testUsingOpenIDConnectWithoutIssuerThrowsException()
{ {
$client = $this->getMock('OAuth2\Storage\ClientInterface'); $this->expectExceptionMessage('issuer');
$userclaims = $this->getMock('OAuth2\OpenID\Storage\UserClaimsInterface'); $client = $this->createMock('OAuth2\Storage\ClientInterface');
$pubkey = $this->getMock('OAuth2\Storage\PublicKeyInterface'); $userclaims = $this->createMock('OAuth2\OpenID\Storage\UserClaimsInterface');
$pubkey = $this->createMock('OAuth2\Storage\PublicKeyInterface');
$server = new Server(array($client, $userclaims, $pubkey), array('use_openid_connect' => true)); $server = new Server(array($client, $userclaims, $pubkey), array('use_openid_connect' => true));
$server->getAuthorizeController(); $server->getAuthorizeController();
@@ -519,9 +487,9 @@ class ServerTest extends TestCase
public function testUsingOpenIDConnectWithIssuerPublicKeyAndUserClaimsIsOkay() public function testUsingOpenIDConnectWithIssuerPublicKeyAndUserClaimsIsOkay()
{ {
$client = $this->getMock('OAuth2\Storage\ClientInterface'); $client = $this->createMock('OAuth2\Storage\ClientInterface');
$userclaims = $this->getMock('OAuth2\OpenID\Storage\UserClaimsInterface'); $userclaims = $this->createMock('OAuth2\OpenID\Storage\UserClaimsInterface');
$pubkey = $this->getMock('OAuth2\Storage\PublicKeyInterface'); $pubkey = $this->createMock('OAuth2\Storage\PublicKeyInterface');
$server = new Server(array($client, $userclaims, $pubkey), array( $server = new Server(array($client, $userclaims, $pubkey), array(
'use_openid_connect' => true, 'use_openid_connect' => true,
'issuer' => 'someguy', 'issuer' => 'someguy',
@@ -533,14 +501,12 @@ class ServerTest extends TestCase
$this->assertNull($server->getResponseType('id_token token')); $this->assertNull($server->getResponseType('id_token token'));
} }
/**
* @expectedException LogicException OAuth2\ResponseType\AccessTokenInterface
**/
public function testUsingOpenIDConnectWithAllowImplicitWithoutTokenStorageThrowsException() public function testUsingOpenIDConnectWithAllowImplicitWithoutTokenStorageThrowsException()
{ {
$client = $this->getMock('OAuth2\Storage\ClientInterface'); $this->expectErrorMessage('OAuth2\ResponseType\AccessTokenInterface');
$userclaims = $this->getMock('OAuth2\OpenID\Storage\UserClaimsInterface'); $client = $this->createMock('OAuth2\Storage\ClientInterface');
$pubkey = $this->getMock('OAuth2\Storage\PublicKeyInterface'); $userclaims = $this->createMock('OAuth2\OpenID\Storage\UserClaimsInterface');
$pubkey = $this->createMock('OAuth2\Storage\PublicKeyInterface');
$server = new Server(array($client, $userclaims, $pubkey), array( $server = new Server(array($client, $userclaims, $pubkey), array(
'use_openid_connect' => true, 'use_openid_connect' => true,
'issuer' => 'someguy', 'issuer' => 'someguy',
@@ -552,9 +518,9 @@ class ServerTest extends TestCase
public function testUsingOpenIDConnectWithAllowImplicitAndUseJwtAccessTokensIsOkay() public function testUsingOpenIDConnectWithAllowImplicitAndUseJwtAccessTokensIsOkay()
{ {
$client = $this->getMock('OAuth2\Storage\ClientInterface'); $client = $this->createMock('OAuth2\Storage\ClientInterface');
$userclaims = $this->getMock('OAuth2\OpenID\Storage\UserClaimsInterface'); $userclaims = $this->createMock('OAuth2\OpenID\Storage\UserClaimsInterface');
$pubkey = $this->getMock('OAuth2\Storage\PublicKeyInterface'); $pubkey = $this->createMock('OAuth2\Storage\PublicKeyInterface');
$server = new Server(array($client, $userclaims, $pubkey), array( $server = new Server(array($client, $userclaims, $pubkey), array(
'use_openid_connect' => true, 'use_openid_connect' => true,
'issuer' => 'someguy', 'issuer' => 'someguy',
@@ -570,10 +536,10 @@ class ServerTest extends TestCase
public function testUsingOpenIDConnectWithAllowImplicitAndAccessTokenStorageIsOkay() public function testUsingOpenIDConnectWithAllowImplicitAndAccessTokenStorageIsOkay()
{ {
$client = $this->getMock('OAuth2\Storage\ClientInterface'); $client = $this->createMock('OAuth2\Storage\ClientInterface');
$userclaims = $this->getMock('OAuth2\OpenID\Storage\UserClaimsInterface'); $userclaims = $this->createMock('OAuth2\OpenID\Storage\UserClaimsInterface');
$pubkey = $this->getMock('OAuth2\Storage\PublicKeyInterface'); $pubkey = $this->createMock('OAuth2\Storage\PublicKeyInterface');
$token = $this->getMock('OAuth2\Storage\AccessTokenInterface'); $token = $this->createMock('OAuth2\Storage\AccessTokenInterface');
$server = new Server(array($client, $userclaims, $pubkey, $token), array( $server = new Server(array($client, $userclaims, $pubkey, $token), array(
'use_openid_connect' => true, 'use_openid_connect' => true,
'issuer' => 'someguy', 'issuer' => 'someguy',
@@ -588,17 +554,17 @@ class ServerTest extends TestCase
public function testUsingOpenIDConnectWithAllowImplicitAndAccessTokenResponseTypeIsOkay() public function testUsingOpenIDConnectWithAllowImplicitAndAccessTokenResponseTypeIsOkay()
{ {
$client = $this->getMock('OAuth2\Storage\ClientInterface'); $client = $this->createMock('OAuth2\Storage\ClientInterface');
$userclaims = $this->getMock('OAuth2\OpenID\Storage\UserClaimsInterface'); $userclaims = $this->createMock('OAuth2\OpenID\Storage\UserClaimsInterface');
$pubkey = $this->getMock('OAuth2\Storage\PublicKeyInterface'); $pubkey = $this->createMock('OAuth2\Storage\PublicKeyInterface');
// $token = $this->getMock('OAuth2\Storage\AccessTokenInterface'); // $token = $this->createMock('OAuth2\Storage\AccessTokenInterface');
$server = new Server(array($client, $userclaims, $pubkey), array( $server = new Server(array($client, $userclaims, $pubkey), array(
'use_openid_connect' => true, 'use_openid_connect' => true,
'issuer' => 'someguy', 'issuer' => 'someguy',
'allow_implicit' => true, 'allow_implicit' => true,
)); ));
$token = $this->getMock('OAuth2\ResponseType\AccessTokenInterface'); $token = $this->createMock('OAuth2\ResponseType\AccessTokenInterface');
$server->addResponseType($token, 'token'); $server->addResponseType($token, 'token');
$server->getAuthorizeController(); $server->getAuthorizeController();
@@ -607,17 +573,15 @@ class ServerTest extends TestCase
$this->assertInstanceOf('OAuth2\OpenID\ResponseType\IdTokenTokenInterface', $server->getResponseType('id_token token')); $this->assertInstanceOf('OAuth2\OpenID\ResponseType\IdTokenTokenInterface', $server->getResponseType('id_token token'));
} }
/**
* @expectedException LogicException OAuth2\OpenID\Storage\AuthorizationCodeInterface
**/
public function testUsingOpenIDConnectWithAuthorizationCodeStorageThrowsException() public function testUsingOpenIDConnectWithAuthorizationCodeStorageThrowsException()
{ {
$client = $this->getMock('OAuth2\Storage\ClientCredentialsInterface'); $client = $this->createMock('OAuth2\Storage\ClientCredentialsInterface');
$userclaims = $this->getMock('OAuth2\OpenID\Storage\UserClaimsInterface'); $userclaims = $this->createMock('OAuth2\OpenID\Storage\UserClaimsInterface');
$pubkey = $this->getMock('OAuth2\Storage\PublicKeyInterface'); $pubkey = $this->createMock('OAuth2\Storage\PublicKeyInterface');
$token = $this->getMock('OAuth2\Storage\AccessTokenInterface'); $token = $this->createMock('OAuth2\Storage\AccessTokenInterface');
$authcode = $this->getMock('OAuth2\Storage\AuthorizationCodeInterface'); $authcode = $this->createMock('OAuth2\Storage\AuthorizationCodeInterface');
$this->expectErrorMessage('OAuth2\OpenID\Storage\AuthorizationCodeInterface');
$server = new Server(array($client, $userclaims, $pubkey, $token, $authcode), array( $server = new Server(array($client, $userclaims, $pubkey, $token, $authcode), array(
'use_openid_connect' => true, 'use_openid_connect' => true,
'issuer' => 'someguy' 'issuer' => 'someguy'
@@ -630,11 +594,11 @@ class ServerTest extends TestCase
public function testUsingOpenIDConnectWithOpenIDAuthorizationCodeStorageCreatesOpenIDAuthorizationCodeGrantType() public function testUsingOpenIDConnectWithOpenIDAuthorizationCodeStorageCreatesOpenIDAuthorizationCodeGrantType()
{ {
$client = $this->getMock('OAuth2\Storage\ClientCredentialsInterface'); $client = $this->createMock('OAuth2\Storage\ClientCredentialsInterface');
$userclaims = $this->getMock('OAuth2\OpenID\Storage\UserClaimsInterface'); $userclaims = $this->createMock('OAuth2\OpenID\Storage\UserClaimsInterface');
$pubkey = $this->getMock('OAuth2\Storage\PublicKeyInterface'); $pubkey = $this->createMock('OAuth2\Storage\PublicKeyInterface');
$token = $this->getMock('OAuth2\Storage\AccessTokenInterface'); $token = $this->createMock('OAuth2\Storage\AccessTokenInterface');
$authcode = $this->getMock('OAuth2\OpenID\Storage\AuthorizationCodeInterface'); $authcode = $this->createMock('OAuth2\OpenID\Storage\AuthorizationCodeInterface');
$server = new Server(array($client, $userclaims, $pubkey, $token, $authcode), array( $server = new Server(array($client, $userclaims, $pubkey, $token, $authcode), array(
'use_openid_connect' => true, 'use_openid_connect' => true,
@@ -648,7 +612,7 @@ class ServerTest extends TestCase
public function testMultipleValuedResponseTypeOrderDoesntMatter() public function testMultipleValuedResponseTypeOrderDoesntMatter()
{ {
$responseType = $this->getMock('OAuth2\OpenID\ResponseType\IdTokenTokenInterface'); $responseType = $this->createMock('OAuth2\OpenID\ResponseType\IdTokenTokenInterface');
$server = new Server(array(), array(), array(), array( $server = new Server(array(), array(), array(), array(
'token id_token' => $responseType, 'token id_token' => $responseType,
)); ));
@@ -659,7 +623,7 @@ class ServerTest extends TestCase
public function testAddGrantTypeWithoutKey() public function testAddGrantTypeWithoutKey()
{ {
$server = new Server(); $server = new Server();
$server->addGrantType(new \OAuth2\GrantType\AuthorizationCode($this->getMock('OAuth2\Storage\AuthorizationCodeInterface'))); $server->addGrantType(new \OAuth2\GrantType\AuthorizationCode($this->createMock('OAuth2\Storage\AuthorizationCodeInterface')));
$grantTypes = $server->getGrantTypes(); $grantTypes = $server->getGrantTypes();
$this->assertEquals('authorization_code', key($grantTypes)); $this->assertEquals('authorization_code', key($grantTypes));
@@ -668,7 +632,7 @@ class ServerTest extends TestCase
public function testAddGrantTypeWithKey() public function testAddGrantTypeWithKey()
{ {
$server = new Server(); $server = new Server();
$server->addGrantType(new \OAuth2\GrantType\AuthorizationCode($this->getMock('OAuth2\Storage\AuthorizationCodeInterface')), 'ac'); $server->addGrantType(new \OAuth2\GrantType\AuthorizationCode($this->createMock('OAuth2\Storage\AuthorizationCodeInterface')), 'ac');
$grantTypes = $server->getGrantTypes(); $grantTypes = $server->getGrantTypes();
$this->assertEquals('ac', key($grantTypes)); $this->assertEquals('ac', key($grantTypes));
@@ -677,7 +641,7 @@ class ServerTest extends TestCase
public function testAddGrantTypeWithKeyNotString() public function testAddGrantTypeWithKeyNotString()
{ {
$server = new Server(); $server = new Server();
$server->addGrantType(new \OAuth2\GrantType\AuthorizationCode($this->getMock('OAuth2\Storage\AuthorizationCodeInterface')), 42); $server->addGrantType(new \OAuth2\GrantType\AuthorizationCode($this->createMock('OAuth2\Storage\AuthorizationCodeInterface')), 42);
$grantTypes = $server->getGrantTypes(); $grantTypes = $server->getGrantTypes();
$this->assertEquals('authorization_code', key($grantTypes)); $this->assertEquals('authorization_code', key($grantTypes));
@@ -2,8 +2,12 @@
namespace OAuth2\Storage; namespace OAuth2\Storage;
use Yoast\PHPUnitPolyfills\Polyfills\ExpectPHPException;
class PdoTest extends BaseTest class PdoTest extends BaseTest
{ {
use ExpectPHPException;
public function testCreatePdoStorageUsingPdoClass() public function testCreatePdoStorageUsingPdoClass()
{ {
$dsn = sprintf('sqlite:%s', Bootstrap::getInstance()->getSqliteDir()); $dsn = sprintf('sqlite:%s', Bootstrap::getInstance()->getSqliteDir());
@@ -30,11 +34,9 @@ class PdoTest extends BaseTest
$this->assertNotNull($storage->getClientDetails('oauth_test_client')); $this->assertNotNull($storage->getClientDetails('oauth_test_client'));
} }
/**
* @expectedException InvalidArgumentException dsn
*/
public function testCreatePdoStorageWithoutDSNThrowsException() public function testCreatePdoStorageWithoutDSNThrowsException()
{ {
$this->expectErrorMessage('dsn');
$config = array('username' => 'brent', 'password' => 'brentisaballer'); $config = array('username' => 'brent', 'password' => 'brentisaballer');
$storage = new Pdo($config); $storage = new Pdo($config);
} }
@@ -35,6 +35,17 @@ class BearerTest extends TestCase
$this->assertEquals($response->getParameter('error_description'), 'The content type for POST requests must be "application/x-www-form-urlencoded"'); $this->assertEquals($response->getParameter('error_description'), 'The content type for POST requests must be "application/x-www-form-urlencoded"');
} }
public function testMissingContentTypeExpectsToBeCorrectContent()
{
$bearer = new Bearer();
$request = TestRequest::createPost(array(
'access_token' => 'ThisIsMyAccessToken'
));
$param = $bearer->getAccessTokenParameter($request, $response = new Response());
$this->assertEquals($param, 'ThisIsMyAccessToken');
}
public function testValidRequestUsingAuthorizationHeader() public function testValidRequestUsingAuthorizationHeader()
{ {
$bearer = new Bearer(); $bearer = new Bearer();
@@ -1,15 +0,0 @@
<?php
require_once(dirname(__FILE__).'/../src/OAuth2/Autoloader.php');
OAuth2\Autoloader::register();
// register test classes
OAuth2\Autoloader::register(dirname(__FILE__).'/lib');
// register vendors if possible
if (file_exists(__DIR__.'/../vendor/autoload.php')) {
require_once(__DIR__.'/../vendor/autoload.php');
}
// remove the dynamoDB database that was created for this build
OAuth2\Storage\Bootstrap::getInstance()->cleanupTravisDynamoDb();
@@ -68,7 +68,7 @@ class Bootstrap
public function getPostgresDriver() public function getPostgresDriver()
{ {
try { try {
$pdo = new \PDO('pgsql:host=localhost;dbname=oauth2_server_php', 'postgres'); $pdo = new \PDO('pgsql:host=localhost;dbname=oauth2_server_php', 'postgres', 'postgres');
return $pdo; return $pdo;
} catch (\PDOException $e) { } catch (\PDOException $e) {
@@ -118,7 +118,7 @@ class Bootstrap
if (!$this->mysql) { if (!$this->mysql) {
$pdo = null; $pdo = null;
try { try {
$pdo = new \PDO('mysql:host=localhost;', 'root'); $pdo = new \PDO('mysql:host=localhost;', 'root', 'root');
} catch (\PDOException $e) { } catch (\PDOException $e) {
$this->mysql = new NullStorage('MySQL', 'Unable to connect to MySQL on root@localhost'); $this->mysql = new NullStorage('MySQL', 'Unable to connect to MySQL on root@localhost');
} }
@@ -352,11 +352,11 @@ class Bootstrap
private function createPostgresDb() private function createPostgresDb()
{ {
if (!`psql postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname='postgres'"`) { if (!`PGPASSWORD=postgres psql postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname='postgres'" -h localhost -U postgres`) {
`createuser -s -r postgres`; `PGPASSWORD=postgres createuser -s -r postgres -h localhost -U postgres`;
} }
`createdb -O postgres oauth2_server_php`; `PGPASSWORD=postgres createdb -O postgres oauth2_server_php -h localhost -U postgres`;
} }
private function populatePostgresDb(\PDO $pdo) private function populatePostgresDb(\PDO $pdo)
@@ -366,8 +366,8 @@ class Bootstrap
private function removePostgresDb() private function removePostgresDb()
{ {
if (trim(`psql -l | grep oauth2_server_php | wc -l`)) { if (trim(`PGPASSWORD=postgres psql -l -h localhost -U postgres | grep oauth2_server_php | wc -l`)) {
`dropdb oauth2_server_php`; `PGPASSWORD=postgres dropdb oauth2_server_php -h localhost -U postgres`;
} }
} }
@@ -945,21 +945,6 @@ class Bootstrap
)); ));
} }
public function cleanupTravisDynamoDb($prefix = null)
{
if (is_null($prefix)) {
// skip this when not applicable
if (!$this->getEnvVar('TRAVIS') || self::DYNAMODB_PHP_VERSION != $this->getEnvVar('TRAVIS_PHP_VERSION')) {
return;
}
$prefix = sprintf('build_%s_', $this->getEnvVar('TRAVIS_JOB_NUMBER'));
}
$client = $this->getDynamoDbClient();
$this->deleteDynamoDb($client, $prefix);
}
private function getEnvVar($var, $default = null) private function getEnvVar($var, $default = null)
{ {
return isset($_SERVER[$var]) ? $_SERVER[$var] : (getenv($var) ?: $default); return isset($_SERVER[$var]) ? $_SERVER[$var] : (getenv($var) ?: $default);
@@ -4,7 +4,7 @@ Types to use Carbon in Doctrine
## Documentation ## Documentation
[Check how to use in the official Carbon documentation](https://carbon.nesbot.com/symfony/) [Check how to use in the official Carbon documentation](https://carbon.nesbot.com/guide/getting-started/symfony.html)
This package is an externalization of [src/Carbon/Doctrine](https://github.com/briannesbitt/Carbon/tree/2.71.0/src/Carbon/Doctrine) This package is an externalization of [src/Carbon/Doctrine](https://github.com/briannesbitt/Carbon/tree/2.71.0/src/Carbon/Doctrine)
from `nestbot/carbon` package. from `nestbot/carbon` package.
@@ -32,5 +32,10 @@
"email": "kylekatarnls@gmail.com" "email": "kylekatarnls@gmail.com"
} }
], ],
"minimum-stability": "dev" "minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-main": "3.x-dev"
}
}
} }
@@ -4,13 +4,14 @@ declare(strict_types=1);
namespace Carbon\Doctrine; namespace Carbon\Doctrine;
use Carbon\CarbonInterface;
use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\AbstractPlatform;
interface CarbonDoctrineType interface CarbonDoctrineType
{ {
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform); public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string;
public function convertToPHPValue(mixed $value, AbstractPlatform $platform); public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?CarbonInterface;
public function convertToDatabaseValue($value, AbstractPlatform $platform); public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): ?string;
} }
@@ -62,7 +62,7 @@ trait CarbonTypeConverter
/** /**
* @SuppressWarnings(PHPMD.UnusedFormalParameter) * @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/ */
public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): ?string
{ {
if ($value === null) { if ($value === null) {
return $value; return $value;
@@ -79,11 +79,11 @@ trait CarbonTypeConverter
); );
} }
private function doConvertToPHPValue(mixed $value) private function doConvertToPHPValue(mixed $value): ?CarbonInterface
{ {
$class = $this->getCarbonClassName(); $class = $this->getCarbonClassName();
if ($value === null || is_a($value, $class)) { if ($value === null || (is_object($value) && is_a($value, $class))) {
return $value; return $value;
} }
+19 -1
View File
@@ -26,6 +26,12 @@ use Composer\Semver\VersionParser;
*/ */
class InstalledVersions class InstalledVersions
{ {
/**
* @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to
* @internal
*/
private static $selfDir = null;
/** /**
* @var mixed[]|null * @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, 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
@@ -322,6 +328,18 @@ class InstalledVersions
self::$installedIsLocalDir = false; self::$installedIsLocalDir = false;
} }
/**
* @return string
*/
private static function getSelfDir()
{
if (self::$selfDir === null) {
self::$selfDir = strtr(__DIR__, '\\', '/');
}
return self::$selfDir;
}
/** /**
* @return array[] * @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, 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[]}>}>
@@ -336,7 +354,7 @@ class InstalledVersions
$copiedLocalDir = false; $copiedLocalDir = false;
if (self::$canGetVendors) { if (self::$canGetVendors) {
$selfDir = strtr(__DIR__, '\\', '/'); $selfDir = self::getSelfDir();
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
$vendorDir = strtr($vendorDir, '\\', '/'); $vendorDir = strtr($vendorDir, '\\', '/');
if (isset(self::$installedByVendor[$vendorDir])) { if (isset(self::$installedByVendor[$vendorDir])) {
+35 -2
View File
@@ -6,12 +6,45 @@ $vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir); $baseDir = dirname($vendorDir);
return array( return array(
'AllowDynamicProperties' => $vendorDir . '/symfony/polyfill-php82/Resources/stubs/AllowDynamicProperties.php',
'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', 'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
'CURLStringFile' => $vendorDir . '/symfony/polyfill-php81/Resources/stubs/CURLStringFile.php',
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
'DateError' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateError.php',
'DateException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateException.php',
'DateInvalidOperationException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateInvalidOperationException.php',
'DateInvalidTimeZoneException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateInvalidTimeZoneException.php',
'DateMalformedIntervalStringException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateMalformedIntervalStringException.php',
'DateMalformedPeriodStringException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateMalformedPeriodStringException.php',
'DateMalformedStringException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateMalformedStringException.php',
'DateObjectError' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateObjectError.php',
'DateRangeError' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateRangeError.php',
'DelayedTargetValidation' => $vendorDir . '/symfony/polyfill-php85/Resources/stubs/DelayedTargetValidation.php',
'Deprecated' => $vendorDir . '/symfony/polyfill-php84/Resources/stubs/Deprecated.php',
'Filter\\FilterException' => $vendorDir . '/symfony/polyfill-php85/Resources/stubs/Filter/FilterException.php',
'Filter\\FilterFailedException' => $vendorDir . '/symfony/polyfill-php85/Resources/stubs/Filter/FilterFailedException.php',
'NoDiscard' => $vendorDir . '/symfony/polyfill-php85/Resources/stubs/NoDiscard.php',
'Override' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/Override.php',
'Pdo\\Dblib' => $vendorDir . '/symfony/polyfill-php84/Resources/stubs/Pdo/Dblib.php',
'Pdo\\Firebird' => $vendorDir . '/symfony/polyfill-php84/Resources/stubs/Pdo/Firebird.php',
'Pdo\\Mysql' => $vendorDir . '/symfony/polyfill-php84/Resources/stubs/Pdo/Mysql.php',
'Pdo\\Odbc' => $vendorDir . '/symfony/polyfill-php84/Resources/stubs/Pdo/Odbc.php',
'Pdo\\Pgsql' => $vendorDir . '/symfony/polyfill-php84/Resources/stubs/Pdo/Pgsql.php',
'Pdo\\Sqlite' => $vendorDir . '/symfony/polyfill-php84/Resources/stubs/Pdo/Sqlite.php',
'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php', 'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
'ReturnTypeWillChange' => $vendorDir . '/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php', 'Random\\BrokenRandomEngineError' => $vendorDir . '/symfony/polyfill-php82/Resources/stubs/Random/BrokenRandomEngineError.php',
'Random\\CryptoSafeEngine' => $vendorDir . '/symfony/polyfill-php82/Resources/stubs/Random/CryptoSafeEngine.php',
'Random\\Engine' => $vendorDir . '/symfony/polyfill-php82/Resources/stubs/Random/Engine.php',
'Random\\Engine\\Secure' => $vendorDir . '/symfony/polyfill-php82/Resources/stubs/Random/Engine/Secure.php',
'Random\\RandomError' => $vendorDir . '/symfony/polyfill-php82/Resources/stubs/Random/RandomError.php',
'Random\\RandomException' => $vendorDir . '/symfony/polyfill-php82/Resources/stubs/Random/RandomException.php',
'ReflectionConstant' => $vendorDir . '/symfony/polyfill-php84/Resources/stubs/ReflectionConstant.php',
'RoundingMode' => $vendorDir . '/symfony/polyfill-php84/Resources/stubs/RoundingMode.php',
'SQLite3Exception' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/SQLite3Exception.php',
'SensitiveParameter' => $vendorDir . '/symfony/polyfill-php82/Resources/stubs/SensitiveParameter.php',
'SensitiveParameterValue' => $vendorDir . '/symfony/polyfill-php82/Resources/stubs/SensitiveParameterValue.php',
'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', 'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
'Time\\Duration' => $vendorDir . '/symfony/polyfill-time/Resources/stubs/Time/Duration.php',
'Time\\TimeException' => $vendorDir . '/symfony/polyfill-time/Resources/stubs/Time/TimeException.php',
'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php', 'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php', 'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
); );
+7 -5
View File
@@ -6,16 +6,18 @@ $vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir); $baseDir = dirname($vendorDir);
return array( return array(
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
'5897ea0ac4cccf14d323035e65887801' => $vendorDir . '/symfony/polyfill-php82/bootstrap.php',
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php', '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php', '662a729f963d39afe703c9d9b7ab4a8c' => $vendorDir . '/symfony/polyfill-php83/bootstrap.php',
'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php', '2203a247e6fda86070a5e4e07aed533a' => $vendorDir . '/symfony/clock/Resources/now.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php', '9d2b9fc6db0f153a0a149fefb182415e' => $vendorDir . '/symfony/polyfill-php84/bootstrap.php',
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php', '606a39d89246991a373564698c2d8383' => $vendorDir . '/symfony/polyfill-php85/bootstrap.php',
'a1105708a18b76903365ca1c4aa61b02' => $vendorDir . '/symfony/translation/Resources/functions.php', 'a1105708a18b76903365ca1c4aa61b02' => $vendorDir . '/symfony/translation/Resources/functions.php',
'23f09fe3194f8c2f70923f90d6702129' => $vendorDir . '/illuminate/collections/functions.php',
'60799491728b879e74601d83e38b2cad' => $vendorDir . '/illuminate/collections/helpers.php', '60799491728b879e74601d83e38b2cad' => $vendorDir . '/illuminate/collections/helpers.php',
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php', '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
'23c18046f52bef3eea034657bafda50f' => $vendorDir . '/symfony/polyfill-php81/bootstrap.php',
'04c6c5c2f7095ccf6c481d3e53e1776f' => $vendorDir . '/mustangostang/spyc/Spyc.php', '04c6c5c2f7095ccf6c481d3e53e1776f' => $vendorDir . '/mustangostang/spyc/Spyc.php',
'89efb1254ef2d1c5d80096acd12c4098' => $vendorDir . '/twig/twig/src/Resources/core.php', '89efb1254ef2d1c5d80096acd12c4098' => $vendorDir . '/twig/twig/src/Resources/core.php',
'ffecb95d45175fd40f75be8a23b34f90' => $vendorDir . '/twig/twig/src/Resources/debug.php', 'ffecb95d45175fd40f75be8a23b34f90' => $vendorDir . '/twig/twig/src/Resources/debug.php',
+6 -1
View File
@@ -7,12 +7,17 @@ $baseDir = dirname($vendorDir);
return array( return array(
'Twig\\' => array($vendorDir . '/twig/twig/src'), 'Twig\\' => array($vendorDir . '/twig/twig/src'),
'Symfony\\Polyfill\\Php81\\' => array($vendorDir . '/symfony/polyfill-php81'), 'Symfony\\Polyfill\\Time\\' => array($vendorDir . '/symfony/polyfill-time'),
'Symfony\\Polyfill\\Php85\\' => array($vendorDir . '/symfony/polyfill-php85'),
'Symfony\\Polyfill\\Php84\\' => array($vendorDir . '/symfony/polyfill-php84'),
'Symfony\\Polyfill\\Php83\\' => array($vendorDir . '/symfony/polyfill-php83'),
'Symfony\\Polyfill\\Php82\\' => array($vendorDir . '/symfony/polyfill-php82'),
'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'), 'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'),
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'), 'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'), 'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'),
'Symfony\\Contracts\\Translation\\' => array($vendorDir . '/symfony/translation-contracts'), 'Symfony\\Contracts\\Translation\\' => array($vendorDir . '/symfony/translation-contracts'),
'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'), 'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'),
'Symfony\\Component\\Clock\\' => array($vendorDir . '/symfony/clock'),
'Stevenmaguire\\OAuth2\\Client\\' => array($vendorDir . '/stevenmaguire/oauth2-keycloak/src'), 'Stevenmaguire\\OAuth2\\Client\\' => array($vendorDir . '/stevenmaguire/oauth2-keycloak/src'),
'RobThree\\Auth\\' => array($vendorDir . '/robthree/twofactorauth/lib'), 'RobThree\\Auth\\' => array($vendorDir . '/robthree/twofactorauth/lib'),
'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'), 'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
+70 -10
View File
@@ -7,16 +7,18 @@ namespace Composer\Autoload;
class ComposerStaticInit873464e4bd965a3168f133248b1b218b class ComposerStaticInit873464e4bd965a3168f133248b1b218b
{ {
public static $files = array ( public static $files = array (
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
'5897ea0ac4cccf14d323035e65887801' => __DIR__ . '/..' . '/symfony/polyfill-php82/bootstrap.php',
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php', '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php', '662a729f963d39afe703c9d9b7ab4a8c' => __DIR__ . '/..' . '/symfony/polyfill-php83/bootstrap.php',
'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php', '2203a247e6fda86070a5e4e07aed533a' => __DIR__ . '/..' . '/symfony/clock/Resources/now.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php', '9d2b9fc6db0f153a0a149fefb182415e' => __DIR__ . '/..' . '/symfony/polyfill-php84/bootstrap.php',
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php', '606a39d89246991a373564698c2d8383' => __DIR__ . '/..' . '/symfony/polyfill-php85/bootstrap.php',
'a1105708a18b76903365ca1c4aa61b02' => __DIR__ . '/..' . '/symfony/translation/Resources/functions.php', 'a1105708a18b76903365ca1c4aa61b02' => __DIR__ . '/..' . '/symfony/translation/Resources/functions.php',
'23f09fe3194f8c2f70923f90d6702129' => __DIR__ . '/..' . '/illuminate/collections/functions.php',
'60799491728b879e74601d83e38b2cad' => __DIR__ . '/..' . '/illuminate/collections/helpers.php', '60799491728b879e74601d83e38b2cad' => __DIR__ . '/..' . '/illuminate/collections/helpers.php',
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php', '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
'23c18046f52bef3eea034657bafda50f' => __DIR__ . '/..' . '/symfony/polyfill-php81/bootstrap.php',
'04c6c5c2f7095ccf6c481d3e53e1776f' => __DIR__ . '/..' . '/mustangostang/spyc/Spyc.php', '04c6c5c2f7095ccf6c481d3e53e1776f' => __DIR__ . '/..' . '/mustangostang/spyc/Spyc.php',
'89efb1254ef2d1c5d80096acd12c4098' => __DIR__ . '/..' . '/twig/twig/src/Resources/core.php', '89efb1254ef2d1c5d80096acd12c4098' => __DIR__ . '/..' . '/twig/twig/src/Resources/core.php',
'ffecb95d45175fd40f75be8a23b34f90' => __DIR__ . '/..' . '/twig/twig/src/Resources/debug.php', 'ffecb95d45175fd40f75be8a23b34f90' => __DIR__ . '/..' . '/twig/twig/src/Resources/debug.php',
@@ -31,12 +33,17 @@ class ComposerStaticInit873464e4bd965a3168f133248b1b218b
), ),
'S' => 'S' =>
array ( array (
'Symfony\\Polyfill\\Php81\\' => 23, 'Symfony\\Polyfill\\Time\\' => 22,
'Symfony\\Polyfill\\Php85\\' => 23,
'Symfony\\Polyfill\\Php84\\' => 23,
'Symfony\\Polyfill\\Php83\\' => 23,
'Symfony\\Polyfill\\Php82\\' => 23,
'Symfony\\Polyfill\\Php80\\' => 23, 'Symfony\\Polyfill\\Php80\\' => 23,
'Symfony\\Polyfill\\Mbstring\\' => 26, 'Symfony\\Polyfill\\Mbstring\\' => 26,
'Symfony\\Polyfill\\Ctype\\' => 23, 'Symfony\\Polyfill\\Ctype\\' => 23,
'Symfony\\Contracts\\Translation\\' => 30, 'Symfony\\Contracts\\Translation\\' => 30,
'Symfony\\Component\\Translation\\' => 30, 'Symfony\\Component\\Translation\\' => 30,
'Symfony\\Component\\Clock\\' => 24,
'Stevenmaguire\\OAuth2\\Client\\' => 28, 'Stevenmaguire\\OAuth2\\Client\\' => 28,
), ),
'R' => 'R' =>
@@ -105,9 +112,25 @@ class ComposerStaticInit873464e4bd965a3168f133248b1b218b
array ( array (
0 => __DIR__ . '/..' . '/twig/twig/src', 0 => __DIR__ . '/..' . '/twig/twig/src',
), ),
'Symfony\\Polyfill\\Php81\\' => 'Symfony\\Polyfill\\Time\\' =>
array ( array (
0 => __DIR__ . '/..' . '/symfony/polyfill-php81', 0 => __DIR__ . '/..' . '/symfony/polyfill-time',
),
'Symfony\\Polyfill\\Php85\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-php85',
),
'Symfony\\Polyfill\\Php84\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-php84',
),
'Symfony\\Polyfill\\Php83\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-php83',
),
'Symfony\\Polyfill\\Php82\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-php82',
), ),
'Symfony\\Polyfill\\Php80\\' => 'Symfony\\Polyfill\\Php80\\' =>
array ( array (
@@ -129,6 +152,10 @@ class ComposerStaticInit873464e4bd965a3168f133248b1b218b
array ( array (
0 => __DIR__ . '/..' . '/symfony/translation', 0 => __DIR__ . '/..' . '/symfony/translation',
), ),
'Symfony\\Component\\Clock\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/clock',
),
'Stevenmaguire\\OAuth2\\Client\\' => 'Stevenmaguire\\OAuth2\\Client\\' =>
array ( array (
0 => __DIR__ . '/..' . '/stevenmaguire/oauth2-keycloak/src', 0 => __DIR__ . '/..' . '/stevenmaguire/oauth2-keycloak/src',
@@ -253,12 +280,45 @@ class ComposerStaticInit873464e4bd965a3168f133248b1b218b
); );
public static $classMap = array ( public static $classMap = array (
'AllowDynamicProperties' => __DIR__ . '/..' . '/symfony/polyfill-php82/Resources/stubs/AllowDynamicProperties.php',
'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', 'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
'CURLStringFile' => __DIR__ . '/..' . '/symfony/polyfill-php81/Resources/stubs/CURLStringFile.php',
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
'DateError' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateError.php',
'DateException' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateException.php',
'DateInvalidOperationException' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateInvalidOperationException.php',
'DateInvalidTimeZoneException' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateInvalidTimeZoneException.php',
'DateMalformedIntervalStringException' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateMalformedIntervalStringException.php',
'DateMalformedPeriodStringException' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateMalformedPeriodStringException.php',
'DateMalformedStringException' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateMalformedStringException.php',
'DateObjectError' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateObjectError.php',
'DateRangeError' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateRangeError.php',
'DelayedTargetValidation' => __DIR__ . '/..' . '/symfony/polyfill-php85/Resources/stubs/DelayedTargetValidation.php',
'Deprecated' => __DIR__ . '/..' . '/symfony/polyfill-php84/Resources/stubs/Deprecated.php',
'Filter\\FilterException' => __DIR__ . '/..' . '/symfony/polyfill-php85/Resources/stubs/Filter/FilterException.php',
'Filter\\FilterFailedException' => __DIR__ . '/..' . '/symfony/polyfill-php85/Resources/stubs/Filter/FilterFailedException.php',
'NoDiscard' => __DIR__ . '/..' . '/symfony/polyfill-php85/Resources/stubs/NoDiscard.php',
'Override' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/Override.php',
'Pdo\\Dblib' => __DIR__ . '/..' . '/symfony/polyfill-php84/Resources/stubs/Pdo/Dblib.php',
'Pdo\\Firebird' => __DIR__ . '/..' . '/symfony/polyfill-php84/Resources/stubs/Pdo/Firebird.php',
'Pdo\\Mysql' => __DIR__ . '/..' . '/symfony/polyfill-php84/Resources/stubs/Pdo/Mysql.php',
'Pdo\\Odbc' => __DIR__ . '/..' . '/symfony/polyfill-php84/Resources/stubs/Pdo/Odbc.php',
'Pdo\\Pgsql' => __DIR__ . '/..' . '/symfony/polyfill-php84/Resources/stubs/Pdo/Pgsql.php',
'Pdo\\Sqlite' => __DIR__ . '/..' . '/symfony/polyfill-php84/Resources/stubs/Pdo/Sqlite.php',
'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php', 'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
'ReturnTypeWillChange' => __DIR__ . '/..' . '/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php', 'Random\\BrokenRandomEngineError' => __DIR__ . '/..' . '/symfony/polyfill-php82/Resources/stubs/Random/BrokenRandomEngineError.php',
'Random\\CryptoSafeEngine' => __DIR__ . '/..' . '/symfony/polyfill-php82/Resources/stubs/Random/CryptoSafeEngine.php',
'Random\\Engine' => __DIR__ . '/..' . '/symfony/polyfill-php82/Resources/stubs/Random/Engine.php',
'Random\\Engine\\Secure' => __DIR__ . '/..' . '/symfony/polyfill-php82/Resources/stubs/Random/Engine/Secure.php',
'Random\\RandomError' => __DIR__ . '/..' . '/symfony/polyfill-php82/Resources/stubs/Random/RandomError.php',
'Random\\RandomException' => __DIR__ . '/..' . '/symfony/polyfill-php82/Resources/stubs/Random/RandomException.php',
'ReflectionConstant' => __DIR__ . '/..' . '/symfony/polyfill-php84/Resources/stubs/ReflectionConstant.php',
'RoundingMode' => __DIR__ . '/..' . '/symfony/polyfill-php84/Resources/stubs/RoundingMode.php',
'SQLite3Exception' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/SQLite3Exception.php',
'SensitiveParameter' => __DIR__ . '/..' . '/symfony/polyfill-php82/Resources/stubs/SensitiveParameter.php',
'SensitiveParameterValue' => __DIR__ . '/..' . '/symfony/polyfill-php82/Resources/stubs/SensitiveParameterValue.php',
'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', 'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
'Time\\Duration' => __DIR__ . '/..' . '/symfony/polyfill-time/Resources/stubs/Time/Duration.php',
'Time\\TimeException' => __DIR__ . '/..' . '/symfony/polyfill-time/Resources/stubs/Time/TimeException.php',
'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php', 'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php', 'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
); );
File diff suppressed because it is too large. Load diff
+145 -118
View File
@@ -29,18 +29,18 @@
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'bshaffer/oauth2-server-php' => array( 'bshaffer/oauth2-server-php' => array(
'pretty_version' => 'v1.11.1', 'pretty_version' => 'v1.14.2',
'version' => '1.11.1.0', 'version' => '1.14.2.0',
'reference' => '5a0c8000d4763b276919e2106f54eddda6bc50fa', 'reference' => '1c715c2d2f71254d1c683b8c89a0f16b61ccd0d6',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../bshaffer/oauth2-server-php', 'install_path' => __DIR__ . '/../bshaffer/oauth2-server-php',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'carbonphp/carbon-doctrine-types' => array( 'carbonphp/carbon-doctrine-types' => array(
'pretty_version' => '3.2.0', 'pretty_version' => '3.2.1',
'version' => '3.2.0.0', 'version' => '3.2.1.0',
'reference' => '18ba5ddfec8976260ead6e866180bd5d2f71aa1d', 'reference' => '5fa5eacafd9ef47c8c6ab9143fc901ba0194d3dc',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../carbonphp/carbon-doctrine-types', 'install_path' => __DIR__ . '/../carbonphp/carbon-doctrine-types',
'aliases' => array(), 'aliases' => array(),
@@ -56,18 +56,18 @@
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'ddeboer/imap' => array( 'ddeboer/imap' => array(
'pretty_version' => '1.13.1', 'pretty_version' => '1.19.0',
'version' => '1.13.1.0', 'version' => '1.19.0.0',
'reference' => '8b772d04b1deadb5df13782fb78c4b648f77496e', 'reference' => '30800b1cfeacc4add5bb418e40a8b6e95a8a04ac',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../ddeboer/imap', 'install_path' => __DIR__ . '/../ddeboer/imap',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'directorytree/ldaprecord' => array( 'directorytree/ldaprecord' => array(
'pretty_version' => 'v3.8.5', 'pretty_version' => 'v3.8.6',
'version' => '3.8.5.0', 'version' => '3.8.6.0',
'reference' => '00e5f088f8c4028d5f398783cccc2e8119a27a65', 'reference' => 'a1cab9f078eb4756fc6fad58caea1e3d11cad0ec',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../directorytree/ldaprecord', 'install_path' => __DIR__ . '/../directorytree/ldaprecord',
'aliases' => array(), 'aliases' => array(),
@@ -80,90 +80,90 @@
), ),
), ),
'firebase/php-jwt' => array( 'firebase/php-jwt' => array(
'pretty_version' => 'v6.5.0', 'pretty_version' => 'v7.2.1',
'version' => '6.5.0.0', 'version' => '7.2.1.0',
'reference' => 'e94e7353302b0c11ec3cfff7180cd0b1743975d2', 'reference' => '0501f5729b9e2448516674aff07c7d1727869b8e',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../firebase/php-jwt', 'install_path' => __DIR__ . '/../firebase/php-jwt',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'guzzlehttp/guzzle' => array( 'guzzlehttp/guzzle' => array(
'pretty_version' => '7.5.0', 'pretty_version' => '8.3.1',
'version' => '7.5.0.0', 'version' => '8.3.1.0',
'reference' => 'b50a2a1251152e43f6a37f0fa053e730a67d25ba', 'reference' => '7d9b1f430bb65ac664e120af227bf9a795ad6333',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../guzzlehttp/guzzle', 'install_path' => __DIR__ . '/../guzzlehttp/guzzle',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'guzzlehttp/promises' => array( 'guzzlehttp/promises' => array(
'pretty_version' => '1.5.2', 'pretty_version' => '3.1.0',
'version' => '1.5.2.0', 'version' => '3.1.0.0',
'reference' => 'b94b2807d85443f9719887892882d0329d1e2598', 'reference' => '094d0ec418eb3b1411ea271a180ec95cae6ae933',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../guzzlehttp/promises', 'install_path' => __DIR__ . '/../guzzlehttp/promises',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'guzzlehttp/psr7' => array( 'guzzlehttp/psr7' => array(
'pretty_version' => '2.4.5', 'pretty_version' => '3.2.0',
'version' => '2.4.5.0', 'version' => '3.2.0.0',
'reference' => '0454e12ef0cd597ccd2adb036f7bda4e7fface66', 'reference' => 'cdc139396207a5eba10c242d241fb014cb098390',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../guzzlehttp/psr7', 'install_path' => __DIR__ . '/../guzzlehttp/psr7',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'illuminate/collections' => array( 'illuminate/collections' => array(
'pretty_version' => 'v10.49.0', 'pretty_version' => 'v12.70.2',
'version' => '10.49.0.0', 'version' => '12.70.2.0',
'reference' => '6ae9c74fa92d4e1824d1b346cd435e8eacdc3232', 'reference' => '83313b009c4afb6f02dbc090bdb67809756eefa2',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../illuminate/collections', 'install_path' => __DIR__ . '/../illuminate/collections',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'illuminate/conditionable' => array( 'illuminate/conditionable' => array(
'pretty_version' => 'v10.49.0', 'pretty_version' => 'v12.70.2',
'version' => '10.49.0.0', 'version' => '12.70.2.0',
'reference' => '47c700320b7a419f0d188d111f3bbed978fcbd3f', 'reference' => 'ec677967c1f2faf90b8428919124d2184a4c9b49',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../illuminate/conditionable', 'install_path' => __DIR__ . '/../illuminate/conditionable',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'illuminate/contracts' => array( 'illuminate/contracts' => array(
'pretty_version' => 'v10.44.0', 'pretty_version' => 'v12.70.2',
'version' => '10.44.0.0', 'version' => '12.70.2.0',
'reference' => '8d7152c4a1f5d9cf7da3e8b71f23e4556f6138ac', 'reference' => 'c16fd7ba7d8e6b8f336639ceb6b7e1ff6ed0efb5',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../illuminate/contracts', 'install_path' => __DIR__ . '/../illuminate/contracts',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'illuminate/macroable' => array( 'illuminate/macroable' => array(
'pretty_version' => 'v10.49.0', 'pretty_version' => 'v12.70.2',
'version' => '10.49.0.0', 'version' => '12.70.2.0',
'reference' => 'dff667a46ac37b634dcf68909d9d41e94dc97c27', 'reference' => 'e295d62d89dcdb87e2b1bd70dd14d074a0ed73cc',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../illuminate/macroable', 'install_path' => __DIR__ . '/../illuminate/macroable',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'league/oauth2-client' => array( 'league/oauth2-client' => array(
'pretty_version' => '2.7.0', 'pretty_version' => '2.9.1',
'version' => '2.7.0.0', 'version' => '2.9.1.0',
'reference' => '160d6274b03562ebeb55ed18399281d8118b76c8', 'reference' => '8cedfef9d01a8d1fd2ecfabb41734b17592c4b71',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../league/oauth2-client', 'install_path' => __DIR__ . '/../league/oauth2-client',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'matthiasmullie/minify' => array( 'matthiasmullie/minify' => array(
'pretty_version' => '1.3.66', 'pretty_version' => '1.3.75',
'version' => '1.3.66.0', 'version' => '1.3.75.0',
'reference' => '45fd3b0f1dfa2c965857c6d4a470bea52adc31a6', 'reference' => '76ba4a5f555fd7bf4aa408af608e991569076671',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../matthiasmullie/minify', 'install_path' => __DIR__ . '/../matthiasmullie/minify',
'aliases' => array(), 'aliases' => array(),
@@ -194,36 +194,27 @@
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'nesbot/carbon' => array( 'nesbot/carbon' => array(
'pretty_version' => '2.72.3', 'pretty_version' => '3.15.1',
'version' => '2.72.3.0', 'version' => '3.15.1.0',
'reference' => '0c6fd108360c562f6e4fd1dedb8233b423e91c83', 'reference' => 'ce0e5636fae7d1e99856eb70cae4c719a284aa08',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../nesbot/carbon', 'install_path' => __DIR__ . '/../nesbot/carbon',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, '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(),
'dev_requirement' => false,
),
'php-mime-mail-parser/php-mime-mail-parser' => array( 'php-mime-mail-parser/php-mime-mail-parser' => array(
'pretty_version' => '7.0.0', 'pretty_version' => '7.1.2',
'version' => '7.0.0.0', 'version' => '7.1.2.0',
'reference' => '9d09a017f3f103fec8456211a4a538b80e0eca0d', 'reference' => '81d84bbc6a73b913110e155e98059526825c7ee3',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../php-mime-mail-parser/php-mime-mail-parser', 'install_path' => __DIR__ . '/../php-mime-mail-parser/php-mime-mail-parser',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'phpmailer/phpmailer' => array( 'phpmailer/phpmailer' => array(
'pretty_version' => 'v6.6.0', 'pretty_version' => 'v6.12.0',
'version' => '6.6.0.0', 'version' => '6.12.0.0',
'reference' => 'e43bac82edc26ca04b36143a48bde1c051cfd5b1', 'reference' => 'd1ac35d784bf9f5e61b424901d5a014967f15b12',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../phpmailer/phpmailer', 'install_path' => __DIR__ . '/../phpmailer/phpmailer',
'aliases' => array(), 'aliases' => array(),
@@ -254,9 +245,9 @@
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'psr/http-client' => array( 'psr/http-client' => array(
'pretty_version' => '1.0.1', 'pretty_version' => '1.0.3',
'version' => '1.0.1.0', 'version' => '1.0.3.0',
'reference' => '2dfb5f6c5eff0e91e20e913f8c5452ed95b86621', 'reference' => 'bb5906edc1c324c9a05aa0873d40117941e5fa90',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../psr/http-client', 'install_path' => __DIR__ . '/../psr/http-client',
'aliases' => array(), 'aliases' => array(),
@@ -269,9 +260,9 @@
), ),
), ),
'psr/http-factory' => array( 'psr/http-factory' => array(
'pretty_version' => '1.0.1', 'pretty_version' => '1.1.0',
'version' => '1.0.1.0', 'version' => '1.1.0.0',
'reference' => '12ac7fcd07e5b077433f5f2bee95b3a771bf61be', 'reference' => '2b4765fddfe3b508ac62f829e852b1501d3f6e8a',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../psr/http-factory', 'install_path' => __DIR__ . '/../psr/http-factory',
'aliases' => array(), 'aliases' => array(),
@@ -280,13 +271,13 @@
'psr/http-factory-implementation' => array( 'psr/http-factory-implementation' => array(
'dev_requirement' => false, 'dev_requirement' => false,
'provided' => array( 'provided' => array(
0 => '1.0', 0 => '1.1',
), ),
), ),
'psr/http-message' => array( 'psr/http-message' => array(
'pretty_version' => '1.0.1', 'pretty_version' => '2.0',
'version' => '1.0.1.0', 'version' => '2.0.0.0',
'reference' => 'f6561bf28d520154e4b0ec72be95418abe6d9363', 'reference' => '402d35bcb92c70c026d1a6a9883f06b2ead23d71',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../psr/http-message', 'install_path' => __DIR__ . '/../psr/http-message',
'aliases' => array(), 'aliases' => array(),
@@ -295,40 +286,31 @@
'psr/http-message-implementation' => array( 'psr/http-message-implementation' => array(
'dev_requirement' => false, 'dev_requirement' => false,
'provided' => array( 'provided' => array(
0 => '1.0', 0 => '2.0',
), ),
), ),
'psr/log' => array( 'psr/log' => array(
'pretty_version' => '3.0.0', 'pretty_version' => '3.0.2',
'version' => '3.0.0.0', 'version' => '3.0.2.0',
'reference' => 'fe5ea303b0887d5caefd3d431c3e61ad47037001', 'reference' => 'f16e1d5863e37f8d8c2a01719f5b34baa2b714d3',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../psr/log', 'install_path' => __DIR__ . '/../psr/log',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'psr/simple-cache' => array( 'psr/simple-cache' => array(
'pretty_version' => '2.0.0', 'pretty_version' => '3.0.0',
'version' => '2.0.0.0', 'version' => '3.0.0.0',
'reference' => '8707bf3cea6f710bf6ef05491234e3ab06f6432a', 'reference' => '764e0b3939f5ca87cb904f570ef9be2d78a07865',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../psr/simple-cache', 'install_path' => __DIR__ . '/../psr/simple-cache',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'ralouphie/getallheaders' => array(
'pretty_version' => '3.0.3',
'version' => '3.0.3.0',
'reference' => '120b605dfeb996808c31b6477290a714d356e822',
'type' => 'library',
'install_path' => __DIR__ . '/../ralouphie/getallheaders',
'aliases' => array(),
'dev_requirement' => false,
),
'robthree/twofactorauth' => array( 'robthree/twofactorauth' => array(
'pretty_version' => '1.8.1', 'pretty_version' => '1.8.2',
'version' => '1.8.1.0', 'version' => '1.8.2.0',
'reference' => '5afcb45282f1c75562a48d479ecd1732c9bdb11b', 'reference' => '65681de5a324eae05140ac58b08648a60212afc0',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../robthree/twofactorauth', 'install_path' => __DIR__ . '/../robthree/twofactorauth',
'aliases' => array(), 'aliases' => array(),
@@ -344,72 +326,117 @@
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'stevenmaguire/oauth2-keycloak' => array( 'stevenmaguire/oauth2-keycloak' => array(
'pretty_version' => '4.0.0', 'pretty_version' => '6.1.1',
'version' => '4.0.0.0', 'version' => '6.1.1.0',
'reference' => '05ead6bb6bcd2b6f96dfae87c769dcd3e5f6129d', 'reference' => '31bb3b1fa15b444212ed43facc898fafc7c2707a',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../stevenmaguire/oauth2-keycloak', 'install_path' => __DIR__ . '/../stevenmaguire/oauth2-keycloak',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/clock' => array(
'pretty_version' => 'v7.4.21',
'version' => '7.4.21.0',
'reference' => '07437f612dca89c1894c1db04491002b1e798f03',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/clock',
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/deprecation-contracts' => array( 'symfony/deprecation-contracts' => array(
'pretty_version' => 'v3.5.0', 'pretty_version' => 'v3.7.1',
'version' => '3.5.0.0', 'version' => '3.7.1.0',
'reference' => '0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1', 'reference' => 'f3202fa1b5097b0af062dc978b32ecf63404e31d',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/deprecation-contracts', 'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/polyfill-ctype' => array( 'symfony/polyfill-ctype' => array(
'pretty_version' => 'v1.24.0', 'pretty_version' => 'v1.37.0',
'version' => '1.24.0.0', 'version' => '1.37.0.0',
'reference' => '30885182c981ab175d4d034db0f6f469898070ab', 'reference' => '141046a8f9477948ff284fa65be2095baafb94f2',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-ctype', 'install_path' => __DIR__ . '/../symfony/polyfill-ctype',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/polyfill-mbstring' => array( 'symfony/polyfill-mbstring' => array(
'pretty_version' => 'v1.29.0', 'pretty_version' => 'v1.43.3',
'version' => '1.29.0.0', 'version' => '1.43.3.0',
'reference' => '9773676c8a1bb1f8d4340a62efe641cf76eda7ec', 'reference' => '122889ed24dc000165bcbfd77ec412f15cbdfc6a',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring', 'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/polyfill-php80' => array( 'symfony/polyfill-php80' => array(
'pretty_version' => 'v1.29.0', 'pretty_version' => 'v1.43.0',
'version' => '1.29.0.0', 'version' => '1.43.0.0',
'reference' => '87b68208d5c1188808dd7839ee1e6c8ec3b02f1b', 'reference' => '9c6a5d6b01ca51d486c813e9a9c0ed55f208bb74',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php80', 'install_path' => __DIR__ . '/../symfony/polyfill-php80',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/polyfill-php81' => array( 'symfony/polyfill-php82' => array(
'pretty_version' => 'v1.31.0', 'pretty_version' => 'v1.43.0',
'version' => '1.31.0.0', 'version' => '1.43.0.0',
'reference' => '4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c', 'reference' => '843bbe8b2a7934307b749751eede0f111f5d1cfc',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php81', 'install_path' => __DIR__ . '/../symfony/polyfill-php82',
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/polyfill-php83' => array(
'pretty_version' => 'v1.43.0',
'version' => '1.43.0.0',
'reference' => '80ccff923a8d61f73ebe9da0aa94e25232f004ce',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php83',
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/polyfill-php84' => array(
'pretty_version' => 'v1.43.0',
'version' => '1.43.0.0',
'reference' => '22c6548499cbe5c5bb072fc0c42312dff062a015',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php84',
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/polyfill-php85' => array(
'pretty_version' => 'v1.43.0',
'version' => '1.43.0.0',
'reference' => '86a42cbcc07adc331481a5ab310a4024bfc227f6',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php85',
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/polyfill-time' => array(
'pretty_version' => 'v1.42.0',
'version' => '1.42.0.0',
'reference' => 'ac9f1ed802dd58f37e720388130c088434aa09fc',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-time',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/translation' => array( 'symfony/translation' => array(
'pretty_version' => 'v6.4.3', 'pretty_version' => 'v7.4.17',
'version' => '6.4.3.0', 'version' => '7.4.17.0',
'reference' => '637c51191b6b184184bbf98937702bcf554f7d04', 'reference' => '2ee1e4a3b32a528a642babe041ff7c440b213b4b',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/translation', 'install_path' => __DIR__ . '/../symfony/translation',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/translation-contracts' => array( 'symfony/translation-contracts' => array(
'pretty_version' => 'v3.4.1', 'pretty_version' => 'v3.7.1',
'version' => '3.4.1.0', 'version' => '3.7.1.0',
'reference' => '06450585bf65e978026bda220cdebca3f867fde7', 'reference' => 'ccb206b98faccc511ebae8e5fad50f2dc0b30621',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/translation-contracts', 'install_path' => __DIR__ . '/../symfony/translation-contracts',
'aliases' => array(), 'aliases' => array(),
@@ -422,9 +449,9 @@
), ),
), ),
'twig/twig' => array( 'twig/twig' => array(
'pretty_version' => 'v3.14.0', 'pretty_version' => 'v3.30.0',
'version' => '3.14.0.0', 'version' => '3.30.0.0',
'reference' => '126b2c97818dbff0cdf3fbfc881aedb3d40aae72', 'reference' => '8c737079b726af72ff8ef3c595be9f6a810ea1ef',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../twig/twig', 'install_path' => __DIR__ . '/../twig/twig',
'aliases' => array(), 'aliases' => array(),
+4 -5
View File
@@ -4,8 +4,8 @@
$issues = array(); $issues = array();
if (!(PHP_VERSION_ID >= 80100)) { if (!(PHP_VERSION_ID >= 80200)) {
$issues[] = 'Your Composer dependencies require a PHP version ">= 8.1.0". You are running ' . PHP_VERSION . '.'; $issues[] = 'Your Composer dependencies require a PHP version ">= 8.2.0". You are running ' . PHP_VERSION . '.';
} }
if ($issues) { if ($issues) {
@@ -19,8 +19,7 @@ if ($issues) {
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; 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( throw new \RuntimeException(
'Composer detected issues in your platform: ' . implode(' ', $issues), 'Composer detected issues in your platform: ' . implode(' ', $issues)
E_USER_ERROR
); );
} }
+3 -3
View File
@@ -17,7 +17,7 @@ return (new PhpCsFixer\Config())
'binary_operator_spaces' => ['default' => 'align_single_space'], 'binary_operator_spaces' => ['default' => 'align_single_space'],
'blank_line_before_statement' => true, 'blank_line_before_statement' => true,
'class_definition' => ['single_item_single_line' => true], 'class_definition' => ['single_item_single_line' => true],
'compact_nullable_typehint' => true, 'compact_nullable_type_declaration' => true,
'concat_space' => ['spacing' => 'one'], 'concat_space' => ['spacing' => 'one'],
'echo_tag_syntax' => ['format' => 'long'], 'echo_tag_syntax' => ['format' => 'long'],
'error_suppression' => false, 'error_suppression' => false,
@@ -39,7 +39,7 @@ return (new PhpCsFixer\Config())
'no_null_property_initialization' => true, 'no_null_property_initialization' => true,
'no_php4_constructor' => true, 'no_php4_constructor' => true,
'no_superfluous_elseif' => true, 'no_superfluous_elseif' => true,
'no_unneeded_curly_braces' => true, 'no_unneeded_braces' => true,
'no_unneeded_final_method' => true, 'no_unneeded_final_method' => true,
'no_unreachable_default_argument_value' => true, 'no_unreachable_default_argument_value' => true,
'no_useless_else' => true, 'no_useless_else' => true,
@@ -49,7 +49,7 @@ return (new PhpCsFixer\Config())
'php_unit_set_up_tear_down_visibility' => true, 'php_unit_set_up_tear_down_visibility' => true,
'php_unit_strict' => true, 'php_unit_strict' => true,
'php_unit_test_annotation' => true, 'php_unit_test_annotation' => true,
'php_unit_test_case_static_method_calls' => true, 'php_unit_test_case_static_method_calls' => false,
'php_unit_test_class_requires_covers' => false, 'php_unit_test_class_requires_covers' => false,
'phpdoc_add_missing_param_annotation' => true, 'phpdoc_add_missing_param_annotation' => true,
'phpdoc_order' => true, 'phpdoc_order' => true,
+20 -22
View File
@@ -2,12 +2,12 @@
[![Latest Stable Version](https://img.shields.io/packagist/v/ddeboer/imap.svg)](https://packagist.org/packages/ddeboer/imap) [![Latest Stable Version](https://img.shields.io/packagist/v/ddeboer/imap.svg)](https://packagist.org/packages/ddeboer/imap)
[![Downloads](https://img.shields.io/packagist/dt/ddeboer/imap.svg)](https://packagist.org/packages/ddeboer/imap) [![Downloads](https://img.shields.io/packagist/dt/ddeboer/imap.svg)](https://packagist.org/packages/ddeboer/imap)
[![Integrate](https://github.com/ddeboer/imap/workflows/Integrate/badge.svg?branch=master)](https://github.com/ddeboer/imap/actions) [![Integrate](https://github.com/ddeboer/imap/workflows/CI/badge.svg)](https://github.com/ddeboer/imap/actions)
[![Code Coverage](https://codecov.io/gh/ddeboer/imap/coverage.svg?branch=master)](https://codecov.io/gh/ddeboer/imap?branch=master) [![Code Coverage](https://codecov.io/gh/ddeboer/imap/coverage.svg?branch=master)](https://codecov.io/gh/ddeboer/imap?branch=master)
A PHP IMAP library to read and process e-mails over IMAP protocol, built with robust Object-Oriented architecture. A PHP IMAP library to read and process e-mails over IMAP protocol, built with robust Object-Oriented architecture.
This library requires PHP >= 7.4 with [IMAP](https://www.php.net/manual/en/book.imap.php), This library requires PHP >= 8.2 with [IMAP](https://www.php.net/manual/en/book.imap.php),
[iconv](https://www.php.net/manual/en/book.iconv.php) and [iconv](https://www.php.net/manual/en/book.iconv.php) and
[Multibyte String](https://www.php.net/manual/en/book.mbstring.php) extensions installed. [Multibyte String](https://www.php.net/manual/en/book.mbstring.php) extensions installed.
@@ -157,7 +157,7 @@ By the way most of the common search criteria are available and functioning, bro
References: References:
1. https://stackoverflow.com/questions/36356715/imap-search-unknown-search-criterion-or 1. https://stackoverflow.com/questions/36356715/imap-search-unknown-search-criterion-or
1. imap-2007f.tar.gz: `./src/c-client/mail.c` and `./docs/internal.txt` 2. imap-2007f.tar.gz: `./src/c-client/mail.c` and `./docs/internal.txt`
#### Message Properties and Operations #### Message Properties and Operations
@@ -182,19 +182,29 @@ $message->isDraft();
$message->isSeen(); $message->isSeen();
``` ```
Get message headers as a [\Ddeboer\Imap\Message\Headers](/src/Ddeboer/Imap/Message/Headers.php) object: Get message headers as a [\Ddeboer\Imap\Message\Headers](/src/Message/Headers.php) object:
```php ```php
$message->getHeaders(); $message->getHeaders();
``` ```
Get message body as HTML or plain text: Get message body as HTML or plain text (only first part):
```php ```php
$message->getBodyHtml(); // Content of text/html part, if present $message->getBodyHtml(); // Content of text/html part, if present
$message->getBodyText(); // Content of text/plain part, if present $message->getBodyText(); // Content of text/plain part, if present
``` ```
Get complete body (all parts):
```php
$body = $message->getCompleteBodyHtml(); // Content of text/html part, if present
if ($body === null) { // If body is null, there are no HTML parts, so let's try getting the text body
$body = $message->getCompleteBodyText(); // Content of text/plain part, if present
}
```
Reading the message body keeps the message as unseen. Reading the message body keeps the message as unseen.
If you want to mark the message as seen: If you want to mark the message as seen:
@@ -284,24 +294,12 @@ Mockability is granted by interfaces present for each API.
Dig into [MockabilityTest](tests/MockabilityTest.php) for an example of a Dig into [MockabilityTest](tests/MockabilityTest.php) for an example of a
mocked workflow. mocked workflow.
## Running the Tests ## Contributing: run the build locally
This library is functionally tested on [Travis CI](https://travis-ci.org/ddeboer/imap) Docker is needed to run the build on your computer.
against a local Dovecot server.
If you have your own IMAP (test) account, you can run the tests locally by First command you need to run is `make start-imap-server`, which starts an IMAP server locally.
providing your IMAP credentials:
```bash Then the local build can be triggered with a bare `make`.
$ composer install
$ IMAP_SERVER_NAME="my.imap.server.com" IMAP_SERVER_PORT="60993" IMAP_USERNAME="johndoe" IMAP_PASSWORD="p4ssword" vendor/bin/phpunit
```
You can also copy `phpunit.xml.dist` file to a custom `phpunit.xml` and put When you finish the development, stop the local IMAP server with `make stop-imap-server`.
these environment variables in it.
**WARNING** Tests create new mailboxes without removing them.
```
$ docker-compose run tests
```
+9 -13
View File
@@ -22,19 +22,20 @@
} }
], ],
"require": { "require": {
"php": "^8.0.1", "php": "~8.2.0 || ~8.3.0",
"ext-dom": "*",
"ext-iconv": "*", "ext-iconv": "*",
"ext-imap": "*", "ext-imap": "*",
"ext-libxml": "*",
"ext-mbstring": "*" "ext-mbstring": "*"
}, },
"require-dev": { "require-dev": {
"friendsofphp/php-cs-fixer": "^v3.4.0", "friendsofphp/php-cs-fixer": "^3.38.2",
"laminas/laminas-mail": "^2.15.1", "laminas/laminas-mail": "^2.25.1",
"malukenho/mcbumpface": "^1.1.5", "phpstan/phpstan": "^1.10.43",
"phpstan/phpstan": "^1.3.3", "phpstan/phpstan-phpunit": "^1.3.15",
"phpstan/phpstan-phpunit": "^1.0.0", "phpstan/phpstan-strict-rules": "^1.5.2",
"phpstan/phpstan-strict-rules": "^1.1.0", "phpunit/phpunit": "^10.4.2"
"phpunit/phpunit": "^9.5.11"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
@@ -45,10 +46,5 @@
"psr-4": { "psr-4": {
"Ddeboer\\Imap\\Tests\\": "tests/" "Ddeboer\\Imap\\Tests\\": "tests/"
} }
},
"config": {
"allow-plugins": {
"malukenho/mcbumpface": true
}
} }
} }
+3 -1
View File
@@ -50,9 +50,11 @@ final class Connection implements ConnectionInterface
public function close(int $flag = 0): bool public function close(int $flag = 0): bool
{ {
$stream = $this->resource->getStream();
$this->resource->clearLastMailboxUsedCache(); $this->resource->clearLastMailboxUsedCache();
return \imap_close($this->resource->getStream(), $flag); return \imap_close($stream, $flag);
} }
public function getQuota(string $root = 'INBOX'): array public function getQuota(string $root = 'INBOX'): array
@@ -6,7 +6,6 @@ namespace Ddeboer\Imap;
use Ddeboer\Imap\Exception\CreateMailboxException; use Ddeboer\Imap\Exception\CreateMailboxException;
use Ddeboer\Imap\Exception\DeleteMailboxException; use Ddeboer\Imap\Exception\DeleteMailboxException;
use Ddeboer\Imap\Exception\InvalidResourceException;
use Ddeboer\Imap\Exception\MailboxDoesNotExistException; use Ddeboer\Imap\Exception\MailboxDoesNotExistException;
/** /**
@@ -31,8 +30,6 @@ interface ConnectionInterface extends \Countable
/** /**
* Check if the connection is still active. * Check if the connection is still active.
*
* @throws InvalidResourceException If connection was closed
*/ */
public function ping(): bool; public function ping(): bool;
@@ -4,6 +4,6 @@ declare(strict_types=1);
namespace Ddeboer\Imap\Exception; namespace Ddeboer\Imap\Exception;
final class InvalidResourceException extends AbstractException final class RenameMailboxException extends AbstractException
{ {
} }
+5 -20
View File
@@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Ddeboer\Imap; namespace Ddeboer\Imap;
use Ddeboer\Imap\Exception\InvalidResourceException;
use Ddeboer\Imap\Exception\ReopenMailboxException; use Ddeboer\Imap\Exception\ReopenMailboxException;
use IMAP\Connection; use IMAP\Connection;
@@ -13,33 +12,21 @@ use IMAP\Connection;
*/ */
final class ImapResource implements ImapResourceInterface final class ImapResource implements ImapResourceInterface
{ {
/** private Connection $resource;
* @var resource private ?MailboxInterface $mailbox;
*/
private $resource;
private ?MailboxInterface $mailbox = null;
private static ?string $lastMailboxUsedCache = null; private static ?string $lastMailboxUsedCache = null;
/** /**
* Constructor. * Constructor.
*
* @param Connection|resource $resource
*/ */
public function __construct($resource, MailboxInterface $mailbox = null) public function __construct(Connection $resource, MailboxInterface $mailbox = null)
{ {
$this->resource = $resource; $this->resource = $resource;
$this->mailbox = $mailbox; $this->mailbox = $mailbox;
} }
public function getStream() public function getStream(): Connection
{ {
if (
!$this->resource instanceof Connection
&& (false === \is_resource($this->resource) || 'imap' !== \get_resource_type($this->resource))
) {
throw new InvalidResourceException('Supplied resource is not a valid imap resource');
}
$this->initMailbox(); $this->initMailbox();
return $this->resource; return $this->resource;
@@ -76,10 +63,8 @@ final class ImapResource implements ImapResourceInterface
/** /**
* Check whether the current mailbox is open. * Check whether the current mailbox is open.
*
* @param resource $resource
*/ */
private static function isMailboxOpen(MailboxInterface $mailbox, $resource): bool private static function isMailboxOpen(MailboxInterface $mailbox, Connection $resource): bool
{ {
$currentMailboxName = $mailbox->getFullEncodedName(); $currentMailboxName = $mailbox->getFullEncodedName();
if ($currentMailboxName === self::$lastMailboxUsedCache) { if ($currentMailboxName === self::$lastMailboxUsedCache) {
@@ -4,18 +4,14 @@ declare(strict_types=1);
namespace Ddeboer\Imap; namespace Ddeboer\Imap;
use Ddeboer\Imap\Exception\InvalidResourceException; use IMAP\Connection;
interface ImapResourceInterface interface ImapResourceInterface
{ {
/** /**
* Get IMAP resource stream. * Get IMAP resource stream.
*
* @throws InvalidResourceException
*
* @return resource
*/ */
public function getStream(); public function getStream(): Connection;
/** /**
* Clear last mailbox used cache. * Clear last mailbox used cache.
+19 -6
View File
@@ -4,12 +4,12 @@ declare(strict_types=1);
namespace Ddeboer\Imap; namespace Ddeboer\Imap;
use DateTimeInterface;
use Ddeboer\Imap\Exception\ImapNumMsgException; use Ddeboer\Imap\Exception\ImapNumMsgException;
use Ddeboer\Imap\Exception\ImapStatusException; use Ddeboer\Imap\Exception\ImapStatusException;
use Ddeboer\Imap\Exception\InvalidSearchCriteriaException; use Ddeboer\Imap\Exception\InvalidSearchCriteriaException;
use Ddeboer\Imap\Exception\MessageCopyException; use Ddeboer\Imap\Exception\MessageCopyException;
use Ddeboer\Imap\Exception\MessageMoveException; use Ddeboer\Imap\Exception\MessageMoveException;
use Ddeboer\Imap\Exception\RenameMailboxException;
use Ddeboer\Imap\Search\ConditionInterface; use Ddeboer\Imap\Search\ConditionInterface;
use Ddeboer\Imap\Search\LogicalOperator\All; use Ddeboer\Imap\Search\LogicalOperator\All;
@@ -41,6 +41,23 @@ final class Mailbox implements MailboxInterface
return $this->name; return $this->name;
} }
public function renameTo(string $name): bool
{
$encodedName = \mb_convert_encoding($name, 'UTF7-IMAP', 'UTF-8');
$oldFullName = $this->getFullEncodedName();
$newFullName = \preg_replace('/' . \preg_quote(\mb_convert_encoding($this->name, 'UTF7-IMAP', 'UTF-8')) . '$/', $encodedName, $oldFullName);
\assert(null !== $newFullName);
$return = \imap_renamemailbox($this->resource->getStream(), $oldFullName, $newFullName);
if (false === $return) {
throw new RenameMailboxException('Could not rename mailbox');
}
$this->name = $name;
$this->info->name = $newFullName;
return true;
}
public function getEncodedName(): string public function getEncodedName(): string
{ {
/** @var string $name */ /** @var string $name */
@@ -104,10 +121,6 @@ final class Mailbox implements MailboxInterface
} }
$query = $search->toString(); $query = $search->toString();
if (\PHP_VERSION_ID < 80000) {
$descending = (int) $descending;
}
// We need to clear the stack to know whether imap_last_error() // We need to clear the stack to know whether imap_last_error()
// is related to this imap_search // is related to this imap_search
\imap_errors(); \imap_errors();
@@ -178,7 +191,7 @@ final class Mailbox implements MailboxInterface
return $this->getMessages(); return $this->getMessages();
} }
public function addMessage(string $message, string $options = null, DateTimeInterface $internalDate = null): bool public function addMessage(string $message, string $options = null, \DateTimeInterface $internalDate = null): bool
{ {
$arguments = [ $arguments = [
$this->resource->getStream(), $this->resource->getStream(),
@@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Ddeboer\Imap; namespace Ddeboer\Imap;
use DateTimeInterface;
use Ddeboer\Imap\Message\PartInterface; use Ddeboer\Imap\Message\PartInterface;
use Ddeboer\Imap\Search\ConditionInterface; use Ddeboer\Imap\Search\ConditionInterface;
@@ -20,6 +19,11 @@ interface MailboxInterface extends \Countable, \IteratorAggregate
*/ */
public function getName(): string; public function getName(): string;
/**
* Set new mailbox name.
*/
public function renameTo(string $name): bool;
/** /**
* Get mailbox encoded path. * Get mailbox encoded path.
*/ */
@@ -92,7 +96,7 @@ interface MailboxInterface extends \Countable, \IteratorAggregate
/** /**
* Add a message to the mailbox. * Add a message to the mailbox.
*/ */
public function addMessage(string $message, string $options = null, DateTimeInterface $internalDate = null): bool; public function addMessage(string $message, string $options = null, \DateTimeInterface $internalDate = null): bool;
/** /**
* Returns a tree of threaded message for the current Mailbox. * Returns a tree of threaded message for the current Mailbox.
+9 -4
View File
@@ -80,7 +80,6 @@ final class Message extends Message\AbstractMessage implements MessageInterface
} }
$this->messageNumberVerified = true; $this->messageNumberVerified = true;
$msgno = null;
\set_error_handler(static function (): bool { \set_error_handler(static function (): bool {
return true; return true;
}); });
@@ -89,7 +88,7 @@ final class Message extends Message\AbstractMessage implements MessageInterface
\restore_error_handler(); \restore_error_handler();
if (\is_numeric($msgno) && $msgno > 0) { if ($msgno > 0) {
$this->imapMsgNo = $msgno; $this->imapMsgNo = $msgno;
return; return;
@@ -133,6 +132,14 @@ final class Message extends Message\AbstractMessage implements MessageInterface
return $this->rawMessage; return $this->rawMessage;
} }
/**
* @param resource|string $file the path to the saved file as a string, or a valid file descriptor
*/
public function saveRawMessage($file): void
{
$this->doSaveContent($file, '');
}
public function getHeaders(): Message\Headers public function getHeaders(): Message\Headers
{ {
if (null === $this->headers) { if (null === $this->headers) {
@@ -198,8 +205,6 @@ final class Message extends Message\AbstractMessage implements MessageInterface
public function maskAsSeen(): bool public function maskAsSeen(): bool
{ {
\trigger_error(\sprintf('%s is deprecated and will be removed in 2.0. Use %s::markAsSeen instead.', __METHOD__, __CLASS__), \E_USER_DEPRECATED);
return $this->markAsSeen(); return $this->markAsSeen();
} }
@@ -203,45 +203,117 @@ abstract class AbstractMessage extends AbstractPart
} }
/** /**
* Get body HTML. * Get first body HTML part.
*/ */
final public function getBodyHtml(): ?string final public function getBodyHtml(): ?string
{
$htmlParts = $this->getAllContentsBySubtype(self::SUBTYPE_HTML);
return $htmlParts[0] ?? null;
}
/**
* Get all contents parts of specific subtype (self::SUBTYPE_HTML or self::SUBTYPE_PLAIN).
*
* @return string[]
*/
final public function getAllContentsBySubtype(string $subtype): array
{ {
$iterator = new \RecursiveIteratorIterator($this, \RecursiveIteratorIterator::SELF_FIRST); $iterator = new \RecursiveIteratorIterator($this, \RecursiveIteratorIterator::SELF_FIRST);
$parts = [];
/** @var PartInterface $part */
foreach ($iterator as $part) { foreach ($iterator as $part) {
if (self::SUBTYPE_HTML === $part->getSubtype()) { if ($subtype === $part->getSubtype()) {
return $part->getDecodedContent(); $parts[] = $part->getDecodedContent();
} }
} }
if (\count($parts) > 0) {
return $parts;
}
// If message has no parts and is HTML, return content of message itself. // If message has no parts and is of right type, return content of message.
if (self::SUBTYPE_HTML === $this->getSubtype()) { if ($subtype === $this->getSubtype()) {
return $this->getDecodedContent(); return [$this->getDecodedContent()];
} }
return [];
}
/**
* Get body HTML parts.
*
* @return string[]
*/
final public function getBodyHtmlParts(): array
{
return $this->getAllContentsBySubtype(self::SUBTYPE_HTML);
}
/**
* Get all body HTML parts merged into 1 html.
*/
final public function getCompleteBodyHtml(): ?string
{
$htmlParts = $this->getAllContentsBySubtype(self::SUBTYPE_HTML);
if (1 === \count($htmlParts)) {
return $htmlParts[0];
}
if (0 === \count($htmlParts)) {
return null; return null;
} }
\libxml_use_internal_errors(true); // Suppress parse errors, get errors with libxml_get_errors();
$newDom = new \DOMDocument();
$newBody = '';
$newDom->loadHTML(\implode('', $htmlParts));
$bodyTags = $newDom->getElementsByTagName('body');
foreach ($bodyTags as $body) {
foreach ($body->childNodes as $node) {
$newBody .= $newDom->saveHTML($node);
}
}
$newDom = new \DOMDocument();
$newDom->loadHTML($newBody);
$completeHtml = $newDom->saveHTML();
return false === $completeHtml ? null : $completeHtml;
}
/** /**
* Get body text. * Get body text.
*/ */
final public function getBodyText(): ?string final public function getBodyText(): ?string
{ {
$iterator = new \RecursiveIteratorIterator($this, \RecursiveIteratorIterator::SELF_FIRST); $plainParts = $this->getAllContentsBySubtype(self::SUBTYPE_PLAIN);
foreach ($iterator as $part) {
if (self::SUBTYPE_PLAIN === $part->getSubtype()) { return $plainParts[0] ?? null;
return $part->getDecodedContent();
}
} }
// If message has no parts, return content of message itself. /**
if (self::SUBTYPE_PLAIN === $this->getSubtype()) { * Get all body PLAIN parts merged into 1 string.
return $this->getDecodedContent(); *
} * @return null|string Null if message has no PLAIN message parts
*/
final public function getCompleteBodyText(): ?string
{
$plainParts = $this->getAllContentsBySubtype(self::SUBTYPE_PLAIN);
if (1 === \count($plainParts)) {
return $plainParts[0];
}
if (0 === \count($plainParts)) {
return null; return null;
} }
return \implode("\n", $plainParts);
}
/** /**
* Get attachments (if any) linked to this e-mail. * Get attachments (if any) linked to this e-mail.
* *
@@ -269,7 +341,7 @@ abstract class AbstractMessage extends AbstractPart
$attachments[] = $childPart; $attachments[] = $childPart;
} }
if ($childPart->hasChildren()) { if ($childPart->hasChildren()) {
$attachments = \array_merge($attachments, self::gatherAttachments($childPart)); $attachments = [...$attachments, ...self::gatherAttachments($childPart)];
} }
} }
@@ -56,8 +56,7 @@ abstract class AbstractPart implements PartInterface
private ?string $encoding = null; private ?string $encoding = null;
private ?string $disposition = null; private ?string $disposition = null;
private ?string $description = null; private ?string $description = null;
/** @var null|int|string */ private string|int|null $bytes;
private $bytes;
private ?string $lines = null; private ?string $lines = null;
private ?string $content = null; private ?string $content = null;
private ?string $decodedContent = null; private ?string $decodedContent = null;
@@ -256,6 +255,26 @@ abstract class AbstractPart implements PartInterface
return $return; return $return;
} }
/**
* Save raw message content to file.
*
* @param resource|string $file the path to the saved file as a string, or a valid file descriptor
*/
final protected function doSaveContent($file, string $partNumber): void
{
$return = \imap_savebody(
$this->resource->getStream(),
$file,
$this->getNumber(),
$partNumber,
\FT_UID | \FT_PEEK
);
if (false === $return) {
throw new ImapFetchbodyException('imap_savebody failed');
}
}
final public function getParts(): array final public function getParts(): array
{ {
$this->lazyParseStructure(); $this->lazyParseStructure();
@@ -265,8 +284,6 @@ abstract class AbstractPart implements PartInterface
/** /**
* Get current child part. * Get current child part.
*
* @return mixed
*/ */
#[\ReturnTypeWillChange] #[\ReturnTypeWillChange]
final public function current() final public function current()
@@ -360,7 +377,7 @@ abstract class AbstractPart implements PartInterface
// When the message is not multipart and the body is the attachment content // When the message is not multipart and the body is the attachment content
// Prevents infinite recursion // Prevents infinite recursion
if (self::isAttachment($this->structure) && !$this instanceof Attachment) { if (!$this instanceof Attachment && self::isAttachment($this->structure)) {
$this->parts[] = new Attachment($this->resource, $this->getNumber(), '1', $this->structure); $this->parts[] = new Attachment($this->resource, $this->getNumber(), '1', $this->structure);
} }
@@ -372,12 +389,11 @@ abstract class AbstractPart implements PartInterface
} }
foreach ($parts as $key => $partStructure) { foreach ($parts as $key => $partStructure) {
$partNumber = (!$this instanceof Message) ? $this->partNumber . '.' : ''; $partNumber = (!$this instanceof Message) ? $this->partNumber . '.' : '';
$partNumber .= (string) ($key + 1); $partNumber .= $key + 1;
$newPartClass = self::isAttachment($partStructure) $newPartClass = self::isAttachment($partStructure)
? Attachment::class ? Attachment::class
: SimplePart::class : SimplePart::class;
;
$this->parts[] = new $newPartClass($this->resource, $this->getNumber(), $partNumber, $partStructure); $this->parts[] = new $newPartClass($this->resource, $this->getNumber(), $partNumber, $partStructure);
} }
@@ -427,10 +443,6 @@ abstract class AbstractPart implements PartInterface
} }
*/ */
if (self::SUBTYPE_RFC822 === \strtoupper($part->subtype)) { return self::SUBTYPE_RFC822 === \strtoupper($part->subtype);
return true;
}
return false;
} }
} }
@@ -18,6 +18,13 @@ interface BasicMessageInterface extends PartInterface
*/ */
public function getRawMessage(): string; public function getRawMessage(): string;
/**
* Save the raw message, including all headers, parts, etc. unencoded and unparsed to file.
*
* @param resource|string $file the path to the saved file as a string, or a valid file descriptor
*/
public function saveRawMessage($file): void;
/** /**
* Get message headers. * Get message headers.
*/ */
@@ -109,17 +116,45 @@ interface BasicMessageInterface extends PartInterface
public function getReferences(): array; public function getReferences(): array;
/** /**
* Get body HTML. * Get message parts by type.
*
* @return string[]
*/
public function getAllContentsBySubtype(string $subtype): array;
/**
* Get first body HTML part.
* *
* @return null|string Null if message has no HTML message part * @return null|string Null if message has no HTML message part
*/ */
public function getBodyHtml(): ?string; public function getBodyHtml(): ?string;
/**
* Get all body HTML parts as array.
*
* @return string[]
*/
public function getBodyHtmlParts(): array;
/**
* Get all body HTML parts merged into 1 html.
*
* @return null|string Null if message has no HTML message parts
*/
public function getCompleteBodyHtml(): ?string;
/** /**
* Get body text. * Get body text.
*/ */
public function getBodyText(): ?string; public function getBodyText(): ?string;
/**
* Get all body PLAIN parts merged into 1 string.
*
* @return null|string Null if message has no PLAIN message parts
*/
public function getCompleteBodyText(): ?string;
/** /**
* Get attachments (if any) linked to this e-mail. * Get attachments (if any) linked to this e-mail.
* *
@@ -38,6 +38,14 @@ final class EmbeddedMessage extends AbstractMessage implements EmbeddedMessageIn
return $this->rawMessage; return $this->rawMessage;
} }
/**
* @param resource|string $file the path to the saved file as a string, or a valid file descriptor
*/
public function saveRawMessage($file): void
{
$this->doSaveContent($file, $this->getPartNumber());
}
/** /**
* Get content part number. * Get content part number.
*/ */
@@ -4,6 +4,8 @@ declare(strict_types=1);
namespace Ddeboer\Imap\Message; namespace Ddeboer\Imap\Message;
use Ddeboer\Imap\Exception\UnsupportedCharsetException;
final class Headers extends Parameters final class Headers extends Parameters
{ {
public function __construct(\stdClass $headers) public function __construct(\stdClass $headers)
@@ -14,7 +16,11 @@ final class Headers extends Parameters
$headers = \array_change_key_case((array) $headers); $headers = \array_change_key_case((array) $headers);
foreach ($headers as $key => $value) { foreach ($headers as $key => $value) {
try {
$this[$key] = $this->parseHeader($key, $value); $this[$key] = $this->parseHeader($key, $value);
} catch (UnsupportedCharsetException) {
// safely skip header with unsupported charset
}
} }
} }
@@ -50,7 +56,6 @@ final class Headers extends Parameters
case 'sender': case 'sender':
case 'return_path': case 'return_path':
\assert(\is_array($value)); \assert(\is_array($value));
/** @var \stdClass $address */
foreach ($value as $address) { foreach ($value as $address) {
if (isset($address->mailbox)) { if (isset($address->mailbox)) {
$address->host = $address->host ?? null; $address->host = $address->host ?? null;
@@ -39,8 +39,8 @@ final class MessageIterator extends \ArrayIterator implements MessageIteratorInt
'The current value "%s" isn\'t an integer and doesn\'t represent a message;' 'The current value "%s" isn\'t an integer and doesn\'t represent a message;'
. ' try to cycle this "%s" with a native php function like foreach or with the method getArrayCopy(),' . ' try to cycle this "%s" with a native php function like foreach or with the method getArrayCopy(),'
. ' or check it by calling the methods valid().', . ' or check it by calling the methods valid().',
\is_object($current) ? \get_class($current) : \gettype($current), \get_debug_type($current),
static::class self::class
)); ));
} }
@@ -4,8 +4,6 @@ declare(strict_types=1);
namespace Ddeboer\Imap\Search; namespace Ddeboer\Imap\Search;
use DateTimeInterface;
/** /**
* Represents a date condition. * Represents a date condition.
*/ */
@@ -19,14 +17,14 @@ abstract class AbstractDate implements ConditionInterface
/** /**
* The date to be used for the condition. * The date to be used for the condition.
*/ */
private DateTimeInterface $date; private \DateTimeInterface $date;
/** /**
* Constructor. * Constructor.
* *
* @param DateTimeInterface $date optional date for the condition * @param \DateTimeInterface $date optional date for the condition
*/ */
public function __construct(DateTimeInterface $date, string $dateFormat = 'j-M-Y') public function __construct(\DateTimeInterface $date, string $dateFormat = 'j-M-Y')
{ {
$this->date = $date; $this->date = $date;
$this->dateFormat = $dateFormat; $this->dateFormat = $dateFormat;
@@ -36,8 +36,8 @@
"psr/log": "*", "psr/log": "*",
"nesbot/carbon": "*", "nesbot/carbon": "*",
"psr/simple-cache": "^1.0|^2.0|^3.0", "psr/simple-cache": "^1.0|^2.0|^3.0",
"illuminate/contracts": "^8.0|^9.0|^10.0|^11.0|^12.0", "illuminate/contracts": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0",
"illuminate/collections": "^8.0|^9.0|^10.0|^11.0|^12.0" "illuminate/collections": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^9.0", "phpunit/phpunit": "^9.0",
@@ -3,7 +3,9 @@
namespace LdapRecord\Auth; namespace LdapRecord\Auth;
use Exception; use Exception;
use LdapRecord\Configuration\ConfigurationException;
use LdapRecord\Configuration\DomainConfiguration; use LdapRecord\Configuration\DomainConfiguration;
use LdapRecord\ConnectionException;
use LdapRecord\Events\DispatcherInterface; use LdapRecord\Events\DispatcherInterface;
use LdapRecord\LdapInterface; use LdapRecord\LdapInterface;
@@ -71,7 +73,7 @@ class Guard
* Attempt binding a user to the LDAP server. Supports sasl and anonymous binding. * Attempt binding a user to the LDAP server. Supports sasl and anonymous binding.
* *
* @throws BindException * @throws BindException
* @throws \LdapRecord\ConnectionException * @throws ConnectionException
*/ */
public function bind(?string $username = null, ?string $password = null): void public function bind(?string $username = null, ?string $password = null): void
{ {
@@ -102,7 +104,7 @@ class Guard
/** /**
* Authenticate by binding to the LDAP server. * Authenticate by binding to the LDAP server.
* *
* @throws \LdapRecord\ConnectionException * @throws ConnectionException
*/ */
protected function authenticate(?string $username = null, ?string $password = null): bool protected function authenticate(?string $username = null, ?string $password = null): bool
{ {
@@ -119,8 +121,8 @@ class Guard
* Bind to the LDAP server using the configured username and password. * Bind to the LDAP server using the configured username and password.
* *
* @throws BindException * @throws BindException
* @throws \LdapRecord\ConnectionException * @throws ConnectionException
* @throws \LdapRecord\Configuration\ConfigurationException * @throws ConfigurationException
*/ */
public function bindAsConfiguredUser(): void public function bindAsConfiguredUser(): void
{ {
@@ -89,7 +89,7 @@ class Connection
/** /**
* Set the connection configuration. * Set the connection configuration.
* *
* @throws Configuration\ConfigurationException * @throws ConfigurationException
*/ */
public function setConfiguration(DomainConfiguration|array $config = []): void public function setConfiguration(DomainConfiguration|array $config = []): void
{ {
@@ -3,6 +3,8 @@
namespace LdapRecord; namespace LdapRecord;
use LDAP\Connection as RawLdapConnection; use LDAP\Connection as RawLdapConnection;
use Ldap\Result;
use Ldap\ResultEntry;
class Ldap implements LdapInterface class Ldap implements LdapInterface
{ {
@@ -24,7 +26,7 @@ class Ldap implements LdapInterface
* *
* @see http://php.net/manual/en/function.ldap-first-entry.php * @see http://php.net/manual/en/function.ldap-first-entry.php
* *
* @param \Ldap\Result $result * @param Result $result
*/ */
public function getFirstEntry(mixed $result): mixed public function getFirstEntry(mixed $result): mixed
{ {
@@ -38,7 +40,7 @@ class Ldap implements LdapInterface
* *
* @see http://php.net/manual/en/function.ldap-next-entry.php * @see http://php.net/manual/en/function.ldap-next-entry.php
* *
* @param \Ldap\ResultEntry $entry * @param ResultEntry $entry
*/ */
public function getNextEntry(mixed $entry): mixed public function getNextEntry(mixed $entry): mixed
{ {
@@ -52,7 +54,7 @@ class Ldap implements LdapInterface
* *
* @see http://php.net/manual/en/function.ldap-get-attributes.php * @see http://php.net/manual/en/function.ldap-get-attributes.php
* *
* @param \Ldap\ResultEntry $entry * @param ResultEntry $entry
*/ */
public function getAttributes(mixed $entry): array|false public function getAttributes(mixed $entry): array|false
{ {
@@ -3,6 +3,7 @@
namespace LdapRecord; namespace LdapRecord;
use LDAP\Connection; use LDAP\Connection;
use LDAP\Result;
/** /**
* @see https://ldap.com/ldap-oid-reference-guide * @see https://ldap.com/ldap-oid-reference-guide
@@ -339,7 +340,7 @@ interface LdapInterface
* *
* @see http://php.net/manual/en/function.ldap-get-entries.php * @see http://php.net/manual/en/function.ldap-get-entries.php
* *
* @param \LDAP\Result $result * @param Result $result
*/ */
public function getEntries(mixed $result): array; public function getEntries(mixed $result): array;
@@ -348,7 +349,7 @@ interface LdapInterface
* *
* @see https://www.php.net/manual/en/function.ldap-first-entry.php * @see https://www.php.net/manual/en/function.ldap-first-entry.php
* *
* @param \LDAP\Result $result * @param Result $result
*/ */
public function getFirstEntry(mixed $result): mixed; public function getFirstEntry(mixed $result): mixed;
@@ -357,7 +358,7 @@ interface LdapInterface
* *
* @see https://www.php.net/manual/en/function.ldap-next-entry.php * @see https://www.php.net/manual/en/function.ldap-next-entry.php
* *
* @param \LDAP\Result $entry * @param Result $entry
*/ */
public function getNextEntry(mixed $entry): mixed; public function getNextEntry(mixed $entry): mixed;
@@ -366,14 +367,14 @@ interface LdapInterface
* *
* @see https://www.php.net/manual/en/function.ldap-get-attributes.php * @see https://www.php.net/manual/en/function.ldap-get-attributes.php
* *
* @param \LDAP\Result $entry * @param Result $entry
*/ */
public function getAttributes(mixed $entry): array|false; public function getAttributes(mixed $entry): array|false;
/** /**
* Reads all the values of the attribute in the entry in the result. * Reads all the values of the attribute in the entry in the result.
* *
* @param \LDAP\Result $entry * @param Result $entry
*/ */
public function getValuesLen(mixed $entry, string $attribute): array|false; public function getValuesLen(mixed $entry, string $attribute): array|false;
@@ -398,7 +399,7 @@ interface LdapInterface
* *
* @see https://www.php.net/manual/en/function.ldap-count-entries.php * @see https://www.php.net/manual/en/function.ldap-count-entries.php
* *
* @param \LDAP\Result $result * @param Result $result
*/ */
public function countEntries(mixed $result): int; public function countEntries(mixed $result): int;
@@ -463,7 +464,7 @@ interface LdapInterface
* *
* @see http://php.net/manual/en/function.ldap-search.php * @see http://php.net/manual/en/function.ldap-search.php
* *
* @return \LDAP\Result * @return Result
*/ */
public function search(string $dn, string $filter, array $fields, bool $onlyAttributes = false, int $size = 0, int $time = 0, int $deref = LDAP_DEREF_NEVER, ?array $controls = null): mixed; public function search(string $dn, string $filter, array $fields, bool $onlyAttributes = false, int $size = 0, int $time = 0, int $deref = LDAP_DEREF_NEVER, ?array $controls = null): mixed;
@@ -472,7 +473,7 @@ interface LdapInterface
* *
* @see http://php.net/manual/en/function.ldap-list.php * @see http://php.net/manual/en/function.ldap-list.php
* *
* @return \LDAP\Result * @return Result
*/ */
public function list(string $dn, string $filter, array $fields, bool $onlyAttributes = false, int $size = 0, int $time = 0, int $deref = LDAP_DEREF_NEVER, ?array $controls = null): mixed; public function list(string $dn, string $filter, array $fields, bool $onlyAttributes = false, int $size = 0, int $time = 0, int $deref = LDAP_DEREF_NEVER, ?array $controls = null): mixed;
@@ -481,7 +482,7 @@ interface LdapInterface
* *
* @see http://php.net/manual/en/function.ldap-read.php * @see http://php.net/manual/en/function.ldap-read.php
* *
* @return \LDAP\Result * @return Result
*/ */
public function read(string $dn, string $filter, array $fields, bool $onlyAttributes = false, int $size = 0, int $time = 0, int $deref = LDAP_DEREF_NEVER, ?array $controls = null): mixed; public function read(string $dn, string $filter, array $fields, bool $onlyAttributes = false, int $size = 0, int $time = 0, int $deref = LDAP_DEREF_NEVER, ?array $controls = null): mixed;
@@ -490,7 +491,7 @@ interface LdapInterface
* *
* @see https://www.php.net/manual/en/function.ldap-parse-result.php * @see https://www.php.net/manual/en/function.ldap-parse-result.php
* *
* @param \LDAP\Result $result * @param Result $result
*/ */
public function parseResult(mixed $result, int &$errorCode = 0, ?string &$dn = null, ?string &$errorMessage = null, ?array &$referrals = null, ?array &$controls = null): LdapResultResponse|false; public function parseResult(mixed $result, int &$errorCode = 0, ?string &$dn = null, ?string &$errorMessage = null, ?array &$referrals = null, ?array &$controls = null): LdapResultResponse|false;
@@ -595,7 +596,7 @@ interface LdapInterface
* *
* @see https://www.php.net/manual/en/function.ldap-free-result.php * @see https://www.php.net/manual/en/function.ldap-free-result.php
* *
* @param \LDAP\Result $result * @param Result $result
*/ */
public function freeResult(mixed $result): bool; public function freeResult(mixed $result): bool;
@@ -4,6 +4,7 @@ namespace LdapRecord\Models\ActiveDirectory;
use InvalidArgumentException; use InvalidArgumentException;
use LdapRecord\Connection; use LdapRecord\Connection;
use LdapRecord\LdapRecordException;
use LdapRecord\Models\Attributes\Sid; use LdapRecord\Models\Attributes\Sid;
use LdapRecord\Models\Entry as BaseEntry; use LdapRecord\Models\Entry as BaseEntry;
use LdapRecord\Models\Events\Updated; use LdapRecord\Models\Events\Updated;
@@ -99,7 +100,7 @@ class Entry extends BaseEntry implements ActiveDirectory
/** /**
* Restore a deleted object. * Restore a deleted object.
* *
* @throws \LdapRecord\LdapRecordException * @throws LdapRecordException
*/ */
public function restore(?string $newParentDn = null): bool public function restore(?string $newParentDn = null): bool
{ {
@@ -4,6 +4,7 @@ namespace LdapRecord\Models\ActiveDirectory\Scopes;
use LdapRecord\Models\ActiveDirectory\Entry; use LdapRecord\Models\ActiveDirectory\Entry;
use LdapRecord\Models\Model; use LdapRecord\Models\Model;
use LdapRecord\Models\ModelNotFoundException;
use LdapRecord\Models\Scope; use LdapRecord\Models\Scope;
use LdapRecord\Query\Model\Builder; use LdapRecord\Query\Model\Builder;
@@ -12,7 +13,7 @@ class InConfigurationContext implements Scope
/** /**
* Refines the base dn to be inside the configuration context. * Refines the base dn to be inside the configuration context.
* *
* @throws \LdapRecord\Models\ModelNotFoundException * @throws ModelNotFoundException
*/ */
public function apply(Builder $query, Model $model): void public function apply(Builder $query, Model $model): void
{ {
@@ -22,7 +23,7 @@ class InConfigurationContext implements Scope
/** /**
* Get the LDAP server configuration naming context distinguished name. * Get the LDAP server configuration naming context distinguished name.
* *
* @throws \LdapRecord\Models\ModelNotFoundException * @throws ModelNotFoundException
*/ */
protected function getConfigurationNamingContext(Model $model) protected function getConfigurationNamingContext(Model $model)
{ {
@@ -2,7 +2,9 @@
namespace LdapRecord\Models\Concerns; namespace LdapRecord\Models\Concerns;
/** @mixin \LdapRecord\Models\Model */ use LdapRecord\Models\Model;
/** @mixin Model */
trait CanAuthenticate trait CanAuthenticate
{ {
/** /**
@@ -6,9 +6,10 @@ use Closure;
use LdapRecord\Events\NullDispatcher; use LdapRecord\Events\NullDispatcher;
use LdapRecord\Models\Events; use LdapRecord\Models\Events;
use LdapRecord\Models\Events\Event; use LdapRecord\Models\Events\Event;
use LdapRecord\Models\Model;
use LdapRecord\Support\Arr; use LdapRecord\Support\Arr;
/** @mixin \LdapRecord\Models\Model */ /** @mixin Model */
trait HasEvents trait HasEvents
{ {
/** /**
@@ -4,9 +4,10 @@ namespace LdapRecord\Models\Concerns;
use Closure; use Closure;
use InvalidArgumentException; use InvalidArgumentException;
use LdapRecord\Models\Model;
use LdapRecord\Models\Scope; use LdapRecord\Models\Scope;
/** @mixin \LdapRecord\Models\Model */ /** @mixin Model */
trait HasGlobalScopes trait HasGlobalScopes
{ {
/** /**
@@ -5,8 +5,9 @@ namespace LdapRecord\Models\Concerns;
use LdapRecord\ConnectionException; use LdapRecord\ConnectionException;
use LdapRecord\LdapRecordException; use LdapRecord\LdapRecordException;
use LdapRecord\Models\Attributes\Password; use LdapRecord\Models\Attributes\Password;
use LdapRecord\Models\Model;
/** @mixin \LdapRecord\Models\Model */ /** @mixin Model */
trait HasPassword trait HasPassword
{ {
/** /**
@@ -2,9 +2,10 @@
namespace LdapRecord\Models\Concerns; namespace LdapRecord\Models\Concerns;
use LdapRecord\Models\Model;
use LdapRecord\Query\Model\Builder; use LdapRecord\Query\Model\Builder;
/** @mixin \LdapRecord\Models\Model */ /** @mixin Model */
trait HasScopes trait HasScopes
{ {
/** /**
@@ -2,12 +2,14 @@
namespace LdapRecord\Models\Concerns; namespace LdapRecord\Models\Concerns;
use LdapRecord\Models\Model;
/** /**
* @author Taylor Otwell * @author Taylor Otwell
* *
* @see https://laravel.com * @see https://laravel.com
* *
* @mixin \LdapRecord\Models\Model * @mixin Model
*/ */
trait HidesAttributes trait HidesAttributes
{ {
@@ -9,6 +9,7 @@ use JsonSerializable;
use LdapRecord\Connection; use LdapRecord\Connection;
use LdapRecord\Container; use LdapRecord\Container;
use LdapRecord\EscapesValues; use LdapRecord\EscapesValues;
use LdapRecord\LdapRecordException;
use LdapRecord\Models\Attributes\DistinguishedName; use LdapRecord\Models\Attributes\DistinguishedName;
use LdapRecord\Models\Attributes\Guid; use LdapRecord\Models\Attributes\Guid;
use LdapRecord\Query\Builder as BaseBuilder; use LdapRecord\Query\Builder as BaseBuilder;
@@ -246,7 +247,7 @@ abstract class Model implements Arrayable, ArrayAccess, JsonSerializable, String
/** /**
* Get the RootDSE (AD schema) record from the directory. * Get the RootDSE (AD schema) record from the directory.
* *
* @throws \LdapRecord\Models\ModelNotFoundException * @throws ModelNotFoundException
*/ */
public static function getRootDse(?string $connection = null): Model public static function getRootDse(?string $connection = null): Model
{ {
@@ -919,7 +920,7 @@ abstract class Model implements Arrayable, ArrayAccess, JsonSerializable, String
/** /**
* Save the model to the directory without raising any events. * Save the model to the directory without raising any events.
* *
* @throws \LdapRecord\LdapRecordException * @throws LdapRecordException
*/ */
public function saveQuietly(array $attributes = []): void public function saveQuietly(array $attributes = []): void
{ {
@@ -931,7 +932,7 @@ abstract class Model implements Arrayable, ArrayAccess, JsonSerializable, String
/** /**
* Save the model to the directory. * Save the model to the directory.
* *
* @throws \LdapRecord\LdapRecordException * @throws LdapRecordException
*/ */
public function save(array $attributes = []): void public function save(array $attributes = []): void
{ {
@@ -951,7 +952,7 @@ abstract class Model implements Arrayable, ArrayAccess, JsonSerializable, String
/** /**
* Inserts the model into the directory. * Inserts the model into the directory.
* *
* @throws \LdapRecord\LdapRecordException * @throws LdapRecordException
*/ */
protected function performInsert(): void protected function performInsert(): void
{ {
@@ -998,7 +999,7 @@ abstract class Model implements Arrayable, ArrayAccess, JsonSerializable, String
/** /**
* Updates the model in the directory. * Updates the model in the directory.
* *
* @throws \LdapRecord\LdapRecordException * @throws LdapRecordException
*/ */
protected function performUpdate(): void protected function performUpdate(): void
{ {
@@ -1020,7 +1021,7 @@ abstract class Model implements Arrayable, ArrayAccess, JsonSerializable, String
/** /**
* Create the model in the directory. * Create the model in the directory.
* *
* @throws \LdapRecord\LdapRecordException * @throws LdapRecordException
*/ */
public static function create(array $attributes = []): static public static function create(array $attributes = []): static
{ {
@@ -1035,7 +1036,7 @@ abstract class Model implements Arrayable, ArrayAccess, JsonSerializable, String
* Add an attribute on the model with the given value. * Add an attribute on the model with the given value.
* *
* @throws ModelDoesNotExistException * @throws ModelDoesNotExistException
* @throws \LdapRecord\LdapRecordException * @throws LdapRecordException
*/ */
public function addAttribute(string $attribute, mixed $value): void public function addAttribute(string $attribute, mixed $value): void
{ {
@@ -1054,7 +1055,7 @@ abstract class Model implements Arrayable, ArrayAccess, JsonSerializable, String
* Update the model. * Update the model.
* *
* @throws ModelDoesNotExistException * @throws ModelDoesNotExistException
* @throws \LdapRecord\LdapRecordException * @throws LdapRecordException
*/ */
public function update(array $attributes = []): void public function update(array $attributes = []): void
{ {
@@ -1067,7 +1068,7 @@ abstract class Model implements Arrayable, ArrayAccess, JsonSerializable, String
* Update the model attribute with the specified value. * Update the model attribute with the specified value.
* *
* @throws ModelDoesNotExistException * @throws ModelDoesNotExistException
* @throws \LdapRecord\LdapRecordException * @throws LdapRecordException
*/ */
public function replaceAttribute(string $attribute, mixed $value): void public function replaceAttribute(string $attribute, mixed $value): void
{ {
@@ -1085,7 +1086,7 @@ abstract class Model implements Arrayable, ArrayAccess, JsonSerializable, String
/** /**
* Destroy the models for the given distinguished names. * Destroy the models for the given distinguished names.
* *
* @throws \LdapRecord\LdapRecordException * @throws LdapRecordException
*/ */
public static function destroy(mixed $dns, bool $recursive = false): int public static function destroy(mixed $dns, bool $recursive = false): int
{ {
@@ -1121,7 +1122,7 @@ abstract class Model implements Arrayable, ArrayAccess, JsonSerializable, String
* not exist or does not contain a distinguished name. * not exist or does not contain a distinguished name.
* *
* @throws ModelDoesNotExistException * @throws ModelDoesNotExistException
* @throws \LdapRecord\LdapRecordException * @throws LdapRecordException
*/ */
public function delete(bool $recursive = false): void public function delete(bool $recursive = false): void
{ {
@@ -1146,7 +1147,7 @@ abstract class Model implements Arrayable, ArrayAccess, JsonSerializable, String
/** /**
* Deletes leaf nodes that are attached to the model. * Deletes leaf nodes that are attached to the model.
* *
* @throws \LdapRecord\LdapRecordException * @throws LdapRecordException
*/ */
protected function deleteLeafNodes(): void protected function deleteLeafNodes(): void
{ {
@@ -1162,7 +1163,7 @@ abstract class Model implements Arrayable, ArrayAccess, JsonSerializable, String
* Remove an attribute on the model. * Remove an attribute on the model.
* *
* @throws ModelDoesNotExistException * @throws ModelDoesNotExistException
* @throws \LdapRecord\LdapRecordException * @throws LdapRecordException
*/ */
public function removeAttribute(string $attribute, mixed $value = null): void public function removeAttribute(string $attribute, mixed $value = null): void
{ {
@@ -1181,7 +1182,7 @@ abstract class Model implements Arrayable, ArrayAccess, JsonSerializable, String
* ["memberuid" => []] * ["memberuid" => []]
* *
* @throws ModelDoesNotExistException * @throws ModelDoesNotExistException
* @throws \LdapRecord\LdapRecordException * @throws LdapRecordException
*/ */
public function removeAttributes(array|string $attributes): void public function removeAttributes(array|string $attributes): void
{ {
@@ -1238,7 +1239,7 @@ abstract class Model implements Arrayable, ArrayAccess, JsonSerializable, String
* *
* @throws UnexpectedValueException * @throws UnexpectedValueException
* @throws ModelDoesNotExistException * @throws ModelDoesNotExistException
* @throws \LdapRecord\LdapRecordException * @throws LdapRecordException
*/ */
public function move(Model|string $newParentDn, bool $deleteOldRdn = true): void public function move(Model|string $newParentDn, bool $deleteOldRdn = true): void
{ {
@@ -1255,7 +1256,7 @@ abstract class Model implements Arrayable, ArrayAccess, JsonSerializable, String
* Rename the model to a new RDN and new parent. * Rename the model to a new RDN and new parent.
* *
* @throws ModelDoesNotExistException * @throws ModelDoesNotExistException
* @throws \LdapRecord\LdapRecordException * @throws LdapRecordException
*/ */
public function rename(string $rdn, Model|string|null $newParentDn = null, bool $deleteOldRdn = true): void public function rename(string $rdn, Model|string|null $newParentDn = null, bool $deleteOldRdn = true): void
{ {
@@ -2,21 +2,23 @@
namespace LdapRecord\Testing; namespace LdapRecord\Testing;
use LdapRecord\Connection;
use LdapRecord\Container; use LdapRecord\Container;
use LdapRecord\ContainerException;
class DirectoryFake class DirectoryFake
{ {
/** /**
* The LDAP connections that were replaced with fakes. * The LDAP connections that were replaced with fakes.
* *
* @var \LdapRecord\Connection[] * @var Connection[]
*/ */
protected static array $replaced = []; protected static array $replaced = [];
/** /**
* Replace a connection a fake. * Replace a connection a fake.
* *
* @throws \LdapRecord\ContainerException * @throws ContainerException
*/ */
public static function setup(?string $name = null): ConnectionFake public static function setup(?string $name = null): ConnectionFake
{ {
+155
View File
@@ -1,5 +1,160 @@
# Changelog # Changelog
## [7.1.1](https://github.com/googleapis/php-jwt/compare/v7.1.0...v7.1.1) (2026-09-02)
### Bug Fixes
* Limit the number of segments a JWT can be exploded into ([#639](https://github.com/googleapis/php-jwt/issues/639)) ([93d248c](https://github.com/googleapis/php-jwt/commit/93d248c650aea615d74a5ffa20c91c7a818e9b8d))
## [7.1.0](https://github.com/googleapis/php-jwt/compare/v7.0.5...v7.1.0) (2026-06-11)
### Features
* Add support for PS256 ([#637](https://github.com/googleapis/php-jwt/issues/637)) ([aa0b447](https://github.com/googleapis/php-jwt/commit/aa0b4472c137255545b177a1840891304eac55f1))
### Bug Fixes
* Skip parsing valid unsupported curves ([#638](https://github.com/googleapis/php-jwt/issues/638)) ([8a6c2a7](https://github.com/googleapis/php-jwt/commit/8a6c2a75d7f45e781c93d618939f8a045fe69f53))
* Update homepage URL in composer.json ([#635](https://github.com/googleapis/php-jwt/issues/635)) ([f7ae571](https://github.com/googleapis/php-jwt/commit/f7ae571efe255aed89c8f8d0f2cb7f71c5835664))
* Validate numeric type of iat, nbf and exp claims in encode ([#634](https://github.com/googleapis/php-jwt/issues/634)) ([958e422](https://github.com/googleapis/php-jwt/commit/958e422961e874843a7fcc339e55a0101a66201d))
## [7.0.5](https://github.com/firebase/php-jwt/compare/v7.0.4...v7.0.5) (2026-03-31)
### Bug Fixes
* RSA from JWK sometimes returns empty Instance ([#628](https://github.com/firebase/php-jwt/issues/628)) ([b4c78aa](https://github.com/firebase/php-jwt/commit/b4c78aa731664122198ad36c0033aa29e807397a))
## [7.0.4](https://github.com/firebase/php-jwt/compare/v7.0.3...v7.0.4) (2026-03-27)
### Bug Fixes
* readme examples, add tests for all examples ([#626](https://github.com/firebase/php-jwt/issues/626)) ([510a00c](https://github.com/firebase/php-jwt/commit/510a00c0e6353bc7d68412fab67e57a13954cb46))
* use urlsafeB64Decode everywhere ([#627](https://github.com/firebase/php-jwt/issues/627)) ([b889495](https://github.com/firebase/php-jwt/commit/b889495c83ddc3f3885ca3f0b65b41b1cb37a3b1))
## [7.0.3](https://github.com/firebase/php-jwt/compare/v7.0.2...v7.0.3) (2026-02-18)
### Miscellaneous Chores
* add environment for Release Please job ([#619](https://github.com/firebase/php-jwt/issues/619)) ([300fd02](https://github.com/firebase/php-jwt/commit/300fd02c883f096c9067df652dbd23f62cb5e2a7))
## [7.0.2](https://github.com/firebase/php-jwt/compare/v7.0.1...v7.0.2) (2025-12-16)
### Bug Fixes
* add key length validation for ec keys ([#615](https://github.com/firebase/php-jwt/issues/615)) ([7044f9a](https://github.com/firebase/php-jwt/commit/7044f9ae7e7d175d28cca71714feb236f1c0e252))
## [7.0.0](https://github.com/firebase/php-jwt/compare/v6.11.1...v7.0.0) (2025-12-15)
### ⚠️ ⚠️ ⚠️ Security Fixes ⚠️ ⚠️ ⚠️
* add key size validation ([#613](https://github.com/firebase/php-jwt/issues/613)) ([6b80341](https://github.com/firebase/php-jwt/commit/6b80341bf57838ea2d011487917337901cd71576))
**NOTE**: This fix will cause keys with a size below the minimally allowed size to break.
### Features
* add SensitiveParameter attribute to security-critical parameters ([#603](https://github.com/firebase/php-jwt/issues/603)) ([4dbfac0](https://github.com/firebase/php-jwt/commit/4dbfac0260eeb0e9e643063c99998e3219cc539b))
* store timestamp in `ExpiredException` ([#604](https://github.com/firebase/php-jwt/issues/604)) ([f174826](https://github.com/firebase/php-jwt/commit/f1748260d218a856b6a0c23715ac7fae1d7ca95b))
### Bug Fixes
* validate iat and nbf on payload ([#568](https://github.com/firebase/php-jwt/issues/568)) ([953b2c8](https://github.com/firebase/php-jwt/commit/953b2c88bb445b7e3bb82a5141928f13d7343afd))
## [6.11.1](https://github.com/firebase/php-jwt/compare/v6.11.0...v6.11.1) (2025-04-09)
### Bug Fixes
* update error text for consistency ([#528](https://github.com/firebase/php-jwt/issues/528)) ([c11113a](https://github.com/firebase/php-jwt/commit/c11113afa13265e016a669e75494b9203b8a7775))
## [6.11.0](https://github.com/firebase/php-jwt/compare/v6.10.2...v6.11.0) (2025-01-23)
### Features
* support octet typed JWK ([#587](https://github.com/firebase/php-jwt/issues/587)) ([7cb8a26](https://github.com/firebase/php-jwt/commit/7cb8a265fa81edf2fa6ef8098f5bc5ae573c33ad))
### Bug Fixes
* refactor constructor Key to use PHP 8.0 syntax ([#577](https://github.com/firebase/php-jwt/issues/577)) ([29fa2ce](https://github.com/firebase/php-jwt/commit/29fa2ce9e0582cd397711eec1e80c05ce20fabca))
## [6.10.2](https://github.com/firebase/php-jwt/compare/v6.10.1...v6.10.2) (2024-11-24)
### Bug Fixes
* Mitigate PHP8.4 deprecation warnings ([#570](https://github.com/firebase/php-jwt/issues/570)) ([76808fa](https://github.com/firebase/php-jwt/commit/76808fa227f3811aa5cdb3bf81233714b799a5b5))
* support php 8.4 ([#583](https://github.com/firebase/php-jwt/issues/583)) ([e3d68b0](https://github.com/firebase/php-jwt/commit/e3d68b044421339443c74199edd020e03fb1887e))
## [6.10.1](https://github.com/firebase/php-jwt/compare/v6.10.0...v6.10.1) (2024-05-18)
### Bug Fixes
* ensure ratelimit expiry is set every time ([#556](https://github.com/firebase/php-jwt/issues/556)) ([09cb208](https://github.com/firebase/php-jwt/commit/09cb2081c2c3bc0f61e2f2a5fbea5741f7498648))
* ratelimit cache expiration ([#550](https://github.com/firebase/php-jwt/issues/550)) ([dda7250](https://github.com/firebase/php-jwt/commit/dda725033585ece30ff8cae8937320d7e9f18bae))
## [6.10.0](https://github.com/firebase/php-jwt/compare/v6.9.0...v6.10.0) (2023-11-28)
### Features
* allow typ header override ([#546](https://github.com/firebase/php-jwt/issues/546)) ([79cb30b](https://github.com/firebase/php-jwt/commit/79cb30b729a22931b2fbd6b53f20629a83031ba9))
## [6.9.0](https://github.com/firebase/php-jwt/compare/v6.8.1...v6.9.0) (2023-10-04)
### Features
* add payload to jwt exception ([#521](https://github.com/firebase/php-jwt/issues/521)) ([175edf9](https://github.com/firebase/php-jwt/commit/175edf958bb61922ec135b2333acf5622f2238a2))
## [6.8.1](https://github.com/firebase/php-jwt/compare/v6.8.0...v6.8.1) (2023-07-14)
### Bug Fixes
* accept float claims but round down to ignore them ([#492](https://github.com/firebase/php-jwt/issues/492)) ([3936842](https://github.com/firebase/php-jwt/commit/39368423beeaacb3002afa7dcb75baebf204fe7e))
* different BeforeValidException messages for nbf and iat ([#526](https://github.com/firebase/php-jwt/issues/526)) ([0a53cf2](https://github.com/firebase/php-jwt/commit/0a53cf2986e45c2bcbf1a269f313ebf56a154ee4))
## [6.8.0](https://github.com/firebase/php-jwt/compare/v6.7.0...v6.8.0) (2023-06-14)
### Features
* add support for P-384 curve ([#515](https://github.com/firebase/php-jwt/issues/515)) ([5de4323](https://github.com/firebase/php-jwt/commit/5de4323f4baf4d70bca8663bd87682a69c656c3d))
### Bug Fixes
* handle invalid http responses ([#508](https://github.com/firebase/php-jwt/issues/508)) ([91c39c7](https://github.com/firebase/php-jwt/commit/91c39c72b22fc3e1191e574089552c1f2041c718))
## [6.7.0](https://github.com/firebase/php-jwt/compare/v6.6.0...v6.7.0) (2023-06-14)
### Features
* add ed25519 support to JWK (public keys) ([#452](https://github.com/firebase/php-jwt/issues/452)) ([e53979a](https://github.com/firebase/php-jwt/commit/e53979abae927de916a75b9d239cfda8ce32be2a))
## [6.6.0](https://github.com/firebase/php-jwt/compare/v6.5.0...v6.6.0) (2023-06-13)
### Features
* allow get headers when decoding token ([#442](https://github.com/firebase/php-jwt/issues/442)) ([fb85f47](https://github.com/firebase/php-jwt/commit/fb85f47cfaeffdd94faf8defdf07164abcdad6c3))
### Bug Fixes
* only check iat if nbf is not used ([#493](https://github.com/firebase/php-jwt/issues/493)) ([398ccd2](https://github.com/firebase/php-jwt/commit/398ccd25ea12fa84b9e4f1085d5ff448c21ec797))
## [6.5.0](https://github.com/firebase/php-jwt/compare/v6.4.0...v6.5.0) (2023-05-12) ## [6.5.0](https://github.com/firebase/php-jwt/compare/v6.4.0...v6.5.0) (2023-05-12)
Loaded 100 of 1296 files, more files were not shown because too many files have changed in this diff. Show more