[Web] Update composer libraries

This commit is contained in:
FreddleSpl0it committed 2026-10-28 11:56:29 +01:00
1 parent 9cc8ac2908
commit bd8c784118
1294 files changed
+69430 -22305

No files matched your search

+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)
+79 -41
View File
@@ -17,22 +17,22 @@ composer require firebase/php-jwt
``` ```
Optionally, install the `paragonie/sodium_compat` package from composer if your Optionally, install the `paragonie/sodium_compat` package from composer if your
php is < 7.2 or does not have libsodium installed: php env does not have libsodium installed:
```bash ```bash
composer require paragonie/sodium_compat composer require paragonie/sodium_compat
``` ```
Example ## Example
-------
```php ```php
use Firebase\JWT\JWT; use Firebase\JWT\JWT;
use Firebase\JWT\Key; use Firebase\JWT\Key;
$key = 'example_key'; $key = 'example_key_of_sufficient_length';
$payload = [ $payload = [
'iss' => 'http://example.org', 'iss' => 'example.org',
'aud' => 'http://example.com', 'aud' => 'example.com',
'iat' => 1356999524, 'iat' => 1356999524,
'nbf' => 1357000000 'nbf' => 1357000000
]; ];
@@ -45,9 +45,13 @@ $payload = [
*/ */
$jwt = JWT::encode($payload, $key, 'HS256'); $jwt = JWT::encode($payload, $key, 'HS256');
$decoded = JWT::decode($jwt, new Key($key, 'HS256')); $decoded = JWT::decode($jwt, new Key($key, 'HS256'));
print_r($decoded); print_r($decoded);
// Pass a stdClass in as the third parameter to get the decoded header values
$headers = new stdClass();
$decoded = JWT::decode($jwt, new Key($key, 'HS256'), $headers);
print_r($headers);
/* /*
NOTE: This will now be an object instead of an associative array. To get NOTE: This will now be an object instead of an associative array. To get
an associative array, you will need to cast it as such: an associative array, you will need to cast it as such:
@@ -65,8 +69,9 @@ $decoded_array = (array) $decoded;
JWT::$leeway = 60; // $leeway in seconds JWT::$leeway = 60; // $leeway in seconds
$decoded = JWT::decode($jwt, new Key($key, 'HS256')); $decoded = JWT::decode($jwt, new Key($key, 'HS256'));
``` ```
Example encode/decode headers
------- ## Example encode/decode headers
Decoding the JWT headers without verifying the JWT first is NOT recommended, and is not supported by Decoding the JWT headers without verifying the JWT first is NOT recommended, and is not supported by
this library. This is because without verifying the JWT, the header values could have been tampered with. this library. This is because without verifying the JWT, the header values could have been tampered with.
Any value pulled from an unverified header should be treated as if it could be any string sent in from an Any value pulled from an unverified header should be treated as if it could be any string sent in from an
@@ -76,10 +81,10 @@ header part:
```php ```php
use Firebase\JWT\JWT; use Firebase\JWT\JWT;
$key = 'example_key'; $key = 'example_key_of_sufficient_length';
$payload = [ $payload = [
'iss' => 'http://example.org', 'iss' => 'example.org',
'aud' => 'http://example.com', 'aud' => 'example.com',
'iat' => 1356999524, 'iat' => 1356999524,
'nbf' => 1357000000 'nbf' => 1357000000
]; ];
@@ -99,8 +104,9 @@ $decoded = json_decode(base64_decode($headersB64), true);
print_r($decoded); print_r($decoded);
``` ```
Example with RS256 (openssl)
---------------------------- ## Example with RS256 (openssl)
```php ```php
use Firebase\JWT\JWT; use Firebase\JWT\JWT;
use Firebase\JWT\Key; use Firebase\JWT\Key;
@@ -168,8 +174,7 @@ $decoded_array = (array) $decoded;
echo "Decode:\n" . print_r($decoded_array, true) . "\n"; echo "Decode:\n" . print_r($decoded_array, true) . "\n";
``` ```
Example with a passphrase ## Example with a passphrase
-------------------------
```php ```php
use Firebase\JWT\JWT; use Firebase\JWT\JWT;
@@ -182,7 +187,7 @@ $passphrase = '[YOUR_PASSPHRASE]';
// Can be generated with "ssh-keygen -t rsa -m pem" // Can be generated with "ssh-keygen -t rsa -m pem"
$privateKeyFile = '/path/to/key-with-passphrase.pem'; $privateKeyFile = '/path/to/key-with-passphrase.pem';
// Create a private key of type "resource" /** @var OpenSSLAsymmetricKey $privateKey */
$privateKey = openssl_pkey_get_private( $privateKey = openssl_pkey_get_private(
file_get_contents($privateKeyFile), file_get_contents($privateKeyFile),
$passphrase $passphrase
@@ -205,8 +210,8 @@ $decoded = JWT::decode($jwt, new Key($publicKey, 'RS256'));
echo "Decode:\n" . print_r((array) $decoded, true) . "\n"; echo "Decode:\n" . print_r((array) $decoded, true) . "\n";
``` ```
Example with EdDSA (libsodium and Ed25519 signature) ## Example with EdDSA (libsodium and Ed25519 signature)
----------------------------
```php ```php
use Firebase\JWT\JWT; use Firebase\JWT\JWT;
use Firebase\JWT\Key; use Firebase\JWT\Key;
@@ -234,21 +239,21 @@ echo "Encode:\n" . print_r($jwt, true) . "\n";
$decoded = JWT::decode($jwt, new Key($publicKey, 'EdDSA')); $decoded = JWT::decode($jwt, new Key($publicKey, 'EdDSA'));
echo "Decode:\n" . print_r((array) $decoded, true) . "\n"; echo "Decode:\n" . print_r((array) $decoded, true) . "\n";
```` ```
## Example with multiple keys
Example with multiple keys
--------------------------
```php ```php
use Firebase\JWT\JWT; use Firebase\JWT\JWT;
use Firebase\JWT\Key; use Firebase\JWT\Key;
// Example RSA keys from previous example // Example RSA keys from previous example
// $privateKey1 = '...'; // $privateRsKey = '...';
// $publicKey1 = '...'; // $publicRsKey = '...';
// Example EdDSA keys from previous example // Example EdDSA keys from previous example
// $privateKey2 = '...'; // $privateEcKey = '...';
// $publicKey2 = '...'; // $publicEcKey = '...';
$payload = [ $payload = [
'iss' => 'example.org', 'iss' => 'example.org',
@@ -257,14 +262,14 @@ $payload = [
'nbf' => 1357000000 'nbf' => 1357000000
]; ];
$jwt1 = JWT::encode($payload, $privateKey1, 'RS256', 'kid1'); $jwt1 = JWT::encode($payload, $privateRsKey, 'RS256', 'kid1');
$jwt2 = JWT::encode($payload, $privateKey2, 'EdDSA', 'kid2'); $jwt2 = JWT::encode($payload, $privateEcKey, 'EdDSA', 'kid2');
echo "Encode 1:\n" . print_r($jwt1, true) . "\n"; echo "Encode 1:\n" . print_r($jwt1, true) . "\n";
echo "Encode 2:\n" . print_r($jwt2, true) . "\n"; echo "Encode 2:\n" . print_r($jwt2, true) . "\n";
$keys = [ $keys = [
'kid1' => new Key($publicKey1, 'RS256'), 'kid1' => new Key($publicRsKey, 'RS256'),
'kid2' => new Key($publicKey2, 'EdDSA'), 'kid2' => new Key($publicEcKey, 'EdDSA'),
]; ];
$decoded1 = JWT::decode($jwt1, $keys); $decoded1 = JWT::decode($jwt1, $keys);
@@ -274,8 +279,39 @@ echo "Decode 1:\n" . print_r((array) $decoded1, true) . "\n";
echo "Decode 2:\n" . print_r((array) $decoded2, true) . "\n"; echo "Decode 2:\n" . print_r((array) $decoded2, true) . "\n";
``` ```
Using JWKs ## Example with PS256
----------
### Note
PHP's OpenSSL extension does not support RSASSA-PSS signatures (PS256) by default, so we provide support via a soft dependency on the [phpseclib/phpseclib](https://github.com/phpseclib/phpseclib) library. It is necessary to install this library in your project if you plan to use PS256.
```bash
composer install phpseclib/phpseclib:^3.0
```
```php
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
$privateRsKey = '-----BEGIN RSA PRIVATE KEY----- ...';
$publicKey = '-----BEGIN PUBLIC KEY----- ...';
$payload = [
'iss' => 'example.org',
'aud' => 'example.com',
'iat' => 1356999524,
'nbf' => 1357000000
];
/**
* PS256 support requires phpseclib/phpseclib
*/
$jwt = JWT::encode($payload, $privateRsKey, 'PS256', 'keyid');
echo "Encode:\n" . print_r($jwt, true) . "\n";
$decoded = JWT::decode($jwt, new Key($publicKey, 'PS256'));
echo "Decode:\n" . print_r((array) $decoded, true) . "\n";
```
## Using JWKs
```php ```php
use Firebase\JWT\JWK; use Firebase\JWT\JWK;
@@ -287,11 +323,11 @@ $jwks = ['keys' => []];
// JWK::parseKeySet($jwks) returns an associative array of **kid** to Firebase\JWT\Key // JWK::parseKeySet($jwks) returns an associative array of **kid** to Firebase\JWT\Key
// objects. Pass this as the second parameter to JWT::decode. // objects. Pass this as the second parameter to JWT::decode.
JWT::decode($payload, JWK::parseKeySet($jwks)); $decoded = JWT::decode($jwt, JWK::parseKeySet($jwks));
print_r($decoded);
``` ```
Using Cached Key Sets ## Using Cached Key Sets
---------------------
The `CachedKeySet` class can be used to fetch and cache JWKS (JSON Web Key Sets) from a public URI. The `CachedKeySet` class can be used to fetch and cache JWKS (JSON Web Key Sets) from a public URI.
This has the following advantages: This has the following advantages:
@@ -311,7 +347,7 @@ $jwksUri = 'https://www.gstatic.com/iap/verify/public_key-jwk';
$httpClient = new GuzzleHttp\Client(); $httpClient = new GuzzleHttp\Client();
// Create an HTTP request factory (can be any PSR-17 compatible HTTP request factory) // Create an HTTP request factory (can be any PSR-17 compatible HTTP request factory)
$httpFactory = new GuzzleHttp\Psr\HttpFactory(); $httpFactory = new GuzzleHttp\Psr7\HttpFactory();
// Create a cache item pool (can be any PSR-6 compatible cache item pool) // Create a cache item pool (can be any PSR-6 compatible cache item pool)
$cacheItemPool = Phpfastcache\CacheManager::getInstance('files'); $cacheItemPool = Phpfastcache\CacheManager::getInstance('files');
@@ -346,7 +382,7 @@ use InvalidArgumentException;
use UnexpectedValueException; use UnexpectedValueException;
try { try {
$decoded = JWT::decode($payload, $keys); $decoded = JWT::decode($jwt, $keys);
} catch (InvalidArgumentException $e) { } catch (InvalidArgumentException $e) {
// provided key/key-array is empty or malformed. // provided key/key-array is empty or malformed.
} catch (DomainException $e) { } catch (DomainException $e) {
@@ -373,8 +409,10 @@ All exceptions in the `Firebase\JWT` namespace extend `UnexpectedValueException`
like this: like this:
```php ```php
use Firebase\JWT\JWT;
use UnexpectedValueException;
try { try {
$decoded = JWT::decode($payload, $keys); $decoded = JWT::decode($jwt, $keys);
} catch (LogicException $e) { } catch (LogicException $e) {
// errors having to do with environmental setup or malformed JWT Keys // errors having to do with environmental setup or malformed JWT Keys
} catch (UnexpectedValueException $e) { } catch (UnexpectedValueException $e) {
@@ -389,7 +427,7 @@ instead, you can do the following:
```php ```php
// return type is stdClass // return type is stdClass
$decoded = JWT::decode($payload, $keys); $decoded = JWT::decode($jwt, $keys);
// cast to array // cast to array
$decoded = json_decode(json_encode($decoded), true); $decoded = json_decode(json_encode($decoded), true);
@@ -400,8 +438,8 @@ Tests
Run the tests using phpunit: Run the tests using phpunit:
```bash ```bash
$ pear install PHPUnit $ composer update
$ phpunit --configuration phpunit.xml.dist $ vendor/bin/phpunit -c phpunit.xml.dist
PHPUnit 3.7.10 by Sebastian Bergmann. PHPUnit 3.7.10 by Sebastian Bergmann.
..... .....
Time: 0 seconds, Memory: 2.50Mb Time: 0 seconds, Memory: 2.50Mb
Loaded 100 of 1294 files, more files were not shown because too many files have changed in this diff. Show more