diff --git a/composer.json b/composer.json index 604c12a28..77550bc3c 100644 --- a/composer.json +++ b/composer.json @@ -6,16 +6,18 @@ "type": "project", "require": { "php": "^7.1.3", + "eveseat/eseye": "^2.3", "fideloper/proxy": "^4.0", "guzzlehttp/guzzle": "^6.3", "khill/lavacharts": "^3.1.14", "laravel/framework": "^7.0", - "twbs/bootstrap": "^4.5", - "laravelcollective/html": "^6.0", - "laravel/helpers": "^1.4", - "laravel/socialite": "^5.1", + "laravel/helpers": "^1.4", "laravel/horizon": "^4.0", - "laravel/ui": "^2.0" + "laravel/socialite": "^5.1", + "laravel/ui": "^2.0", + "laravelcollective/html": "^6.0", + "predis/predis": "^1.1", + "twbs/bootstrap": "^4.5" }, "require-dev": { "beyondcode/laravel-dump-server": "^1.0", diff --git a/composer.lock b/composer.lock index 7faec1748..c03619281 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d04c9ac5ed284e0e932fe5564f2ce781", + "content-hash": "def63b69a9e4d54db5c28bafdf491100", "packages": [ { "name": "brick/math", @@ -429,6 +429,144 @@ ], "time": "2020-11-14T15:56:27+00:00" }, + { + "name": "eveseat/eseye", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/eveseat/eseye.git", + "reference": "e1e310592ea03aa8a68728d8c15d7f04cf29f957" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/eveseat/eseye/zipball/e1e310592ea03aa8a68728d8c15d7f04cf29f957", + "reference": "e1e310592ea03aa8a68728d8c15d7f04cf29f957", + "shasum": "" + }, + "require": { + "ext-gmp": "*", + "ext-json": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "guzzlehttp/guzzle": "^6.2", + "monolog/monolog": "^2.0", + "nesbot/carbon": "^2.0", + "php": ">= 7.2", + "predis/predis": "^1.1", + "web-token/jwt-easy": "^2.1", + "web-token/jwt-signature-algorithm-ecdsa": "^2.1", + "web-token/jwt-signature-algorithm-hmac": "^2.1", + "web-token/jwt-signature-algorithm-rsa": "^2.1" + }, + "require-dev": { + "codeclimate/php-test-reporter": "dev-master", + "mikey179/vfsstream": "~1", + "phpunit/phpunit": "^5.7" + }, + "bin": [ + "bin/tokengenerator" + ], + "type": "library", + "autoload": { + "psr-4": { + "Seat\\Eseye\\": "src/" + }, + "files": [ + "src/Helpers/helpers.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0" + ], + "authors": [ + { + "name": "Leon Jacobs", + "email": "leonja511@gmail.com" + } + ], + "description": "A Standalone PHP ESI (EVE Swagger Interface) Client Library", + "support": { + "issues": "https://github.com/eveseat/eseye/issues", + "source": "https://github.com/eveseat/eseye/tree/2.3.0" + }, + "time": "2020-11-14T15:15:05+00:00" + }, + { + "name": "fgrosse/phpasn1", + "version": "v2.2.0", + "source": { + "type": "git", + "url": "https://github.com/fgrosse/PHPASN1.git", + "reference": "d1978f7abd580f3fc33561e7f71d4c12c7531fad" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fgrosse/PHPASN1/zipball/d1978f7abd580f3fc33561e7f71d4c12c7531fad", + "reference": "d1978f7abd580f3fc33561e7f71d4c12c7531fad", + "shasum": "" + }, + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": "~6.3", + "satooshi/php-coveralls": "~2.0" + }, + "suggest": { + "ext-bcmath": "BCmath is the fallback extension for big integer calculations", + "ext-curl": "For loading OID information from the web if they have not bee defined statically", + "ext-gmp": "GMP is the preferred extension for big integer calculations", + "phpseclib/bcmath_compat": "BCmath polyfill for servers where neither GMP nor BCmath is available" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "FG\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Friedrich Große", + "email": "friedrich.grosse@gmail.com", + "homepage": "https://github.com/FGrosse", + "role": "Author" + }, + { + "name": "All contributors", + "homepage": "https://github.com/FGrosse/PHPASN1/contributors" + } + ], + "description": "A PHP Framework that allows you to encode and decode arbitrary ASN.1 structures using the ITU-T X.690 Encoding Rules.", + "homepage": "https://github.com/FGrosse/PHPASN1", + "keywords": [ + "DER", + "asn.1", + "asn1", + "ber", + "binary", + "decoding", + "encoding", + "x.509", + "x.690", + "x509", + "x690" + ], + "support": { + "issues": "https://github.com/fgrosse/PHPASN1/issues", + "source": "https://github.com/fgrosse/PHPASN1/tree/v2.2.0" + }, + "time": "2020-10-11T16:28:18+00:00" + }, { "name": "fideloper/proxy", "version": "4.4.1", @@ -1902,6 +2040,85 @@ ], "time": "2020-07-20T17:29:33+00:00" }, + { + "name": "predis/predis", + "version": "v1.1.6", + "source": { + "type": "git", + "url": "https://github.com/predis/predis.git", + "reference": "9930e933c67446962997b05201c69c2319bf26de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/predis/predis/zipball/9930e933c67446962997b05201c69c2319bf26de", + "reference": "9930e933c67446962997b05201c69c2319bf26de", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "cweagans/composer-patches": "^1.6", + "phpunit/phpunit": "~4.8" + }, + "suggest": { + "ext-curl": "Allows access to Webdis when paired with phpiredis", + "ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol" + }, + "type": "library", + "extra": { + "composer-exit-on-patch-failure": true, + "patches": { + "phpunit/phpunit-mock-objects": { + "Fix PHP 7 and 8 compatibility": "./tests/phpunit_mock_objects.patch" + }, + "phpunit/phpunit": { + "Fix PHP 7 compatibility": "./tests/phpunit_php7.patch", + "Fix PHP 8 compatibility": "./tests/phpunit_php8.patch" + } + } + }, + "autoload": { + "psr-4": { + "Predis\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniele Alessandri", + "email": "suppakilla@gmail.com", + "homepage": "http://clorophilla.net", + "role": "Creator & Maintainer" + }, + { + "name": "Till Krüss", + "homepage": "https://till.im", + "role": "Maintainer" + } + ], + "description": "Flexible and feature-complete Redis client for PHP and HHVM", + "homepage": "http://github.com/predis/predis", + "keywords": [ + "nosql", + "predis", + "redis" + ], + "support": { + "issues": "https://github.com/predis/predis/issues", + "source": "https://github.com/predis/predis/tree/v1.1.6" + }, + "funding": [ + { + "url": "https://github.com/sponsors/tillkruss", + "type": "github" + } + ], + "time": "2020-09-11T19:18:05+00:00" + }, { "name": "psr/container", "version": "1.0.0", @@ -2368,6 +2585,71 @@ ], "time": "2020-08-18T17:17:46+00:00" }, + { + "name": "spomky-labs/base64url", + "version": "v2.0.4", + "source": { + "type": "git", + "url": "https://github.com/Spomky-Labs/base64url.git", + "reference": "7752ce931ec285da4ed1f4c5aa27e45e097be61d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Spomky-Labs/base64url/zipball/7752ce931ec285da4ed1f4c5aa27e45e097be61d", + "reference": "7752ce931ec285da4ed1f4c5aa27e45e097be61d", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.11|^0.12", + "phpstan/phpstan-beberlei-assert": "^0.11|^0.12", + "phpstan/phpstan-deprecation-rules": "^0.11|^0.12", + "phpstan/phpstan-phpunit": "^0.11|^0.12", + "phpstan/phpstan-strict-rules": "^0.11|^0.12" + }, + "type": "library", + "autoload": { + "psr-4": { + "Base64Url\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky-Labs/base64url/contributors" + } + ], + "description": "Base 64 URL Safe Encoding/Decoding PHP Library", + "homepage": "https://github.com/Spomky-Labs/base64url", + "keywords": [ + "base64", + "rfc4648", + "safe", + "url" + ], + "support": { + "issues": "https://github.com/Spomky-Labs/base64url/issues", + "source": "https://github.com/Spomky-Labs/base64url/tree/v2.0.4" + }, + "funding": [ + { + "url": "https://github.com/Spomky", + "type": "github" + }, + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "time": "2020-11-03T09:10:25+00:00" + }, { "name": "swiftmailer/swiftmailer", "version": "v6.2.4", @@ -4877,6 +5159,704 @@ } ], "time": "2020-11-12T00:07:28+00:00" + }, + { + "name": "web-token/jwt-checker", + "version": "v2.2.6", + "source": { + "type": "git", + "url": "https://github.com/web-token/jwt-checker.git", + "reference": "73ffe42041b0b8d6ddce56e42533a931d249629c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-token/jwt-checker/zipball/73ffe42041b0b8d6ddce56e42533a931d249629c", + "reference": "73ffe42041b0b8d6ddce56e42533a931d249629c", + "shasum": "" + }, + "require": { + "web-token/jwt-core": "^2.1" + }, + "require-dev": { + "phpunit/phpunit": "^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "v1.0": "1.0.x-dev", + "v1.1": "1.1.x-dev", + "v1.2": "1.2.x-dev", + "v1.3": "1.3.x-dev", + "v2.0": "2.0.x-dev", + "v2.1": "2.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jose\\Component\\Checker\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-checker/contributors" + } + ], + "description": "Checker component of the JWT Framework.", + "homepage": "https://github.com/web-token", + "keywords": [ + "JOSE", + "JWE", + "JWK", + "JWKSet", + "JWS", + "Jot", + "RFC7515", + "RFC7516", + "RFC7517", + "RFC7518", + "RFC7519", + "RFC7520", + "bundle", + "jwa", + "jwt", + "symfony" + ], + "support": { + "source": "https://github.com/web-token/jwt-checker/tree/v2.2.6" + }, + "funding": [ + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "time": "2020-09-29T22:16:17+00:00" + }, + { + "name": "web-token/jwt-core", + "version": "v2.2.6", + "source": { + "type": "git", + "url": "https://github.com/web-token/jwt-core.git", + "reference": "8115d846f3a9ee817dca03024eb4c17a3c0ff81a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-token/jwt-core/zipball/8115d846f3a9ee817dca03024eb4c17a3c0ff81a", + "reference": "8115d846f3a9ee817dca03024eb4c17a3c0ff81a", + "shasum": "" + }, + "require": { + "brick/math": "^0.8.17|^0.9", + "ext-json": "*", + "ext-mbstring": "*", + "fgrosse/phpasn1": "^2.0", + "php": ">=7.2", + "spomky-labs/base64url": "^1.0|^2.0" + }, + "conflict": { + "spomky-labs/jose": "*" + }, + "require-dev": { + "phpunit/phpunit": "^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "v1.0": "1.0.x-dev", + "v1.1": "1.1.x-dev", + "v1.2": "1.2.x-dev", + "v1.3": "1.3.x-dev", + "v2.0": "2.0.x-dev", + "v2.1": "2.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jose\\Component\\Core\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-framework/contributors" + } + ], + "description": "Core component of the JWT Framework.", + "homepage": "https://github.com/web-token", + "keywords": [ + "JOSE", + "JWE", + "JWK", + "JWKSet", + "JWS", + "Jot", + "RFC7515", + "RFC7516", + "RFC7517", + "RFC7518", + "RFC7519", + "RFC7520", + "bundle", + "jwa", + "jwt", + "symfony" + ], + "support": { + "source": "https://github.com/web-token/jwt-core/tree/v2.2.6" + }, + "funding": [ + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "time": "2020-09-29T21:35:58+00:00" + }, + { + "name": "web-token/jwt-easy", + "version": "v2.2.6", + "source": { + "type": "git", + "url": "https://github.com/web-token/jwt-easy.git", + "reference": "cc59f479dd6b5014586be5d2d40ce84bbd9b4539" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-token/jwt-easy/zipball/cc59f479dd6b5014586be5d2d40ce84bbd9b4539", + "reference": "cc59f479dd6b5014586be5d2d40ce84bbd9b4539", + "shasum": "" + }, + "require": { + "web-token/jwt-checker": "^2.1", + "web-token/jwt-encryption": "^2.1", + "web-token/jwt-signature": "^2.1" + }, + "require-dev": { + "phpunit/phpunit": "^8.0" + }, + "suggest": { + "web-token/jwt-encryption-algorithm-aescbc": "Adds AES-CBC based encryption algorithms", + "web-token/jwt-encryption-algorithm-aesgcm": "Adds AES-GCM based encryption algorithms", + "web-token/jwt-encryption-algorithm-aesgcmkw": "Adds AES-GCM Key Wrapping based encryption algorithms", + "web-token/jwt-encryption-algorithm-aeskw": "Adds AES Key Wrapping based encryption algorithms", + "web-token/jwt-encryption-algorithm-dir": "Adds Direct encryption algorithm", + "web-token/jwt-encryption-algorithm-ecdh-es": "Adds ECDH-ES based encryption algorithms", + "web-token/jwt-encryption-algorithm-pbes2": "Adds PBES2 based encryption algorithms", + "web-token/jwt-encryption-algorithm-rsa": "Adds RSA based encryption algorithms", + "web-token/jwt-signature-algorithm-ecdsa": "Adds ECDSA based signature algorithms", + "web-token/jwt-signature-algorithm-eddsa": "Adds EdDSA based signature algorithms", + "web-token/jwt-signature-algorithm-hmac": "Adds HMAC based signature algorithms", + "web-token/jwt-signature-algorithm-none": "Adds none signature algorithms", + "web-token/jwt-signature-algorithm-rsa": "Adds RSA based signature algorithms" + }, + "type": "library", + "extra": { + "branch-alias": { + "v1.0": "1.0.x-dev", + "v1.1": "1.1.x-dev", + "v1.2": "1.2.x-dev", + "v1.3": "1.3.x-dev", + "v2.0": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jose\\Easy\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-framework/contributors" + } + ], + "description": "Easy toolset to use the JWT Framework.", + "homepage": "https://github.com/web-token", + "keywords": [ + "JOSE", + "JWE", + "JWK", + "JWKSet", + "JWS", + "Jot", + "RFC7515", + "RFC7516", + "RFC7517", + "RFC7518", + "RFC7519", + "RFC7520", + "bundle", + "jwa", + "jwt", + "symfony" + ], + "support": { + "source": "https://github.com/web-token/jwt-easy/tree/v2.2.6" + }, + "funding": [ + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "time": "2020-12-07T21:10:31+00:00" + }, + { + "name": "web-token/jwt-encryption", + "version": "v2.2.6", + "source": { + "type": "git", + "url": "https://github.com/web-token/jwt-encryption.git", + "reference": "36afc9f25fe8546840dc63c3fc1c6fe166879052" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-token/jwt-encryption/zipball/36afc9f25fe8546840dc63c3fc1c6fe166879052", + "reference": "36afc9f25fe8546840dc63c3fc1c6fe166879052", + "shasum": "" + }, + "require": { + "web-token/jwt-core": "^2.1" + }, + "require-dev": { + "phpunit/phpunit": "^8.0" + }, + "suggest": { + "web-token/jwt-encryption-algorithm-aescbc": "AES CBC Based Content Encryption Algorithms", + "web-token/jwt-encryption-algorithm-aesgcm": "AES GCM Based Content Encryption Algorithms", + "web-token/jwt-encryption-algorithm-aesgcmkw": "AES GCM Key Wrapping Based Key Encryption Algorithms", + "web-token/jwt-encryption-algorithm-aeskw": "AES Key Wrapping Based Key Encryption Algorithms", + "web-token/jwt-encryption-algorithm-dir": "Direct Key Encryption Algorithms", + "web-token/jwt-encryption-algorithm-ecdh-es": "ECDH-ES Based Key Encryption Algorithms", + "web-token/jwt-encryption-algorithm-experimental": "Experimental Key and Signature Algorithms", + "web-token/jwt-encryption-algorithm-pbes2": "PBES2 Based Key Encryption Algorithms", + "web-token/jwt-encryption-algorithm-rsa": "RSA Based Key Encryption Algorithms" + }, + "type": "library", + "extra": { + "branch-alias": { + "v1.0": "1.0.x-dev", + "v1.1": "1.1.x-dev", + "v1.2": "1.2.x-dev", + "v1.3": "1.3.x-dev", + "v2.0": "2.0.x-dev", + "v2.1": "2.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jose\\Component\\Encryption\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-encryption/contributors" + } + ], + "description": "Encryption component of the JWT Framework.", + "homepage": "https://github.com/web-token", + "keywords": [ + "JOSE", + "JWE", + "JWK", + "JWKSet", + "JWS", + "Jot", + "RFC7515", + "RFC7516", + "RFC7517", + "RFC7518", + "RFC7519", + "RFC7520", + "bundle", + "jwa", + "jwt", + "symfony" + ], + "support": { + "source": "https://github.com/web-token/jwt-encryption/tree/v2.2.1" + }, + "funding": [ + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "time": "2020-08-01T11:48:26+00:00" + }, + { + "name": "web-token/jwt-signature", + "version": "v2.2.6", + "source": { + "type": "git", + "url": "https://github.com/web-token/jwt-signature.git", + "reference": "f4d83f77031c9d27edebbea053931074030d3b09" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-token/jwt-signature/zipball/f4d83f77031c9d27edebbea053931074030d3b09", + "reference": "f4d83f77031c9d27edebbea053931074030d3b09", + "shasum": "" + }, + "require": { + "web-token/jwt-core": "^2.1" + }, + "require-dev": { + "phpunit/phpunit": "^8.0" + }, + "suggest": { + "web-token/jwt-signature-algorithm-ecdsa": "ECDSA Based Signature Algorithms", + "web-token/jwt-signature-algorithm-eddsa": "EdDSA Based Signature Algorithms", + "web-token/jwt-signature-algorithm-experimental": "Experimental Signature Algorithms", + "web-token/jwt-signature-algorithm-hmac": "HMAC Based Signature Algorithms", + "web-token/jwt-signature-algorithm-none": "None Signature Algorithm", + "web-token/jwt-signature-algorithm-rsa": "RSA Based Signature Algorithms" + }, + "type": "library", + "extra": { + "branch-alias": { + "v1.0": "1.0.x-dev", + "v1.1": "1.1.x-dev", + "v1.2": "1.2.x-dev", + "v1.3": "1.3.x-dev", + "v2.0": "2.0.x-dev", + "v2.1": "2.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jose\\Component\\Signature\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-signature/contributors" + } + ], + "description": "Signature component of the JWT Framework.", + "homepage": "https://github.com/web-token", + "keywords": [ + "JOSE", + "JWE", + "JWK", + "JWKSet", + "JWS", + "Jot", + "RFC7515", + "RFC7516", + "RFC7517", + "RFC7518", + "RFC7519", + "RFC7520", + "bundle", + "jwa", + "jwt", + "symfony" + ], + "support": { + "source": "https://github.com/web-token/jwt-signature/tree/v2.2.0" + }, + "funding": [ + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "time": "2020-08-01T11:48:26+00:00" + }, + { + "name": "web-token/jwt-signature-algorithm-ecdsa", + "version": "v2.2.6", + "source": { + "type": "git", + "url": "https://github.com/web-token/jwt-signature-algorithm-ecdsa.git", + "reference": "3ae6e56ccf34e8b7b65ec988497d5d1bceca9864" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-token/jwt-signature-algorithm-ecdsa/zipball/3ae6e56ccf34e8b7b65ec988497d5d1bceca9864", + "reference": "3ae6e56ccf34e8b7b65ec988497d5d1bceca9864", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "web-token/jwt-signature": "^2.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "v1.0": "1.0.x-dev", + "v1.1": "1.1.x-dev", + "v1.2": "1.2.x-dev", + "v1.3": "1.3.x-dev", + "v2.0": "2.0.x-dev", + "v2.1": "2.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jose\\Component\\Signature\\Algorithm\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-framework/contributors" + } + ], + "description": "ECDSA Based Signature Algorithms the JWT Framework.", + "homepage": "https://github.com/web-token", + "keywords": [ + "JOSE", + "JWE", + "JWK", + "JWKSet", + "JWS", + "Jot", + "RFC7515", + "RFC7516", + "RFC7517", + "RFC7518", + "RFC7519", + "RFC7520", + "bundle", + "jwa", + "jwt", + "symfony" + ], + "support": { + "source": "https://github.com/web-token/jwt-signature-algorithm-ecdsa/tree/v2.2" + }, + "funding": [ + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "time": "2020-08-22T13:17:25+00:00" + }, + { + "name": "web-token/jwt-signature-algorithm-hmac", + "version": "v2.2.6", + "source": { + "type": "git", + "url": "https://github.com/web-token/jwt-signature-algorithm-hmac.git", + "reference": "f4fd8166e6830298fc820a8d5776c124cc2496c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-token/jwt-signature-algorithm-hmac/zipball/f4fd8166e6830298fc820a8d5776c124cc2496c4", + "reference": "f4fd8166e6830298fc820a8d5776c124cc2496c4", + "shasum": "" + }, + "require": { + "web-token/jwt-signature": "^2.1" + }, + "require-dev": { + "phpunit/phpunit": "^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "v1.0": "1.0.x-dev", + "v1.1": "1.1.x-dev", + "v1.2": "1.2.x-dev", + "v1.3": "1.3.x-dev", + "v2.0": "2.0.x-dev", + "v2.1": "2.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jose\\Component\\Signature\\Algorithm\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-framework/contributors" + } + ], + "description": "HMAC Based Signature Algorithms the JWT Framework.", + "homepage": "https://github.com/web-token", + "keywords": [ + "JOSE", + "JWE", + "JWK", + "JWKSet", + "JWS", + "Jot", + "RFC7515", + "RFC7516", + "RFC7517", + "RFC7518", + "RFC7519", + "RFC7520", + "bundle", + "jwa", + "jwt", + "symfony" + ], + "support": { + "source": "https://github.com/web-token/jwt-signature-algorithm-hmac/tree/v2.2.2" + }, + "funding": [ + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "time": "2020-08-01T11:48:26+00:00" + }, + { + "name": "web-token/jwt-signature-algorithm-rsa", + "version": "v2.2.6", + "source": { + "type": "git", + "url": "https://github.com/web-token/jwt-signature-algorithm-rsa.git", + "reference": "ba94b8868bd8fdd481f448097ab900a8cb2c0eed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-token/jwt-signature-algorithm-rsa/zipball/ba94b8868bd8fdd481f448097ab900a8cb2c0eed", + "reference": "ba94b8868bd8fdd481f448097ab900a8cb2c0eed", + "shasum": "" + }, + "require": { + "brick/math": "^0.8.17|^0.9", + "ext-openssl": "*", + "web-token/jwt-signature": "^2.1" + }, + "require-dev": { + "phpunit/phpunit": "^8.0" + }, + "suggest": { + "ext-bcmath": "GMP or BCMath is highly recommended to improve the library performance", + "ext-gmp": "GMP or BCMath is highly recommended to improve the library performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "v1.0": "1.0.x-dev", + "v1.1": "1.1.x-dev", + "v1.2": "1.2.x-dev", + "v1.3": "1.3.x-dev", + "v2.0": "2.0.x-dev", + "v2.1": "2.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jose\\Component\\Signature\\Algorithm\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-framework/contributors" + } + ], + "description": "RSA Based Signature Algorithms the JWT Framework.", + "homepage": "https://github.com/web-token", + "keywords": [ + "JOSE", + "JWE", + "JWK", + "JWKSet", + "JWS", + "Jot", + "RFC7515", + "RFC7516", + "RFC7517", + "RFC7518", + "RFC7519", + "RFC7520", + "bundle", + "jwa", + "jwt", + "symfony" + ], + "support": { + "source": "https://github.com/web-token/jwt-signature-algorithm-rsa/tree/v2.2" + }, + "funding": [ + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "time": "2020-08-22T13:37:56+00:00" } ], "packages-dev": [ diff --git a/vendor/bin/tokengenerator b/vendor/bin/tokengenerator new file mode 120000 index 000000000..0ad0ef431 --- /dev/null +++ b/vendor/bin/tokengenerator @@ -0,0 +1 @@ +../eveseat/eseye/bin/tokengenerator \ No newline at end of file diff --git a/vendor/composer/InstalledVersions.php b/vendor/composer/InstalledVersions.php index 178ae8708..b1bee7dbe 100644 --- a/vendor/composer/InstalledVersions.php +++ b/vendor/composer/InstalledVersions.php @@ -29,7 +29,7 @@ private static $installed = array ( 'aliases' => array ( ), - 'reference' => '516105c492788aee680ef1dce3d45e514d703747', + 'reference' => '0ddd2983509921acf00db95c3cd019abf733acb1', 'name' => 'laravel/laravel', ), 'versions' => @@ -120,6 +120,15 @@ private static $installed = array ( ), 'reference' => 'ca90a3291eee1538cd48ff25163240695bd95448', ), + 'eveseat/eseye' => + array ( + 'pretty_version' => '2.3.0', + 'version' => '2.3.0.0', + 'aliases' => + array ( + ), + 'reference' => 'e1e310592ea03aa8a68728d8c15d7f04cf29f957', + ), 'facade/ignition-contracts' => array ( 'pretty_version' => '1.0.2', @@ -129,6 +138,15 @@ private static $installed = array ( ), 'reference' => '3c921a1cdba35b68a7f0ccffc6dffc1995b18267', ), + 'fgrosse/phpasn1' => + array ( + 'pretty_version' => 'v2.2.0', + 'version' => '2.2.0.0', + 'aliases' => + array ( + ), + 'reference' => 'd1978f7abd580f3fc33561e7f71d4c12c7531fad', + ), 'fideloper/proxy' => array ( 'pretty_version' => '4.4.1', @@ -445,7 +463,7 @@ private static $installed = array ( 'aliases' => array ( ), - 'reference' => '516105c492788aee680ef1dce3d45e514d703747', + 'reference' => '0ddd2983509921acf00db95c3cd019abf733acb1', ), 'laravel/socialite' => array ( @@ -685,6 +703,15 @@ private static $installed = array ( ), 'reference' => '8e86be391a58104ef86037ba8a846524528d784e', ), + 'predis/predis' => + array ( + 'pretty_version' => 'v1.1.6', + 'version' => '1.1.6.0', + 'aliases' => + array ( + ), + 'reference' => '9930e933c67446962997b05201c69c2319bf26de', + ), 'psr/container' => array ( 'pretty_version' => '1.0.0', @@ -901,6 +928,15 @@ private static $installed = array ( ), 'reference' => '99732be0ddb3361e16ad77b68ba41efc8e979019', ), + 'spomky-labs/base64url' => + array ( + 'pretty_version' => 'v2.0.4', + 'version' => '2.0.4.0', + 'aliases' => + array ( + ), + 'reference' => '7752ce931ec285da4ed1f4c5aa27e45e097be61d', + ), 'swiftmailer/swiftmailer' => array ( 'pretty_version' => 'v6.2.4', @@ -1219,6 +1255,78 @@ private static $installed = array ( ), 'reference' => '80953678b19901e5165c56752d087fc11526017c', ), + 'web-token/jwt-checker' => + array ( + 'pretty_version' => 'v2.2.6', + 'version' => '2.2.6.0', + 'aliases' => + array ( + ), + 'reference' => '73ffe42041b0b8d6ddce56e42533a931d249629c', + ), + 'web-token/jwt-core' => + array ( + 'pretty_version' => 'v2.2.6', + 'version' => '2.2.6.0', + 'aliases' => + array ( + ), + 'reference' => '8115d846f3a9ee817dca03024eb4c17a3c0ff81a', + ), + 'web-token/jwt-easy' => + array ( + 'pretty_version' => 'v2.2.6', + 'version' => '2.2.6.0', + 'aliases' => + array ( + ), + 'reference' => 'cc59f479dd6b5014586be5d2d40ce84bbd9b4539', + ), + 'web-token/jwt-encryption' => + array ( + 'pretty_version' => 'v2.2.6', + 'version' => '2.2.6.0', + 'aliases' => + array ( + ), + 'reference' => '36afc9f25fe8546840dc63c3fc1c6fe166879052', + ), + 'web-token/jwt-signature' => + array ( + 'pretty_version' => 'v2.2.6', + 'version' => '2.2.6.0', + 'aliases' => + array ( + ), + 'reference' => 'f4d83f77031c9d27edebbea053931074030d3b09', + ), + 'web-token/jwt-signature-algorithm-ecdsa' => + array ( + 'pretty_version' => 'v2.2.6', + 'version' => '2.2.6.0', + 'aliases' => + array ( + ), + 'reference' => '3ae6e56ccf34e8b7b65ec988497d5d1bceca9864', + ), + 'web-token/jwt-signature-algorithm-hmac' => + array ( + 'pretty_version' => 'v2.2.6', + 'version' => '2.2.6.0', + 'aliases' => + array ( + ), + 'reference' => 'f4fd8166e6830298fc820a8d5776c124cc2496c4', + ), + 'web-token/jwt-signature-algorithm-rsa' => + array ( + 'pretty_version' => 'v2.2.6', + 'version' => '2.2.6.0', + 'aliases' => + array ( + ), + 'reference' => 'ba94b8868bd8fdd481f448097ab900a8cb2c0eed', + ), 'webmozart/assert' => array ( 'pretty_version' => '1.9.1', diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index 2263ff411..9f19f032f 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -165,6 +165,7 @@ return array( 'App\\Traits\\EveOAuth' => $baseDir . '/app/Traits/EveOAuth.php', 'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', 'AvailableUserPermissions' => $baseDir . '/database/seeds/AvailableUserPermissions.php', + 'Base64Url\\Base64Url' => $vendorDir . '/spomky-labs/base64url/src/Base64Url.php', 'BeyondCode\\DumpServer\\DumpServerCommand' => $vendorDir . '/beyondcode/laravel-dump-server/src/DumpServerCommand.php', 'BeyondCode\\DumpServer\\DumpServerServiceProvider' => $vendorDir . '/beyondcode/laravel-dump-server/src/DumpServerServiceProvider.php', 'BeyondCode\\DumpServer\\Dumper' => $vendorDir . '/beyondcode/laravel-dump-server/src/Dumper.php', @@ -464,6 +465,60 @@ return array( 'Egulias\\EmailValidator\\Warning\\QuotedString' => $vendorDir . '/egulias/email-validator/src/Warning/QuotedString.php', 'Egulias\\EmailValidator\\Warning\\TLD' => $vendorDir . '/egulias/email-validator/src/Warning/TLD.php', 'Egulias\\EmailValidator\\Warning\\Warning' => $vendorDir . '/egulias/email-validator/src/Warning/Warning.php', + 'FG\\ASN1\\ASNObject' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/ASNObject.php', + 'FG\\ASN1\\AbstractString' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/AbstractString.php', + 'FG\\ASN1\\AbstractTime' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/AbstractTime.php', + 'FG\\ASN1\\Base128' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Base128.php', + 'FG\\ASN1\\Composite\\AttributeTypeAndValue' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Composite/AttributeTypeAndValue.php', + 'FG\\ASN1\\Composite\\RDNString' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Composite/RDNString.php', + 'FG\\ASN1\\Composite\\RelativeDistinguishedName' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Composite/RelativeDistinguishedName.php', + 'FG\\ASN1\\Construct' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Construct.php', + 'FG\\ASN1\\Exception\\NotImplementedException' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Exception/NotImplementedException.php', + 'FG\\ASN1\\Exception\\ParserException' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Exception/ParserException.php', + 'FG\\ASN1\\ExplicitlyTaggedObject' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/ExplicitlyTaggedObject.php', + 'FG\\ASN1\\Identifier' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Identifier.php', + 'FG\\ASN1\\OID' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/OID.php', + 'FG\\ASN1\\Parsable' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Parsable.php', + 'FG\\ASN1\\TemplateParser' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/TemplateParser.php', + 'FG\\ASN1\\Universal\\BMPString' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/BMPString.php', + 'FG\\ASN1\\Universal\\BitString' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/BitString.php', + 'FG\\ASN1\\Universal\\Boolean' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/Boolean.php', + 'FG\\ASN1\\Universal\\CharacterString' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/CharacterString.php', + 'FG\\ASN1\\Universal\\Enumerated' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/Enumerated.php', + 'FG\\ASN1\\Universal\\GeneralString' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/GeneralString.php', + 'FG\\ASN1\\Universal\\GeneralizedTime' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/GeneralizedTime.php', + 'FG\\ASN1\\Universal\\GraphicString' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/GraphicString.php', + 'FG\\ASN1\\Universal\\IA5String' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/IA5String.php', + 'FG\\ASN1\\Universal\\Integer' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/Integer.php', + 'FG\\ASN1\\Universal\\NullObject' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/NullObject.php', + 'FG\\ASN1\\Universal\\NumericString' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/NumericString.php', + 'FG\\ASN1\\Universal\\ObjectDescriptor' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/ObjectDescriptor.php', + 'FG\\ASN1\\Universal\\ObjectIdentifier' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/ObjectIdentifier.php', + 'FG\\ASN1\\Universal\\OctetString' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/OctetString.php', + 'FG\\ASN1\\Universal\\PrintableString' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/PrintableString.php', + 'FG\\ASN1\\Universal\\RelativeObjectIdentifier' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/RelativeObjectIdentifier.php', + 'FG\\ASN1\\Universal\\Sequence' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/Sequence.php', + 'FG\\ASN1\\Universal\\Set' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/Set.php', + 'FG\\ASN1\\Universal\\T61String' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/T61String.php', + 'FG\\ASN1\\Universal\\UTCTime' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/UTCTime.php', + 'FG\\ASN1\\Universal\\UTF8String' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/UTF8String.php', + 'FG\\ASN1\\Universal\\UniversalString' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/UniversalString.php', + 'FG\\ASN1\\Universal\\VisibleString' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/VisibleString.php', + 'FG\\ASN1\\UnknownConstructedObject' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/UnknownConstructedObject.php', + 'FG\\ASN1\\UnknownObject' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/UnknownObject.php', + 'FG\\Utility\\BigInteger' => $vendorDir . '/fgrosse/phpasn1/lib/Utility/BigInteger.php', + 'FG\\Utility\\BigIntegerBcmath' => $vendorDir . '/fgrosse/phpasn1/lib/Utility/BigIntegerBcmath.php', + 'FG\\Utility\\BigIntegerGmp' => $vendorDir . '/fgrosse/phpasn1/lib/Utility/BigIntegerGmp.php', + 'FG\\X509\\AlgorithmIdentifier' => $vendorDir . '/fgrosse/phpasn1/lib/X509/AlgorithmIdentifier.php', + 'FG\\X509\\CSR\\Attributes' => $vendorDir . '/fgrosse/phpasn1/lib/X509/CSR/Attributes.php', + 'FG\\X509\\CSR\\CSR' => $vendorDir . '/fgrosse/phpasn1/lib/X509/CSR/CSR.php', + 'FG\\X509\\CertificateExtensions' => $vendorDir . '/fgrosse/phpasn1/lib/X509/CertificateExtensions.php', + 'FG\\X509\\CertificateSubject' => $vendorDir . '/fgrosse/phpasn1/lib/X509/CertificateSubject.php', + 'FG\\X509\\PrivateKey' => $vendorDir . '/fgrosse/phpasn1/lib/X509/PrivateKey.php', + 'FG\\X509\\PublicKey' => $vendorDir . '/fgrosse/phpasn1/lib/X509/PublicKey.php', + 'FG\\X509\\SAN\\DNSName' => $vendorDir . '/fgrosse/phpasn1/lib/X509/SAN/DNSName.php', + 'FG\\X509\\SAN\\IPAddress' => $vendorDir . '/fgrosse/phpasn1/lib/X509/SAN/IPAddress.php', + 'FG\\X509\\SAN\\SubjectAlternativeNames' => $vendorDir . '/fgrosse/phpasn1/lib/X509/SAN/SubjectAlternativeNames.php', 'Facade\\IgnitionContracts\\BaseSolution' => $vendorDir . '/facade/ignition-contracts/src/BaseSolution.php', 'Facade\\IgnitionContracts\\HasSolutionsForThrowable' => $vendorDir . '/facade/ignition-contracts/src/HasSolutionsForThrowable.php', 'Facade\\IgnitionContracts\\ProvidesSolution' => $vendorDir . '/facade/ignition-contracts/src/ProvidesSolution.php', @@ -1950,6 +2005,177 @@ return array( 'Illuminate\\View\\ViewFinderInterface' => $vendorDir . '/laravel/framework/src/Illuminate/View/ViewFinderInterface.php', 'Illuminate\\View\\ViewName' => $vendorDir . '/laravel/framework/src/Illuminate/View/ViewName.php', 'Illuminate\\View\\ViewServiceProvider' => $vendorDir . '/laravel/framework/src/Illuminate/View/ViewServiceProvider.php', + 'Jose\\Component\\Checker\\AlgorithmChecker' => $vendorDir . '/web-token/jwt-checker/AlgorithmChecker.php', + 'Jose\\Component\\Checker\\AudienceChecker' => $vendorDir . '/web-token/jwt-checker/AudienceChecker.php', + 'Jose\\Component\\Checker\\ClaimChecker' => $vendorDir . '/web-token/jwt-checker/ClaimChecker.php', + 'Jose\\Component\\Checker\\ClaimCheckerManager' => $vendorDir . '/web-token/jwt-checker/ClaimCheckerManager.php', + 'Jose\\Component\\Checker\\ClaimCheckerManagerFactory' => $vendorDir . '/web-token/jwt-checker/ClaimCheckerManagerFactory.php', + 'Jose\\Component\\Checker\\ClaimExceptionInterface' => $vendorDir . '/web-token/jwt-checker/ClaimExceptionInterface.php', + 'Jose\\Component\\Checker\\ExpirationTimeChecker' => $vendorDir . '/web-token/jwt-checker/ExpirationTimeChecker.php', + 'Jose\\Component\\Checker\\HeaderChecker' => $vendorDir . '/web-token/jwt-checker/HeaderChecker.php', + 'Jose\\Component\\Checker\\HeaderCheckerManager' => $vendorDir . '/web-token/jwt-checker/HeaderCheckerManager.php', + 'Jose\\Component\\Checker\\HeaderCheckerManagerFactory' => $vendorDir . '/web-token/jwt-checker/HeaderCheckerManagerFactory.php', + 'Jose\\Component\\Checker\\InvalidClaimException' => $vendorDir . '/web-token/jwt-checker/InvalidClaimException.php', + 'Jose\\Component\\Checker\\InvalidHeaderException' => $vendorDir . '/web-token/jwt-checker/InvalidHeaderException.php', + 'Jose\\Component\\Checker\\IssuedAtChecker' => $vendorDir . '/web-token/jwt-checker/IssuedAtChecker.php', + 'Jose\\Component\\Checker\\IssuerChecker' => $vendorDir . '/web-token/jwt-checker/IssuerChecker.php', + 'Jose\\Component\\Checker\\MissingMandatoryClaimException' => $vendorDir . '/web-token/jwt-checker/MissingMandatoryClaimException.php', + 'Jose\\Component\\Checker\\MissingMandatoryHeaderParameterException' => $vendorDir . '/web-token/jwt-checker/MissingMandatoryHeaderParameterException.php', + 'Jose\\Component\\Checker\\NotBeforeChecker' => $vendorDir . '/web-token/jwt-checker/NotBeforeChecker.php', + 'Jose\\Component\\Checker\\Tests\\AlgorithmHeaderCheckerTest' => $vendorDir . '/web-token/jwt-checker/Tests/AlgorithmHeaderCheckerTest.php', + 'Jose\\Component\\Checker\\Tests\\AudienceClaimCheckerTest' => $vendorDir . '/web-token/jwt-checker/Tests/AudienceClaimCheckerTest.php', + 'Jose\\Component\\Checker\\Tests\\AudienceHeaderCheckerTest' => $vendorDir . '/web-token/jwt-checker/Tests/AudienceHeaderCheckerTest.php', + 'Jose\\Component\\Checker\\Tests\\ClaimCheckerManagerFactoryTest' => $vendorDir . '/web-token/jwt-checker/Tests/ClaimCheckerManagerFactoryTest.php', + 'Jose\\Component\\Checker\\Tests\\ClaimCheckerManagerTest' => $vendorDir . '/web-token/jwt-checker/Tests/ClaimCheckerManagerTest.php', + 'Jose\\Component\\Checker\\Tests\\ExpirationTimeClaimCheckerTest' => $vendorDir . '/web-token/jwt-checker/Tests/ExpirationTimeClaimCheckerTest.php', + 'Jose\\Component\\Checker\\Tests\\HeaderCheckerManagerFactoryTest' => $vendorDir . '/web-token/jwt-checker/Tests/HeaderCheckerManagerFactoryTest.php', + 'Jose\\Component\\Checker\\Tests\\IssuedAtClaimCheckerTest' => $vendorDir . '/web-token/jwt-checker/Tests/IssuedAtClaimCheckerTest.php', + 'Jose\\Component\\Checker\\Tests\\NotBeforeClaimCheckerTest' => $vendorDir . '/web-token/jwt-checker/Tests/NotBeforeClaimCheckerTest.php', + 'Jose\\Component\\Checker\\Tests\\Stub\\OtherToken' => $vendorDir . '/web-token/jwt-checker/Tests/Stub/OtherToken.php', + 'Jose\\Component\\Checker\\Tests\\Stub\\Token' => $vendorDir . '/web-token/jwt-checker/Tests/Stub/Token.php', + 'Jose\\Component\\Checker\\Tests\\Stub\\TokenSupport' => $vendorDir . '/web-token/jwt-checker/Tests/Stub/TokenSupport.php', + 'Jose\\Component\\Checker\\Tests\\UnencodedPayloadHeaderCheckerTest' => $vendorDir . '/web-token/jwt-checker/Tests/UnencodedPayloadHeaderCheckerTest.php', + 'Jose\\Component\\Checker\\TokenTypeSupport' => $vendorDir . '/web-token/jwt-checker/TokenTypeSupport.php', + 'Jose\\Component\\Checker\\UnencodedPayloadChecker' => $vendorDir . '/web-token/jwt-checker/UnencodedPayloadChecker.php', + 'Jose\\Component\\Core\\Algorithm' => $vendorDir . '/web-token/jwt-core/Algorithm.php', + 'Jose\\Component\\Core\\AlgorithmManager' => $vendorDir . '/web-token/jwt-core/AlgorithmManager.php', + 'Jose\\Component\\Core\\AlgorithmManagerFactory' => $vendorDir . '/web-token/jwt-core/AlgorithmManagerFactory.php', + 'Jose\\Component\\Core\\JWK' => $vendorDir . '/web-token/jwt-core/JWK.php', + 'Jose\\Component\\Core\\JWKSet' => $vendorDir . '/web-token/jwt-core/JWKSet.php', + 'Jose\\Component\\Core\\JWT' => $vendorDir . '/web-token/jwt-core/JWT.php', + 'Jose\\Component\\Core\\Tests\\AlgorithmManagerFactoryTest' => $vendorDir . '/web-token/jwt-core/Tests/AlgorithmManagerFactoryTest.php', + 'Jose\\Component\\Core\\Tests\\FooAlgorithm' => $vendorDir . '/web-token/jwt-core/Tests/FooAlgorithm.php', + 'Jose\\Component\\Core\\Tests\\JWKSetTest' => $vendorDir . '/web-token/jwt-core/Tests/JWKSetTest.php', + 'Jose\\Component\\Core\\Tests\\JWKTest' => $vendorDir . '/web-token/jwt-core/Tests/JWKTest.php', + 'Jose\\Component\\Core\\Tests\\JsonConverterTest' => $vendorDir . '/web-token/jwt-core/Tests/JsonConverterTest.php', + 'Jose\\Component\\Core\\Util\\BigInteger' => $vendorDir . '/web-token/jwt-core/Util/BigInteger.php', + 'Jose\\Component\\Core\\Util\\ECKey' => $vendorDir . '/web-token/jwt-core/Util/ECKey.php', + 'Jose\\Component\\Core\\Util\\ECSignature' => $vendorDir . '/web-token/jwt-core/Util/ECSignature.php', + 'Jose\\Component\\Core\\Util\\Hash' => $vendorDir . '/web-token/jwt-core/Util/Hash.php', + 'Jose\\Component\\Core\\Util\\JsonConverter' => $vendorDir . '/web-token/jwt-core/Util/JsonConverter.php', + 'Jose\\Component\\Core\\Util\\KeyChecker' => $vendorDir . '/web-token/jwt-core/Util/KeyChecker.php', + 'Jose\\Component\\Core\\Util\\RSAKey' => $vendorDir . '/web-token/jwt-core/Util/RSAKey.php', + 'Jose\\Component\\Encryption\\Algorithm\\ContentEncryptionAlgorithm' => $vendorDir . '/web-token/jwt-encryption/Algorithm/ContentEncryptionAlgorithm.php', + 'Jose\\Component\\Encryption\\Algorithm\\KeyEncryptionAlgorithm' => $vendorDir . '/web-token/jwt-encryption/Algorithm/KeyEncryptionAlgorithm.php', + 'Jose\\Component\\Encryption\\Algorithm\\KeyEncryption\\DirectEncryption' => $vendorDir . '/web-token/jwt-encryption/Algorithm/KeyEncryption/DirectEncryption.php', + 'Jose\\Component\\Encryption\\Algorithm\\KeyEncryption\\KeyAgreement' => $vendorDir . '/web-token/jwt-encryption/Algorithm/KeyEncryption/KeyAgreement.php', + 'Jose\\Component\\Encryption\\Algorithm\\KeyEncryption\\KeyAgreementWithKeyWrapping' => $vendorDir . '/web-token/jwt-encryption/Algorithm/KeyEncryption/KeyAgreementWithKeyWrapping.php', + 'Jose\\Component\\Encryption\\Algorithm\\KeyEncryption\\KeyEncryption' => $vendorDir . '/web-token/jwt-encryption/Algorithm/KeyEncryption/KeyEncryption.php', + 'Jose\\Component\\Encryption\\Algorithm\\KeyEncryption\\KeyWrapping' => $vendorDir . '/web-token/jwt-encryption/Algorithm/KeyEncryption/KeyWrapping.php', + 'Jose\\Component\\Encryption\\Compression\\CompressionMethod' => $vendorDir . '/web-token/jwt-encryption/Compression/CompressionMethod.php', + 'Jose\\Component\\Encryption\\Compression\\CompressionMethodManager' => $vendorDir . '/web-token/jwt-encryption/Compression/CompressionMethodManager.php', + 'Jose\\Component\\Encryption\\Compression\\CompressionMethodManagerFactory' => $vendorDir . '/web-token/jwt-encryption/Compression/CompressionMethodManagerFactory.php', + 'Jose\\Component\\Encryption\\Compression\\Deflate' => $vendorDir . '/web-token/jwt-encryption/Compression/Deflate.php', + 'Jose\\Component\\Encryption\\JWE' => $vendorDir . '/web-token/jwt-encryption/JWE.php', + 'Jose\\Component\\Encryption\\JWEBuilder' => $vendorDir . '/web-token/jwt-encryption/JWEBuilder.php', + 'Jose\\Component\\Encryption\\JWEBuilderFactory' => $vendorDir . '/web-token/jwt-encryption/JWEBuilderFactory.php', + 'Jose\\Component\\Encryption\\JWEDecrypter' => $vendorDir . '/web-token/jwt-encryption/JWEDecrypter.php', + 'Jose\\Component\\Encryption\\JWEDecrypterFactory' => $vendorDir . '/web-token/jwt-encryption/JWEDecrypterFactory.php', + 'Jose\\Component\\Encryption\\JWELoader' => $vendorDir . '/web-token/jwt-encryption/JWELoader.php', + 'Jose\\Component\\Encryption\\JWELoaderFactory' => $vendorDir . '/web-token/jwt-encryption/JWELoaderFactory.php', + 'Jose\\Component\\Encryption\\JWETokenSupport' => $vendorDir . '/web-token/jwt-encryption/JWETokenSupport.php', + 'Jose\\Component\\Encryption\\Recipient' => $vendorDir . '/web-token/jwt-encryption/Recipient.php', + 'Jose\\Component\\Encryption\\Serializer\\CompactSerializer' => $vendorDir . '/web-token/jwt-encryption/Serializer/CompactSerializer.php', + 'Jose\\Component\\Encryption\\Serializer\\JSONFlattenedSerializer' => $vendorDir . '/web-token/jwt-encryption/Serializer/JSONFlattenedSerializer.php', + 'Jose\\Component\\Encryption\\Serializer\\JSONGeneralSerializer' => $vendorDir . '/web-token/jwt-encryption/Serializer/JSONGeneralSerializer.php', + 'Jose\\Component\\Encryption\\Serializer\\JWESerializer' => $vendorDir . '/web-token/jwt-encryption/Serializer/JWESerializer.php', + 'Jose\\Component\\Encryption\\Serializer\\JWESerializerManager' => $vendorDir . '/web-token/jwt-encryption/Serializer/JWESerializerManager.php', + 'Jose\\Component\\Encryption\\Serializer\\JWESerializerManagerFactory' => $vendorDir . '/web-token/jwt-encryption/Serializer/JWESerializerManagerFactory.php', + 'Jose\\Component\\Encryption\\Tests\\CompressionTest' => $vendorDir . '/web-token/jwt-encryption/Tests/CompressionTest.php', + 'Jose\\Component\\Encryption\\Tests\\ECDHESWithX25519EncryptionTest' => $vendorDir . '/web-token/jwt-encryption/Tests/ECDHESWithX25519EncryptionTest.php', + 'Jose\\Component\\Encryption\\Tests\\EncrypterTest' => $vendorDir . '/web-token/jwt-encryption/Tests/EncrypterTest.php', + 'Jose\\Component\\Encryption\\Tests\\EncryptionTest' => $vendorDir . '/web-token/jwt-encryption/Tests/EncryptionTest.php', + 'Jose\\Component\\Encryption\\Tests\\InvalidCurveAttackTest' => $vendorDir . '/web-token/jwt-encryption/Tests/InvalidCurveAttackTest.php', + 'Jose\\Component\\Encryption\\Tests\\JWEFlattenedTest' => $vendorDir . '/web-token/jwt-encryption/Tests/JWEFlattenedTest.php', + 'Jose\\Component\\Encryption\\Tests\\JWELoaderTest' => $vendorDir . '/web-token/jwt-encryption/Tests/JWELoaderTest.php', + 'Jose\\Component\\Encryption\\Tests\\JWESplitTest' => $vendorDir . '/web-token/jwt-encryption/Tests/JWESplitTest.php', + 'Jose\\Component\\Encryption\\Tests\\RFC7520\\A128KWAndA128GCMEncryptionProtectedContentOnlyTest' => $vendorDir . '/web-token/jwt-encryption/Tests/RFC7520/A128KWAndA128GCMEncryptionProtectedContentOnlyTest.php', + 'Jose\\Component\\Encryption\\Tests\\RFC7520\\A128KWAndA128GCMEncryptionTest' => $vendorDir . '/web-token/jwt-encryption/Tests/RFC7520/A128KWAndA128GCMEncryptionTest.php', + 'Jose\\Component\\Encryption\\Tests\\RFC7520\\A128KWAndA128GCMEncryptionWithAdditionalAuthenticatedDataTest' => $vendorDir . '/web-token/jwt-encryption/Tests/RFC7520/A128KWAndA128GCMEncryptionWithAdditionalAuthenticatedDataTest.php', + 'Jose\\Component\\Encryption\\Tests\\RFC7520\\A128KWAndA128GCMEncryptionWithCompressionTest' => $vendorDir . '/web-token/jwt-encryption/Tests/RFC7520/A128KWAndA128GCMEncryptionWithCompressionTest.php', + 'Jose\\Component\\Encryption\\Tests\\RFC7520\\A128KWAndA128GCMEncryptionWithSpecificProtectedHeaderValuesTest' => $vendorDir . '/web-token/jwt-encryption/Tests/RFC7520/A128KWAndA128GCMEncryptionWithSpecificProtectedHeaderValuesTest.php', + 'Jose\\Component\\Encryption\\Tests\\RFC7520\\A256GCMKWAndA128CBC_HS256EncryptionTest' => $vendorDir . '/web-token/jwt-encryption/Tests/RFC7520/A256GCMKWAndA128CBC_HS256EncryptionTest.php', + 'Jose\\Component\\Encryption\\Tests\\RFC7520\\DirAndA128GCMEncryptionTest' => $vendorDir . '/web-token/jwt-encryption/Tests/RFC7520/DirAndA128GCMEncryptionTest.php', + 'Jose\\Component\\Encryption\\Tests\\RFC7520\\ECDH_ES_A128KWAndA128GCMEncryptionTest' => $vendorDir . '/web-token/jwt-encryption/Tests/RFC7520/ECDH_ES_A128KWAndA128GCMEncryptionTest.php', + 'Jose\\Component\\Encryption\\Tests\\RFC7520\\ECDH_ES_AndA128CBC_HS256EncryptionTest' => $vendorDir . '/web-token/jwt-encryption/Tests/RFC7520/ECDH_ES_AndA128CBC_HS256EncryptionTest.php', + 'Jose\\Component\\Encryption\\Tests\\RFC7520\\MultipleRecipientEncryptionTest' => $vendorDir . '/web-token/jwt-encryption/Tests/RFC7520/MultipleRecipientEncryptionTest.php', + 'Jose\\Component\\Encryption\\Tests\\RFC7520\\PBES2_HS512_A256KWAndA128CBC_HS256EncryptionTest' => $vendorDir . '/web-token/jwt-encryption/Tests/RFC7520/PBES2_HS512_A256KWAndA128CBC_HS256EncryptionTest.php', + 'Jose\\Component\\Encryption\\Tests\\RFC7520\\RSA1_5AndA128CBC_HS256EncryptionTest' => $vendorDir . '/web-token/jwt-encryption/Tests/RFC7520/RSA1_5AndA128CBC_HS256EncryptionTest.php', + 'Jose\\Component\\Encryption\\Tests\\RFC7520\\RSA_OAEPAndA256GCMEncryptionTest' => $vendorDir . '/web-token/jwt-encryption/Tests/RFC7520/RSA_OAEPAndA256GCMEncryptionTest.php', + 'Jose\\Component\\Encryption\\Tests\\RSAEncryptionTest' => $vendorDir . '/web-token/jwt-encryption/Tests/RSAEncryptionTest.php', + 'Jose\\Component\\Encryption\\Tests\\RSAKeyEncryptionTest' => $vendorDir . '/web-token/jwt-encryption/Tests/RSAKeyEncryptionTest.php', + 'Jose\\Component\\Encryption\\Tests\\RSAKeyWithoutAllPrimesTest' => $vendorDir . '/web-token/jwt-encryption/Tests/RSAKeyWithoutAllPrimesTest.php', + 'Jose\\Component\\Signature\\Algorithm\\ECDSA' => $vendorDir . '/web-token/jwt-signature-algorithm-ecdsa/ECDSA.php', + 'Jose\\Component\\Signature\\Algorithm\\ES256' => $vendorDir . '/web-token/jwt-signature-algorithm-ecdsa/ES256.php', + 'Jose\\Component\\Signature\\Algorithm\\ES384' => $vendorDir . '/web-token/jwt-signature-algorithm-ecdsa/ES384.php', + 'Jose\\Component\\Signature\\Algorithm\\ES512' => $vendorDir . '/web-token/jwt-signature-algorithm-ecdsa/ES512.php', + 'Jose\\Component\\Signature\\Algorithm\\HMAC' => $vendorDir . '/web-token/jwt-signature-algorithm-hmac/HMAC.php', + 'Jose\\Component\\Signature\\Algorithm\\HS256' => $vendorDir . '/web-token/jwt-signature-algorithm-hmac/HS256.php', + 'Jose\\Component\\Signature\\Algorithm\\HS384' => $vendorDir . '/web-token/jwt-signature-algorithm-hmac/HS384.php', + 'Jose\\Component\\Signature\\Algorithm\\HS512' => $vendorDir . '/web-token/jwt-signature-algorithm-hmac/HS512.php', + 'Jose\\Component\\Signature\\Algorithm\\MacAlgorithm' => $vendorDir . '/web-token/jwt-signature/Algorithm/MacAlgorithm.php', + 'Jose\\Component\\Signature\\Algorithm\\PS256' => $vendorDir . '/web-token/jwt-signature-algorithm-rsa/PS256.php', + 'Jose\\Component\\Signature\\Algorithm\\PS384' => $vendorDir . '/web-token/jwt-signature-algorithm-rsa/PS384.php', + 'Jose\\Component\\Signature\\Algorithm\\PS512' => $vendorDir . '/web-token/jwt-signature-algorithm-rsa/PS512.php', + 'Jose\\Component\\Signature\\Algorithm\\RS256' => $vendorDir . '/web-token/jwt-signature-algorithm-rsa/RS256.php', + 'Jose\\Component\\Signature\\Algorithm\\RS384' => $vendorDir . '/web-token/jwt-signature-algorithm-rsa/RS384.php', + 'Jose\\Component\\Signature\\Algorithm\\RS512' => $vendorDir . '/web-token/jwt-signature-algorithm-rsa/RS512.php', + 'Jose\\Component\\Signature\\Algorithm\\RSA' => $vendorDir . '/web-token/jwt-signature-algorithm-rsa/RSA.php', + 'Jose\\Component\\Signature\\Algorithm\\RSAPKCS1' => $vendorDir . '/web-token/jwt-signature-algorithm-rsa/RSAPKCS1.php', + 'Jose\\Component\\Signature\\Algorithm\\RSAPSS' => $vendorDir . '/web-token/jwt-signature-algorithm-rsa/RSAPSS.php', + 'Jose\\Component\\Signature\\Algorithm\\SignatureAlgorithm' => $vendorDir . '/web-token/jwt-signature/Algorithm/SignatureAlgorithm.php', + 'Jose\\Component\\Signature\\Algorithm\\Tests\\ECDSAFromRFC6979Test' => $vendorDir . '/web-token/jwt-signature-algorithm-ecdsa/Tests/ECDSAFromRFC6979Test.php', + 'Jose\\Component\\Signature\\Algorithm\\Tests\\ECDSAFromRFC7520Test' => $vendorDir . '/web-token/jwt-signature-algorithm-ecdsa/Tests/ECDSAFromRFC7520Test.php', + 'Jose\\Component\\Signature\\Algorithm\\Tests\\ECDSASignatureTest' => $vendorDir . '/web-token/jwt-signature-algorithm-ecdsa/Tests/ECDSASignatureTest.php', + 'Jose\\Component\\Signature\\Algorithm\\Tests\\HMACFromRFC7520Test' => $vendorDir . '/web-token/jwt-signature-algorithm-hmac/Tests/HMACFromRFC7520Test.php', + 'Jose\\Component\\Signature\\Algorithm\\Tests\\HMACSignatureTest' => $vendorDir . '/web-token/jwt-signature-algorithm-hmac/Tests/HMACSignatureTest.php', + 'Jose\\Component\\Signature\\Algorithm\\Tests\\RSA15SignatureTest' => $vendorDir . '/web-token/jwt-signature-algorithm-rsa/Tests/RSA15SignatureTest.php', + 'Jose\\Component\\Signature\\Algorithm\\Tests\\RSAKeyWithoutAllPrimesTest' => $vendorDir . '/web-token/jwt-signature-algorithm-rsa/Tests/RSAKeyWithoutAllPrimesTest.php', + 'Jose\\Component\\Signature\\Algorithm\\Tests\\RSAPSSSignatureTest' => $vendorDir . '/web-token/jwt-signature-algorithm-rsa/Tests/RSAPSSSignatureTest.php', + 'Jose\\Component\\Signature\\Algorithm\\Tests\\RSASignatureTest' => $vendorDir . '/web-token/jwt-signature-algorithm-rsa/Tests/RSASignatureTest.php', + 'Jose\\Component\\Signature\\Algorithm\\Util\\RSA' => $vendorDir . '/web-token/jwt-signature-algorithm-rsa/Util/RSA.php', + 'Jose\\Component\\Signature\\JWS' => $vendorDir . '/web-token/jwt-signature/JWS.php', + 'Jose\\Component\\Signature\\JWSBuilder' => $vendorDir . '/web-token/jwt-signature/JWSBuilder.php', + 'Jose\\Component\\Signature\\JWSBuilderFactory' => $vendorDir . '/web-token/jwt-signature/JWSBuilderFactory.php', + 'Jose\\Component\\Signature\\JWSLoader' => $vendorDir . '/web-token/jwt-signature/JWSLoader.php', + 'Jose\\Component\\Signature\\JWSLoaderFactory' => $vendorDir . '/web-token/jwt-signature/JWSLoaderFactory.php', + 'Jose\\Component\\Signature\\JWSTokenSupport' => $vendorDir . '/web-token/jwt-signature/JWSTokenSupport.php', + 'Jose\\Component\\Signature\\JWSVerifier' => $vendorDir . '/web-token/jwt-signature/JWSVerifier.php', + 'Jose\\Component\\Signature\\JWSVerifierFactory' => $vendorDir . '/web-token/jwt-signature/JWSVerifierFactory.php', + 'Jose\\Component\\Signature\\Serializer\\CompactSerializer' => $vendorDir . '/web-token/jwt-signature/Serializer/CompactSerializer.php', + 'Jose\\Component\\Signature\\Serializer\\JSONFlattenedSerializer' => $vendorDir . '/web-token/jwt-signature/Serializer/JSONFlattenedSerializer.php', + 'Jose\\Component\\Signature\\Serializer\\JSONGeneralSerializer' => $vendorDir . '/web-token/jwt-signature/Serializer/JSONGeneralSerializer.php', + 'Jose\\Component\\Signature\\Serializer\\JWSSerializer' => $vendorDir . '/web-token/jwt-signature/Serializer/JWSSerializer.php', + 'Jose\\Component\\Signature\\Serializer\\JWSSerializerManager' => $vendorDir . '/web-token/jwt-signature/Serializer/JWSSerializerManager.php', + 'Jose\\Component\\Signature\\Serializer\\JWSSerializerManagerFactory' => $vendorDir . '/web-token/jwt-signature/Serializer/JWSSerializerManagerFactory.php', + 'Jose\\Component\\Signature\\Serializer\\Serializer' => $vendorDir . '/web-token/jwt-signature/Serializer/Serializer.php', + 'Jose\\Component\\Signature\\Signature' => $vendorDir . '/web-token/jwt-signature/Signature.php', + 'Jose\\Component\\Signature\\Tests\\ForeignJWTTest' => $vendorDir . '/web-token/jwt-signature/Tests/ForeignJWTTest.php', + 'Jose\\Component\\Signature\\Tests\\JWSFlattenedTest' => $vendorDir . '/web-token/jwt-signature/Tests/JWSFlattenedTest.php', + 'Jose\\Component\\Signature\\Tests\\JWSLoaderTest' => $vendorDir . '/web-token/jwt-signature/Tests/JWSLoaderTest.php', + 'Jose\\Component\\Signature\\Tests\\JWSSplitTest' => $vendorDir . '/web-token/jwt-signature/Tests/JWSSplitTest.php', + 'Jose\\Component\\Signature\\Tests\\JWSTest' => $vendorDir . '/web-token/jwt-signature/Tests/JWSTest.php', + 'Jose\\Component\\Signature\\Tests\\RFC7520\\MultipleSignaturesTest' => $vendorDir . '/web-token/jwt-signature/Tests/RFC7520/MultipleSignaturesTest.php', + 'Jose\\Component\\Signature\\Tests\\RFC7520\\NestingTest' => $vendorDir . '/web-token/jwt-signature/Tests/RFC7520/NestingTest.php', + 'Jose\\Component\\Signature\\Tests\\SignatureTest' => $vendorDir . '/web-token/jwt-signature/Tests/SignatureTest.php', + 'Jose\\Component\\Signature\\Tests\\SignerTest' => $vendorDir . '/web-token/jwt-signature/Tests/SignerTest.php', + 'Jose\\Easy\\AbstractBuilder' => $vendorDir . '/web-token/jwt-easy/AbstractBuilder.php', + 'Jose\\Easy\\AbstractLoader' => $vendorDir . '/web-token/jwt-easy/AbstractLoader.php', + 'Jose\\Easy\\AlgorithmProvider' => $vendorDir . '/web-token/jwt-easy/AlgorithmProvider.php', + 'Jose\\Easy\\Build' => $vendorDir . '/web-token/jwt-easy/Build.php', + 'Jose\\Easy\\CallableChecker' => $vendorDir . '/web-token/jwt-easy/CallableChecker.php', + 'Jose\\Easy\\ContentEncryptionAlgorithmChecker' => $vendorDir . '/web-token/jwt-easy/ContentEncryptionAlgorithmChecker.php', + 'Jose\\Easy\\Decrypt' => $vendorDir . '/web-token/jwt-easy/Decrypt.php', + 'Jose\\Easy\\JWEBuilder' => $vendorDir . '/web-token/jwt-easy/JWEBuilder.php', + 'Jose\\Easy\\JWSBuilder' => $vendorDir . '/web-token/jwt-easy/JWSBuilder.php', + 'Jose\\Easy\\JWT' => $vendorDir . '/web-token/jwt-easy/JWT.php', + 'Jose\\Easy\\Load' => $vendorDir . '/web-token/jwt-easy/Load.php', + 'Jose\\Easy\\ParameterBag' => $vendorDir . '/web-token/jwt-easy/ParameterBag.php', + 'Jose\\Easy\\Tests\\AlgorithmProviderTest' => $vendorDir . '/web-token/jwt-easy/Tests/AlgorithmProviderTest.php', + 'Jose\\Easy\\Tests\\EncryptionTest' => $vendorDir . '/web-token/jwt-easy/Tests/EncryptionTest.php', + 'Jose\\Easy\\Tests\\ParameterBagTest' => $vendorDir . '/web-token/jwt-easy/Tests/ParameterBagTest.php', + 'Jose\\Easy\\Tests\\SignatureTest' => $vendorDir . '/web-token/jwt-easy/Tests/SignatureTest.php', + 'Jose\\Easy\\Validate' => $vendorDir . '/web-token/jwt-easy/Validate.php', 'JsonException' => $vendorDir . '/symfony/polyfill-php73/Resources/stubs/JsonException.php', 'Khill\\Lavacharts\\Builders\\ChartBuilder' => $vendorDir . '/khill/lavacharts/src/Builders/ChartBuilder.php', 'Khill\\Lavacharts\\Builders\\DashboardBuilder' => $vendorDir . '/khill/lavacharts/src/Builders/DashboardBuilder.php', @@ -3238,6 +3464,277 @@ return array( 'PhpOption\\None' => $vendorDir . '/phpoption/phpoption/src/PhpOption/None.php', 'PhpOption\\Option' => $vendorDir . '/phpoption/phpoption/src/PhpOption/Option.php', 'PhpOption\\Some' => $vendorDir . '/phpoption/phpoption/src/PhpOption/Some.php', + 'Predis\\Autoloader' => $vendorDir . '/predis/predis/src/Autoloader.php', + 'Predis\\Client' => $vendorDir . '/predis/predis/src/Client.php', + 'Predis\\ClientContextInterface' => $vendorDir . '/predis/predis/src/ClientContextInterface.php', + 'Predis\\ClientException' => $vendorDir . '/predis/predis/src/ClientException.php', + 'Predis\\ClientInterface' => $vendorDir . '/predis/predis/src/ClientInterface.php', + 'Predis\\Cluster\\ClusterStrategy' => $vendorDir . '/predis/predis/src/Cluster/ClusterStrategy.php', + 'Predis\\Cluster\\Distributor\\DistributorInterface' => $vendorDir . '/predis/predis/src/Cluster/Distributor/DistributorInterface.php', + 'Predis\\Cluster\\Distributor\\EmptyRingException' => $vendorDir . '/predis/predis/src/Cluster/Distributor/EmptyRingException.php', + 'Predis\\Cluster\\Distributor\\HashRing' => $vendorDir . '/predis/predis/src/Cluster/Distributor/HashRing.php', + 'Predis\\Cluster\\Distributor\\KetamaRing' => $vendorDir . '/predis/predis/src/Cluster/Distributor/KetamaRing.php', + 'Predis\\Cluster\\Hash\\CRC16' => $vendorDir . '/predis/predis/src/Cluster/Hash/CRC16.php', + 'Predis\\Cluster\\Hash\\HashGeneratorInterface' => $vendorDir . '/predis/predis/src/Cluster/Hash/HashGeneratorInterface.php', + 'Predis\\Cluster\\PredisStrategy' => $vendorDir . '/predis/predis/src/Cluster/PredisStrategy.php', + 'Predis\\Cluster\\RedisStrategy' => $vendorDir . '/predis/predis/src/Cluster/RedisStrategy.php', + 'Predis\\Cluster\\StrategyInterface' => $vendorDir . '/predis/predis/src/Cluster/StrategyInterface.php', + 'Predis\\Collection\\Iterator\\CursorBasedIterator' => $vendorDir . '/predis/predis/src/Collection/Iterator/CursorBasedIterator.php', + 'Predis\\Collection\\Iterator\\HashKey' => $vendorDir . '/predis/predis/src/Collection/Iterator/HashKey.php', + 'Predis\\Collection\\Iterator\\Keyspace' => $vendorDir . '/predis/predis/src/Collection/Iterator/Keyspace.php', + 'Predis\\Collection\\Iterator\\ListKey' => $vendorDir . '/predis/predis/src/Collection/Iterator/ListKey.php', + 'Predis\\Collection\\Iterator\\SetKey' => $vendorDir . '/predis/predis/src/Collection/Iterator/SetKey.php', + 'Predis\\Collection\\Iterator\\SortedSetKey' => $vendorDir . '/predis/predis/src/Collection/Iterator/SortedSetKey.php', + 'Predis\\Command\\Command' => $vendorDir . '/predis/predis/src/Command/Command.php', + 'Predis\\Command\\CommandInterface' => $vendorDir . '/predis/predis/src/Command/CommandInterface.php', + 'Predis\\Command\\ConnectionAuth' => $vendorDir . '/predis/predis/src/Command/ConnectionAuth.php', + 'Predis\\Command\\ConnectionEcho' => $vendorDir . '/predis/predis/src/Command/ConnectionEcho.php', + 'Predis\\Command\\ConnectionPing' => $vendorDir . '/predis/predis/src/Command/ConnectionPing.php', + 'Predis\\Command\\ConnectionQuit' => $vendorDir . '/predis/predis/src/Command/ConnectionQuit.php', + 'Predis\\Command\\ConnectionSelect' => $vendorDir . '/predis/predis/src/Command/ConnectionSelect.php', + 'Predis\\Command\\GeospatialGeoAdd' => $vendorDir . '/predis/predis/src/Command/GeospatialGeoAdd.php', + 'Predis\\Command\\GeospatialGeoDist' => $vendorDir . '/predis/predis/src/Command/GeospatialGeoDist.php', + 'Predis\\Command\\GeospatialGeoHash' => $vendorDir . '/predis/predis/src/Command/GeospatialGeoHash.php', + 'Predis\\Command\\GeospatialGeoPos' => $vendorDir . '/predis/predis/src/Command/GeospatialGeoPos.php', + 'Predis\\Command\\GeospatialGeoRadius' => $vendorDir . '/predis/predis/src/Command/GeospatialGeoRadius.php', + 'Predis\\Command\\GeospatialGeoRadiusByMember' => $vendorDir . '/predis/predis/src/Command/GeospatialGeoRadiusByMember.php', + 'Predis\\Command\\HashDelete' => $vendorDir . '/predis/predis/src/Command/HashDelete.php', + 'Predis\\Command\\HashExists' => $vendorDir . '/predis/predis/src/Command/HashExists.php', + 'Predis\\Command\\HashGet' => $vendorDir . '/predis/predis/src/Command/HashGet.php', + 'Predis\\Command\\HashGetAll' => $vendorDir . '/predis/predis/src/Command/HashGetAll.php', + 'Predis\\Command\\HashGetMultiple' => $vendorDir . '/predis/predis/src/Command/HashGetMultiple.php', + 'Predis\\Command\\HashIncrementBy' => $vendorDir . '/predis/predis/src/Command/HashIncrementBy.php', + 'Predis\\Command\\HashIncrementByFloat' => $vendorDir . '/predis/predis/src/Command/HashIncrementByFloat.php', + 'Predis\\Command\\HashKeys' => $vendorDir . '/predis/predis/src/Command/HashKeys.php', + 'Predis\\Command\\HashLength' => $vendorDir . '/predis/predis/src/Command/HashLength.php', + 'Predis\\Command\\HashScan' => $vendorDir . '/predis/predis/src/Command/HashScan.php', + 'Predis\\Command\\HashSet' => $vendorDir . '/predis/predis/src/Command/HashSet.php', + 'Predis\\Command\\HashSetMultiple' => $vendorDir . '/predis/predis/src/Command/HashSetMultiple.php', + 'Predis\\Command\\HashSetPreserve' => $vendorDir . '/predis/predis/src/Command/HashSetPreserve.php', + 'Predis\\Command\\HashStringLength' => $vendorDir . '/predis/predis/src/Command/HashStringLength.php', + 'Predis\\Command\\HashValues' => $vendorDir . '/predis/predis/src/Command/HashValues.php', + 'Predis\\Command\\HyperLogLogAdd' => $vendorDir . '/predis/predis/src/Command/HyperLogLogAdd.php', + 'Predis\\Command\\HyperLogLogCount' => $vendorDir . '/predis/predis/src/Command/HyperLogLogCount.php', + 'Predis\\Command\\HyperLogLogMerge' => $vendorDir . '/predis/predis/src/Command/HyperLogLogMerge.php', + 'Predis\\Command\\KeyDelete' => $vendorDir . '/predis/predis/src/Command/KeyDelete.php', + 'Predis\\Command\\KeyDump' => $vendorDir . '/predis/predis/src/Command/KeyDump.php', + 'Predis\\Command\\KeyExists' => $vendorDir . '/predis/predis/src/Command/KeyExists.php', + 'Predis\\Command\\KeyExpire' => $vendorDir . '/predis/predis/src/Command/KeyExpire.php', + 'Predis\\Command\\KeyExpireAt' => $vendorDir . '/predis/predis/src/Command/KeyExpireAt.php', + 'Predis\\Command\\KeyKeys' => $vendorDir . '/predis/predis/src/Command/KeyKeys.php', + 'Predis\\Command\\KeyMigrate' => $vendorDir . '/predis/predis/src/Command/KeyMigrate.php', + 'Predis\\Command\\KeyMove' => $vendorDir . '/predis/predis/src/Command/KeyMove.php', + 'Predis\\Command\\KeyPersist' => $vendorDir . '/predis/predis/src/Command/KeyPersist.php', + 'Predis\\Command\\KeyPreciseExpire' => $vendorDir . '/predis/predis/src/Command/KeyPreciseExpire.php', + 'Predis\\Command\\KeyPreciseExpireAt' => $vendorDir . '/predis/predis/src/Command/KeyPreciseExpireAt.php', + 'Predis\\Command\\KeyPreciseTimeToLive' => $vendorDir . '/predis/predis/src/Command/KeyPreciseTimeToLive.php', + 'Predis\\Command\\KeyRandom' => $vendorDir . '/predis/predis/src/Command/KeyRandom.php', + 'Predis\\Command\\KeyRename' => $vendorDir . '/predis/predis/src/Command/KeyRename.php', + 'Predis\\Command\\KeyRenamePreserve' => $vendorDir . '/predis/predis/src/Command/KeyRenamePreserve.php', + 'Predis\\Command\\KeyRestore' => $vendorDir . '/predis/predis/src/Command/KeyRestore.php', + 'Predis\\Command\\KeyScan' => $vendorDir . '/predis/predis/src/Command/KeyScan.php', + 'Predis\\Command\\KeySort' => $vendorDir . '/predis/predis/src/Command/KeySort.php', + 'Predis\\Command\\KeyTimeToLive' => $vendorDir . '/predis/predis/src/Command/KeyTimeToLive.php', + 'Predis\\Command\\KeyType' => $vendorDir . '/predis/predis/src/Command/KeyType.php', + 'Predis\\Command\\ListIndex' => $vendorDir . '/predis/predis/src/Command/ListIndex.php', + 'Predis\\Command\\ListInsert' => $vendorDir . '/predis/predis/src/Command/ListInsert.php', + 'Predis\\Command\\ListLength' => $vendorDir . '/predis/predis/src/Command/ListLength.php', + 'Predis\\Command\\ListPopFirst' => $vendorDir . '/predis/predis/src/Command/ListPopFirst.php', + 'Predis\\Command\\ListPopFirstBlocking' => $vendorDir . '/predis/predis/src/Command/ListPopFirstBlocking.php', + 'Predis\\Command\\ListPopLast' => $vendorDir . '/predis/predis/src/Command/ListPopLast.php', + 'Predis\\Command\\ListPopLastBlocking' => $vendorDir . '/predis/predis/src/Command/ListPopLastBlocking.php', + 'Predis\\Command\\ListPopLastPushHead' => $vendorDir . '/predis/predis/src/Command/ListPopLastPushHead.php', + 'Predis\\Command\\ListPopLastPushHeadBlocking' => $vendorDir . '/predis/predis/src/Command/ListPopLastPushHeadBlocking.php', + 'Predis\\Command\\ListPushHead' => $vendorDir . '/predis/predis/src/Command/ListPushHead.php', + 'Predis\\Command\\ListPushHeadX' => $vendorDir . '/predis/predis/src/Command/ListPushHeadX.php', + 'Predis\\Command\\ListPushTail' => $vendorDir . '/predis/predis/src/Command/ListPushTail.php', + 'Predis\\Command\\ListPushTailX' => $vendorDir . '/predis/predis/src/Command/ListPushTailX.php', + 'Predis\\Command\\ListRange' => $vendorDir . '/predis/predis/src/Command/ListRange.php', + 'Predis\\Command\\ListRemove' => $vendorDir . '/predis/predis/src/Command/ListRemove.php', + 'Predis\\Command\\ListSet' => $vendorDir . '/predis/predis/src/Command/ListSet.php', + 'Predis\\Command\\ListTrim' => $vendorDir . '/predis/predis/src/Command/ListTrim.php', + 'Predis\\Command\\PrefixableCommandInterface' => $vendorDir . '/predis/predis/src/Command/PrefixableCommandInterface.php', + 'Predis\\Command\\Processor\\KeyPrefixProcessor' => $vendorDir . '/predis/predis/src/Command/Processor/KeyPrefixProcessor.php', + 'Predis\\Command\\Processor\\ProcessorChain' => $vendorDir . '/predis/predis/src/Command/Processor/ProcessorChain.php', + 'Predis\\Command\\Processor\\ProcessorInterface' => $vendorDir . '/predis/predis/src/Command/Processor/ProcessorInterface.php', + 'Predis\\Command\\PubSubPublish' => $vendorDir . '/predis/predis/src/Command/PubSubPublish.php', + 'Predis\\Command\\PubSubPubsub' => $vendorDir . '/predis/predis/src/Command/PubSubPubsub.php', + 'Predis\\Command\\PubSubSubscribe' => $vendorDir . '/predis/predis/src/Command/PubSubSubscribe.php', + 'Predis\\Command\\PubSubSubscribeByPattern' => $vendorDir . '/predis/predis/src/Command/PubSubSubscribeByPattern.php', + 'Predis\\Command\\PubSubUnsubscribe' => $vendorDir . '/predis/predis/src/Command/PubSubUnsubscribe.php', + 'Predis\\Command\\PubSubUnsubscribeByPattern' => $vendorDir . '/predis/predis/src/Command/PubSubUnsubscribeByPattern.php', + 'Predis\\Command\\RawCommand' => $vendorDir . '/predis/predis/src/Command/RawCommand.php', + 'Predis\\Command\\ScriptCommand' => $vendorDir . '/predis/predis/src/Command/ScriptCommand.php', + 'Predis\\Command\\ServerBackgroundRewriteAOF' => $vendorDir . '/predis/predis/src/Command/ServerBackgroundRewriteAOF.php', + 'Predis\\Command\\ServerBackgroundSave' => $vendorDir . '/predis/predis/src/Command/ServerBackgroundSave.php', + 'Predis\\Command\\ServerClient' => $vendorDir . '/predis/predis/src/Command/ServerClient.php', + 'Predis\\Command\\ServerCommand' => $vendorDir . '/predis/predis/src/Command/ServerCommand.php', + 'Predis\\Command\\ServerConfig' => $vendorDir . '/predis/predis/src/Command/ServerConfig.php', + 'Predis\\Command\\ServerDatabaseSize' => $vendorDir . '/predis/predis/src/Command/ServerDatabaseSize.php', + 'Predis\\Command\\ServerEval' => $vendorDir . '/predis/predis/src/Command/ServerEval.php', + 'Predis\\Command\\ServerEvalSHA' => $vendorDir . '/predis/predis/src/Command/ServerEvalSHA.php', + 'Predis\\Command\\ServerFlushAll' => $vendorDir . '/predis/predis/src/Command/ServerFlushAll.php', + 'Predis\\Command\\ServerFlushDatabase' => $vendorDir . '/predis/predis/src/Command/ServerFlushDatabase.php', + 'Predis\\Command\\ServerInfo' => $vendorDir . '/predis/predis/src/Command/ServerInfo.php', + 'Predis\\Command\\ServerInfoV26x' => $vendorDir . '/predis/predis/src/Command/ServerInfoV26x.php', + 'Predis\\Command\\ServerLastSave' => $vendorDir . '/predis/predis/src/Command/ServerLastSave.php', + 'Predis\\Command\\ServerMonitor' => $vendorDir . '/predis/predis/src/Command/ServerMonitor.php', + 'Predis\\Command\\ServerObject' => $vendorDir . '/predis/predis/src/Command/ServerObject.php', + 'Predis\\Command\\ServerSave' => $vendorDir . '/predis/predis/src/Command/ServerSave.php', + 'Predis\\Command\\ServerScript' => $vendorDir . '/predis/predis/src/Command/ServerScript.php', + 'Predis\\Command\\ServerSentinel' => $vendorDir . '/predis/predis/src/Command/ServerSentinel.php', + 'Predis\\Command\\ServerShutdown' => $vendorDir . '/predis/predis/src/Command/ServerShutdown.php', + 'Predis\\Command\\ServerSlaveOf' => $vendorDir . '/predis/predis/src/Command/ServerSlaveOf.php', + 'Predis\\Command\\ServerSlowlog' => $vendorDir . '/predis/predis/src/Command/ServerSlowlog.php', + 'Predis\\Command\\ServerTime' => $vendorDir . '/predis/predis/src/Command/ServerTime.php', + 'Predis\\Command\\SetAdd' => $vendorDir . '/predis/predis/src/Command/SetAdd.php', + 'Predis\\Command\\SetCardinality' => $vendorDir . '/predis/predis/src/Command/SetCardinality.php', + 'Predis\\Command\\SetDifference' => $vendorDir . '/predis/predis/src/Command/SetDifference.php', + 'Predis\\Command\\SetDifferenceStore' => $vendorDir . '/predis/predis/src/Command/SetDifferenceStore.php', + 'Predis\\Command\\SetIntersection' => $vendorDir . '/predis/predis/src/Command/SetIntersection.php', + 'Predis\\Command\\SetIntersectionStore' => $vendorDir . '/predis/predis/src/Command/SetIntersectionStore.php', + 'Predis\\Command\\SetIsMember' => $vendorDir . '/predis/predis/src/Command/SetIsMember.php', + 'Predis\\Command\\SetMembers' => $vendorDir . '/predis/predis/src/Command/SetMembers.php', + 'Predis\\Command\\SetMove' => $vendorDir . '/predis/predis/src/Command/SetMove.php', + 'Predis\\Command\\SetPop' => $vendorDir . '/predis/predis/src/Command/SetPop.php', + 'Predis\\Command\\SetRandomMember' => $vendorDir . '/predis/predis/src/Command/SetRandomMember.php', + 'Predis\\Command\\SetRemove' => $vendorDir . '/predis/predis/src/Command/SetRemove.php', + 'Predis\\Command\\SetScan' => $vendorDir . '/predis/predis/src/Command/SetScan.php', + 'Predis\\Command\\SetUnion' => $vendorDir . '/predis/predis/src/Command/SetUnion.php', + 'Predis\\Command\\SetUnionStore' => $vendorDir . '/predis/predis/src/Command/SetUnionStore.php', + 'Predis\\Command\\StringAppend' => $vendorDir . '/predis/predis/src/Command/StringAppend.php', + 'Predis\\Command\\StringBitCount' => $vendorDir . '/predis/predis/src/Command/StringBitCount.php', + 'Predis\\Command\\StringBitField' => $vendorDir . '/predis/predis/src/Command/StringBitField.php', + 'Predis\\Command\\StringBitOp' => $vendorDir . '/predis/predis/src/Command/StringBitOp.php', + 'Predis\\Command\\StringBitPos' => $vendorDir . '/predis/predis/src/Command/StringBitPos.php', + 'Predis\\Command\\StringDecrement' => $vendorDir . '/predis/predis/src/Command/StringDecrement.php', + 'Predis\\Command\\StringDecrementBy' => $vendorDir . '/predis/predis/src/Command/StringDecrementBy.php', + 'Predis\\Command\\StringGet' => $vendorDir . '/predis/predis/src/Command/StringGet.php', + 'Predis\\Command\\StringGetBit' => $vendorDir . '/predis/predis/src/Command/StringGetBit.php', + 'Predis\\Command\\StringGetMultiple' => $vendorDir . '/predis/predis/src/Command/StringGetMultiple.php', + 'Predis\\Command\\StringGetRange' => $vendorDir . '/predis/predis/src/Command/StringGetRange.php', + 'Predis\\Command\\StringGetSet' => $vendorDir . '/predis/predis/src/Command/StringGetSet.php', + 'Predis\\Command\\StringIncrement' => $vendorDir . '/predis/predis/src/Command/StringIncrement.php', + 'Predis\\Command\\StringIncrementBy' => $vendorDir . '/predis/predis/src/Command/StringIncrementBy.php', + 'Predis\\Command\\StringIncrementByFloat' => $vendorDir . '/predis/predis/src/Command/StringIncrementByFloat.php', + 'Predis\\Command\\StringPreciseSetExpire' => $vendorDir . '/predis/predis/src/Command/StringPreciseSetExpire.php', + 'Predis\\Command\\StringSet' => $vendorDir . '/predis/predis/src/Command/StringSet.php', + 'Predis\\Command\\StringSetBit' => $vendorDir . '/predis/predis/src/Command/StringSetBit.php', + 'Predis\\Command\\StringSetExpire' => $vendorDir . '/predis/predis/src/Command/StringSetExpire.php', + 'Predis\\Command\\StringSetMultiple' => $vendorDir . '/predis/predis/src/Command/StringSetMultiple.php', + 'Predis\\Command\\StringSetMultiplePreserve' => $vendorDir . '/predis/predis/src/Command/StringSetMultiplePreserve.php', + 'Predis\\Command\\StringSetPreserve' => $vendorDir . '/predis/predis/src/Command/StringSetPreserve.php', + 'Predis\\Command\\StringSetRange' => $vendorDir . '/predis/predis/src/Command/StringSetRange.php', + 'Predis\\Command\\StringStrlen' => $vendorDir . '/predis/predis/src/Command/StringStrlen.php', + 'Predis\\Command\\StringSubstr' => $vendorDir . '/predis/predis/src/Command/StringSubstr.php', + 'Predis\\Command\\TransactionDiscard' => $vendorDir . '/predis/predis/src/Command/TransactionDiscard.php', + 'Predis\\Command\\TransactionExec' => $vendorDir . '/predis/predis/src/Command/TransactionExec.php', + 'Predis\\Command\\TransactionMulti' => $vendorDir . '/predis/predis/src/Command/TransactionMulti.php', + 'Predis\\Command\\TransactionUnwatch' => $vendorDir . '/predis/predis/src/Command/TransactionUnwatch.php', + 'Predis\\Command\\TransactionWatch' => $vendorDir . '/predis/predis/src/Command/TransactionWatch.php', + 'Predis\\Command\\ZSetAdd' => $vendorDir . '/predis/predis/src/Command/ZSetAdd.php', + 'Predis\\Command\\ZSetCardinality' => $vendorDir . '/predis/predis/src/Command/ZSetCardinality.php', + 'Predis\\Command\\ZSetCount' => $vendorDir . '/predis/predis/src/Command/ZSetCount.php', + 'Predis\\Command\\ZSetIncrementBy' => $vendorDir . '/predis/predis/src/Command/ZSetIncrementBy.php', + 'Predis\\Command\\ZSetIntersectionStore' => $vendorDir . '/predis/predis/src/Command/ZSetIntersectionStore.php', + 'Predis\\Command\\ZSetLexCount' => $vendorDir . '/predis/predis/src/Command/ZSetLexCount.php', + 'Predis\\Command\\ZSetRange' => $vendorDir . '/predis/predis/src/Command/ZSetRange.php', + 'Predis\\Command\\ZSetRangeByLex' => $vendorDir . '/predis/predis/src/Command/ZSetRangeByLex.php', + 'Predis\\Command\\ZSetRangeByScore' => $vendorDir . '/predis/predis/src/Command/ZSetRangeByScore.php', + 'Predis\\Command\\ZSetRank' => $vendorDir . '/predis/predis/src/Command/ZSetRank.php', + 'Predis\\Command\\ZSetRemove' => $vendorDir . '/predis/predis/src/Command/ZSetRemove.php', + 'Predis\\Command\\ZSetRemoveRangeByLex' => $vendorDir . '/predis/predis/src/Command/ZSetRemoveRangeByLex.php', + 'Predis\\Command\\ZSetRemoveRangeByRank' => $vendorDir . '/predis/predis/src/Command/ZSetRemoveRangeByRank.php', + 'Predis\\Command\\ZSetRemoveRangeByScore' => $vendorDir . '/predis/predis/src/Command/ZSetRemoveRangeByScore.php', + 'Predis\\Command\\ZSetReverseRange' => $vendorDir . '/predis/predis/src/Command/ZSetReverseRange.php', + 'Predis\\Command\\ZSetReverseRangeByLex' => $vendorDir . '/predis/predis/src/Command/ZSetReverseRangeByLex.php', + 'Predis\\Command\\ZSetReverseRangeByScore' => $vendorDir . '/predis/predis/src/Command/ZSetReverseRangeByScore.php', + 'Predis\\Command\\ZSetReverseRank' => $vendorDir . '/predis/predis/src/Command/ZSetReverseRank.php', + 'Predis\\Command\\ZSetScan' => $vendorDir . '/predis/predis/src/Command/ZSetScan.php', + 'Predis\\Command\\ZSetScore' => $vendorDir . '/predis/predis/src/Command/ZSetScore.php', + 'Predis\\Command\\ZSetUnionStore' => $vendorDir . '/predis/predis/src/Command/ZSetUnionStore.php', + 'Predis\\CommunicationException' => $vendorDir . '/predis/predis/src/CommunicationException.php', + 'Predis\\Configuration\\ClusterOption' => $vendorDir . '/predis/predis/src/Configuration/ClusterOption.php', + 'Predis\\Configuration\\ConnectionFactoryOption' => $vendorDir . '/predis/predis/src/Configuration/ConnectionFactoryOption.php', + 'Predis\\Configuration\\ExceptionsOption' => $vendorDir . '/predis/predis/src/Configuration/ExceptionsOption.php', + 'Predis\\Configuration\\OptionInterface' => $vendorDir . '/predis/predis/src/Configuration/OptionInterface.php', + 'Predis\\Configuration\\Options' => $vendorDir . '/predis/predis/src/Configuration/Options.php', + 'Predis\\Configuration\\OptionsInterface' => $vendorDir . '/predis/predis/src/Configuration/OptionsInterface.php', + 'Predis\\Configuration\\PrefixOption' => $vendorDir . '/predis/predis/src/Configuration/PrefixOption.php', + 'Predis\\Configuration\\ProfileOption' => $vendorDir . '/predis/predis/src/Configuration/ProfileOption.php', + 'Predis\\Configuration\\ReplicationOption' => $vendorDir . '/predis/predis/src/Configuration/ReplicationOption.php', + 'Predis\\Connection\\AbstractConnection' => $vendorDir . '/predis/predis/src/Connection/AbstractConnection.php', + 'Predis\\Connection\\AggregateConnectionInterface' => $vendorDir . '/predis/predis/src/Connection/AggregateConnectionInterface.php', + 'Predis\\Connection\\Aggregate\\ClusterInterface' => $vendorDir . '/predis/predis/src/Connection/Aggregate/ClusterInterface.php', + 'Predis\\Connection\\Aggregate\\MasterSlaveReplication' => $vendorDir . '/predis/predis/src/Connection/Aggregate/MasterSlaveReplication.php', + 'Predis\\Connection\\Aggregate\\PredisCluster' => $vendorDir . '/predis/predis/src/Connection/Aggregate/PredisCluster.php', + 'Predis\\Connection\\Aggregate\\RedisCluster' => $vendorDir . '/predis/predis/src/Connection/Aggregate/RedisCluster.php', + 'Predis\\Connection\\Aggregate\\ReplicationInterface' => $vendorDir . '/predis/predis/src/Connection/Aggregate/ReplicationInterface.php', + 'Predis\\Connection\\Aggregate\\SentinelReplication' => $vendorDir . '/predis/predis/src/Connection/Aggregate/SentinelReplication.php', + 'Predis\\Connection\\CompositeConnectionInterface' => $vendorDir . '/predis/predis/src/Connection/CompositeConnectionInterface.php', + 'Predis\\Connection\\CompositeStreamConnection' => $vendorDir . '/predis/predis/src/Connection/CompositeStreamConnection.php', + 'Predis\\Connection\\ConnectionException' => $vendorDir . '/predis/predis/src/Connection/ConnectionException.php', + 'Predis\\Connection\\ConnectionInterface' => $vendorDir . '/predis/predis/src/Connection/ConnectionInterface.php', + 'Predis\\Connection\\Factory' => $vendorDir . '/predis/predis/src/Connection/Factory.php', + 'Predis\\Connection\\FactoryInterface' => $vendorDir . '/predis/predis/src/Connection/FactoryInterface.php', + 'Predis\\Connection\\NodeConnectionInterface' => $vendorDir . '/predis/predis/src/Connection/NodeConnectionInterface.php', + 'Predis\\Connection\\Parameters' => $vendorDir . '/predis/predis/src/Connection/Parameters.php', + 'Predis\\Connection\\ParametersInterface' => $vendorDir . '/predis/predis/src/Connection/ParametersInterface.php', + 'Predis\\Connection\\PhpiredisSocketConnection' => $vendorDir . '/predis/predis/src/Connection/PhpiredisSocketConnection.php', + 'Predis\\Connection\\PhpiredisStreamConnection' => $vendorDir . '/predis/predis/src/Connection/PhpiredisStreamConnection.php', + 'Predis\\Connection\\StreamConnection' => $vendorDir . '/predis/predis/src/Connection/StreamConnection.php', + 'Predis\\Connection\\WebdisConnection' => $vendorDir . '/predis/predis/src/Connection/WebdisConnection.php', + 'Predis\\Monitor\\Consumer' => $vendorDir . '/predis/predis/src/Monitor/Consumer.php', + 'Predis\\NotSupportedException' => $vendorDir . '/predis/predis/src/NotSupportedException.php', + 'Predis\\Pipeline\\Atomic' => $vendorDir . '/predis/predis/src/Pipeline/Atomic.php', + 'Predis\\Pipeline\\ConnectionErrorProof' => $vendorDir . '/predis/predis/src/Pipeline/ConnectionErrorProof.php', + 'Predis\\Pipeline\\FireAndForget' => $vendorDir . '/predis/predis/src/Pipeline/FireAndForget.php', + 'Predis\\Pipeline\\Pipeline' => $vendorDir . '/predis/predis/src/Pipeline/Pipeline.php', + 'Predis\\PredisException' => $vendorDir . '/predis/predis/src/PredisException.php', + 'Predis\\Profile\\Factory' => $vendorDir . '/predis/predis/src/Profile/Factory.php', + 'Predis\\Profile\\ProfileInterface' => $vendorDir . '/predis/predis/src/Profile/ProfileInterface.php', + 'Predis\\Profile\\RedisProfile' => $vendorDir . '/predis/predis/src/Profile/RedisProfile.php', + 'Predis\\Profile\\RedisUnstable' => $vendorDir . '/predis/predis/src/Profile/RedisUnstable.php', + 'Predis\\Profile\\RedisVersion200' => $vendorDir . '/predis/predis/src/Profile/RedisVersion200.php', + 'Predis\\Profile\\RedisVersion220' => $vendorDir . '/predis/predis/src/Profile/RedisVersion220.php', + 'Predis\\Profile\\RedisVersion240' => $vendorDir . '/predis/predis/src/Profile/RedisVersion240.php', + 'Predis\\Profile\\RedisVersion260' => $vendorDir . '/predis/predis/src/Profile/RedisVersion260.php', + 'Predis\\Profile\\RedisVersion280' => $vendorDir . '/predis/predis/src/Profile/RedisVersion280.php', + 'Predis\\Profile\\RedisVersion300' => $vendorDir . '/predis/predis/src/Profile/RedisVersion300.php', + 'Predis\\Profile\\RedisVersion320' => $vendorDir . '/predis/predis/src/Profile/RedisVersion320.php', + 'Predis\\Protocol\\ProtocolException' => $vendorDir . '/predis/predis/src/Protocol/ProtocolException.php', + 'Predis\\Protocol\\ProtocolProcessorInterface' => $vendorDir . '/predis/predis/src/Protocol/ProtocolProcessorInterface.php', + 'Predis\\Protocol\\RequestSerializerInterface' => $vendorDir . '/predis/predis/src/Protocol/RequestSerializerInterface.php', + 'Predis\\Protocol\\ResponseReaderInterface' => $vendorDir . '/predis/predis/src/Protocol/ResponseReaderInterface.php', + 'Predis\\Protocol\\Text\\CompositeProtocolProcessor' => $vendorDir . '/predis/predis/src/Protocol/Text/CompositeProtocolProcessor.php', + 'Predis\\Protocol\\Text\\Handler\\BulkResponse' => $vendorDir . '/predis/predis/src/Protocol/Text/Handler/BulkResponse.php', + 'Predis\\Protocol\\Text\\Handler\\ErrorResponse' => $vendorDir . '/predis/predis/src/Protocol/Text/Handler/ErrorResponse.php', + 'Predis\\Protocol\\Text\\Handler\\IntegerResponse' => $vendorDir . '/predis/predis/src/Protocol/Text/Handler/IntegerResponse.php', + 'Predis\\Protocol\\Text\\Handler\\MultiBulkResponse' => $vendorDir . '/predis/predis/src/Protocol/Text/Handler/MultiBulkResponse.php', + 'Predis\\Protocol\\Text\\Handler\\ResponseHandlerInterface' => $vendorDir . '/predis/predis/src/Protocol/Text/Handler/ResponseHandlerInterface.php', + 'Predis\\Protocol\\Text\\Handler\\StatusResponse' => $vendorDir . '/predis/predis/src/Protocol/Text/Handler/StatusResponse.php', + 'Predis\\Protocol\\Text\\Handler\\StreamableMultiBulkResponse' => $vendorDir . '/predis/predis/src/Protocol/Text/Handler/StreamableMultiBulkResponse.php', + 'Predis\\Protocol\\Text\\ProtocolProcessor' => $vendorDir . '/predis/predis/src/Protocol/Text/ProtocolProcessor.php', + 'Predis\\Protocol\\Text\\RequestSerializer' => $vendorDir . '/predis/predis/src/Protocol/Text/RequestSerializer.php', + 'Predis\\Protocol\\Text\\ResponseReader' => $vendorDir . '/predis/predis/src/Protocol/Text/ResponseReader.php', + 'Predis\\PubSub\\AbstractConsumer' => $vendorDir . '/predis/predis/src/PubSub/AbstractConsumer.php', + 'Predis\\PubSub\\Consumer' => $vendorDir . '/predis/predis/src/PubSub/Consumer.php', + 'Predis\\PubSub\\DispatcherLoop' => $vendorDir . '/predis/predis/src/PubSub/DispatcherLoop.php', + 'Predis\\Replication\\MissingMasterException' => $vendorDir . '/predis/predis/src/Replication/MissingMasterException.php', + 'Predis\\Replication\\ReplicationStrategy' => $vendorDir . '/predis/predis/src/Replication/ReplicationStrategy.php', + 'Predis\\Replication\\RoleException' => $vendorDir . '/predis/predis/src/Replication/RoleException.php', + 'Predis\\Response\\Error' => $vendorDir . '/predis/predis/src/Response/Error.php', + 'Predis\\Response\\ErrorInterface' => $vendorDir . '/predis/predis/src/Response/ErrorInterface.php', + 'Predis\\Response\\Iterator\\MultiBulk' => $vendorDir . '/predis/predis/src/Response/Iterator/MultiBulk.php', + 'Predis\\Response\\Iterator\\MultiBulkIterator' => $vendorDir . '/predis/predis/src/Response/Iterator/MultiBulkIterator.php', + 'Predis\\Response\\Iterator\\MultiBulkTuple' => $vendorDir . '/predis/predis/src/Response/Iterator/MultiBulkTuple.php', + 'Predis\\Response\\ResponseInterface' => $vendorDir . '/predis/predis/src/Response/ResponseInterface.php', + 'Predis\\Response\\ServerException' => $vendorDir . '/predis/predis/src/Response/ServerException.php', + 'Predis\\Response\\Status' => $vendorDir . '/predis/predis/src/Response/Status.php', + 'Predis\\Session\\Handler' => $vendorDir . '/predis/predis/src/Session/Handler.php', + 'Predis\\Transaction\\AbortedMultiExecException' => $vendorDir . '/predis/predis/src/Transaction/AbortedMultiExecException.php', + 'Predis\\Transaction\\MultiExec' => $vendorDir . '/predis/predis/src/Transaction/MultiExec.php', + 'Predis\\Transaction\\MultiExecState' => $vendorDir . '/predis/predis/src/Transaction/MultiExecState.php', 'Prophecy\\Argument' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Argument.php', 'Prophecy\\Argument\\ArgumentsWildcard' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Argument/ArgumentsWildcard.php', 'Prophecy\\Argument\\Token\\AnyValueToken' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Argument/Token/AnyValueToken.php', @@ -3491,6 +3988,41 @@ return array( 'Ramsey\\Uuid\\UuidInterface' => $vendorDir . '/ramsey/uuid/src/UuidInterface.php', 'Ramsey\\Uuid\\Validator\\GenericValidator' => $vendorDir . '/ramsey/uuid/src/Validator/GenericValidator.php', 'Ramsey\\Uuid\\Validator\\ValidatorInterface' => $vendorDir . '/ramsey/uuid/src/Validator/ValidatorInterface.php', + 'Seat\\Eseye\\Access\\AccessInterface' => $vendorDir . '/eveseat/eseye/src/Access/AccessInterface.php', + 'Seat\\Eseye\\Access\\CheckAccess' => $vendorDir . '/eveseat/eseye/src/Access/CheckAccess.php', + 'Seat\\Eseye\\Cache\\CacheInterface' => $vendorDir . '/eveseat/eseye/src/Cache/CacheInterface.php', + 'Seat\\Eseye\\Cache\\FileCache' => $vendorDir . '/eveseat/eseye/src/Cache/FileCache.php', + 'Seat\\Eseye\\Cache\\HashesStrings' => $vendorDir . '/eveseat/eseye/src/Cache/HashesStrings.php', + 'Seat\\Eseye\\Cache\\MemcachedCache' => $vendorDir . '/eveseat/eseye/src/Cache/MemcachedCache.php', + 'Seat\\Eseye\\Cache\\NullCache' => $vendorDir . '/eveseat/eseye/src/Cache/NullCache.php', + 'Seat\\Eseye\\Cache\\RedisCache' => $vendorDir . '/eveseat/eseye/src/Cache/RedisCache.php', + 'Seat\\Eseye\\Checker\\Claim\\AzpChecker' => $vendorDir . '/eveseat/eseye/src/Checker/Claim/AzpChecker.php', + 'Seat\\Eseye\\Checker\\Claim\\NameChecker' => $vendorDir . '/eveseat/eseye/src/Checker/Claim/NameChecker.php', + 'Seat\\Eseye\\Checker\\Claim\\OwnerChecker' => $vendorDir . '/eveseat/eseye/src/Checker/Claim/OwnerChecker.php', + 'Seat\\Eseye\\Checker\\Claim\\SubEveCharacterChecker' => $vendorDir . '/eveseat/eseye/src/Checker/Claim/SubEveCharacterChecker.php', + 'Seat\\Eseye\\Checker\\Header\\TypeChecker' => $vendorDir . '/eveseat/eseye/src/Checker/Header/TypeChecker.php', + 'Seat\\Eseye\\Configuration' => $vendorDir . '/eveseat/eseye/src/Configuration.php', + 'Seat\\Eseye\\Containers\\AbstractArrayAccess' => $vendorDir . '/eveseat/eseye/src/Containers/AbstractArrayAccess.php', + 'Seat\\Eseye\\Containers\\EsiAuthentication' => $vendorDir . '/eveseat/eseye/src/Containers/EsiAuthentication.php', + 'Seat\\Eseye\\Containers\\EsiConfiguration' => $vendorDir . '/eveseat/eseye/src/Containers/EsiConfiguration.php', + 'Seat\\Eseye\\Containers\\EsiResponse' => $vendorDir . '/eveseat/eseye/src/Containers/EsiResponse.php', + 'Seat\\Eseye\\Eseye' => $vendorDir . '/eveseat/eseye/src/Eseye.php', + 'Seat\\Eseye\\Exceptions\\CachePathException' => $vendorDir . '/eveseat/eseye/src/Exceptions/CachePathException.php', + 'Seat\\Eseye\\Exceptions\\EsiScopeAccessDeniedException' => $vendorDir . '/eveseat/eseye/src/Exceptions/EsiScopeAccessDeniedException.php', + 'Seat\\Eseye\\Exceptions\\InvalidAuthenticationException' => $vendorDir . '/eveseat/eseye/src/Exceptions/InvalidAuthenticationException.php', + 'Seat\\Eseye\\Exceptions\\InvalidConfigurationException' => $vendorDir . '/eveseat/eseye/src/Exceptions/InvalidConfigurationException.php', + 'Seat\\Eseye\\Exceptions\\InvalidContainerDataException' => $vendorDir . '/eveseat/eseye/src/Exceptions/InvalidContainerDataException.php', + 'Seat\\Eseye\\Exceptions\\InvalidEsiSpecException' => $vendorDir . '/eveseat/eseye/src/Exceptions/InvalidEsiSpecException.php', + 'Seat\\Eseye\\Exceptions\\RequestFailedException' => $vendorDir . '/eveseat/eseye/src/Exceptions/RequestFailedException.php', + 'Seat\\Eseye\\Exceptions\\UriDataMissingException' => $vendorDir . '/eveseat/eseye/src/Exceptions/UriDataMissingException.php', + 'Seat\\Eseye\\Fetchers\\FetcherInterface' => $vendorDir . '/eveseat/eseye/src/Fetchers/FetcherInterface.php', + 'Seat\\Eseye\\Fetchers\\GuzzleFetcher' => $vendorDir . '/eveseat/eseye/src/Fetchers/GuzzleFetcher.php', + 'Seat\\Eseye\\Log\\FileLogger' => $vendorDir . '/eveseat/eseye/src/Log/FileLogger.php', + 'Seat\\Eseye\\Log\\LogInterface' => $vendorDir . '/eveseat/eseye/src/Log/LogInterface.php', + 'Seat\\Eseye\\Log\\NullLogger' => $vendorDir . '/eveseat/eseye/src/Log/NullLogger.php', + 'Seat\\Eseye\\Log\\RotatingFileLogger' => $vendorDir . '/eveseat/eseye/src/Log/RotatingFileLogger.php', + 'Seat\\Eseye\\Traits\\ConstructsContainers' => $vendorDir . '/eveseat/eseye/src/Traits/ConstructsContainers.php', + 'Seat\\Eseye\\Traits\\ValidatesContainers' => $vendorDir . '/eveseat/eseye/src/Traits/ValidatesContainers.php', 'SebastianBergmann\\CodeCoverage\\CodeCoverage' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage.php', 'SebastianBergmann\\CodeCoverage\\CoveredCodeNotExecutedException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/CoveredCodeNotExecutedException.php', 'SebastianBergmann\\CodeCoverage\\Driver\\Driver' => $vendorDir . '/phpunit/php-code-coverage/src/Driver/Driver.php', diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php index 8af7ebd76..31b53eda1 100644 --- a/vendor/composer/autoload_files.php +++ b/vendor/composer/autoload_files.php @@ -33,4 +33,5 @@ return array( '0d8253363903f0ac7b0978dcde4e28a0' => $vendorDir . '/beyondcode/laravel-dump-server/helpers.php', '17d016dc52a631c1e74d2eb8fdd57342' => $vendorDir . '/laravel/helpers/src/helpers.php', 'f18cc91337d49233e5754e93f3ed9ec3' => $vendorDir . '/laravelcollective/html/src/helpers.php', + '664e151c91315b3715336cbec9a6600a' => $vendorDir . '/eveseat/eseye/src/Helpers/helpers.php', ); diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index d6798f428..9618d6ffd 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -38,6 +38,7 @@ return array( 'Symfony\\Component\\ErrorHandler\\' => array($vendorDir . '/symfony/error-handler'), 'Symfony\\Component\\CssSelector\\' => array($vendorDir . '/symfony/css-selector'), 'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'), + 'Seat\\Eseye\\' => array($vendorDir . '/eveseat/eseye/src'), 'Ramsey\\Uuid\\' => array($vendorDir . '/ramsey/uuid/src'), 'Ramsey\\Collection\\' => array($vendorDir . '/ramsey/collection/src'), 'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'), @@ -46,6 +47,7 @@ return array( 'Psr\\EventDispatcher\\' => array($vendorDir . '/psr/event-dispatcher/src'), 'Psr\\Container\\' => array($vendorDir . '/psr/container/src'), 'Prophecy\\' => array($vendorDir . '/phpspec/prophecy/src/Prophecy'), + 'Predis\\' => array($vendorDir . '/predis/predis/src'), 'PhpOption\\' => array($vendorDir . '/phpoption/phpoption/src/PhpOption'), 'Opis\\Closure\\' => array($vendorDir . '/opis/closure/src'), 'NunoMaduro\\Collision\\' => array($vendorDir . '/nunomaduro/collision/src'), @@ -58,6 +60,12 @@ return array( 'Laravel\\Socialite\\' => array($vendorDir . '/laravel/socialite/src'), 'Laravel\\Horizon\\' => array($vendorDir . '/laravel/horizon/src'), 'Khill\\Lavacharts\\' => array($vendorDir . '/khill/lavacharts/src'), + 'Jose\\Easy\\' => array($vendorDir . '/web-token/jwt-easy'), + 'Jose\\Component\\Signature\\Algorithm\\' => array($vendorDir . '/web-token/jwt-signature-algorithm-rsa', $vendorDir . '/web-token/jwt-signature-algorithm-hmac', $vendorDir . '/web-token/jwt-signature-algorithm-ecdsa'), + 'Jose\\Component\\Signature\\' => array($vendorDir . '/web-token/jwt-signature'), + 'Jose\\Component\\Encryption\\' => array($vendorDir . '/web-token/jwt-encryption'), + 'Jose\\Component\\Core\\' => array($vendorDir . '/web-token/jwt-core'), + 'Jose\\Component\\Checker\\' => array($vendorDir . '/web-token/jwt-checker'), 'Illuminate\\Foundation\\Auth\\' => array($vendorDir . '/laravel/ui/auth-backend'), 'Illuminate\\' => array($vendorDir . '/laravel/framework/src/Illuminate'), 'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'), @@ -66,6 +74,7 @@ return array( 'Fideloper\\Proxy\\' => array($vendorDir . '/fideloper/proxy/src'), 'Faker\\' => array($vendorDir . '/fzaninotto/faker/src/Faker'), 'Facade\\IgnitionContracts\\' => array($vendorDir . '/facade/ignition-contracts/src'), + 'FG\\' => array($vendorDir . '/fgrosse/phpasn1/lib'), 'Egulias\\EmailValidator\\' => array($vendorDir . '/egulias/email-validator/src'), 'Dotenv\\' => array($vendorDir . '/vlucas/phpdotenv/src'), 'Doctrine\\Instantiator\\' => array($vendorDir . '/doctrine/instantiator/src/Doctrine/Instantiator'), @@ -78,5 +87,6 @@ return array( 'Cake\\Chronos\\' => array($vendorDir . '/cakephp/chronos/src'), 'Brick\\Math\\' => array($vendorDir . '/brick/math/src'), 'BeyondCode\\DumpServer\\' => array($vendorDir . '/beyondcode/laravel-dump-server/src'), + 'Base64Url\\' => array($vendorDir . '/spomky-labs/base64url/src'), 'App\\' => array($baseDir . '/app'), ); diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index ef04cacc9..31ca2676a 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -34,6 +34,7 @@ class ComposerStaticInitc3f953f8a7291d41a76e1664339777c9 '0d8253363903f0ac7b0978dcde4e28a0' => __DIR__ . '/..' . '/beyondcode/laravel-dump-server/helpers.php', '17d016dc52a631c1e74d2eb8fdd57342' => __DIR__ . '/..' . '/laravel/helpers/src/helpers.php', 'f18cc91337d49233e5754e93f3ed9ec3' => __DIR__ . '/..' . '/laravelcollective/html/src/helpers.php', + '664e151c91315b3715336cbec9a6600a' => __DIR__ . '/..' . '/eveseat/eseye/src/Helpers/helpers.php', ); public static $prefixLengthsPsr4 = array ( @@ -83,6 +84,7 @@ class ComposerStaticInitc3f953f8a7291d41a76e1664339777c9 'Symfony\\Component\\ErrorHandler\\' => 31, 'Symfony\\Component\\CssSelector\\' => 30, 'Symfony\\Component\\Console\\' => 26, + 'Seat\\Eseye\\' => 11, ), 'R' => array ( @@ -97,6 +99,7 @@ class ComposerStaticInitc3f953f8a7291d41a76e1664339777c9 'Psr\\EventDispatcher\\' => 20, 'Psr\\Container\\' => 14, 'Prophecy\\' => 9, + 'Predis\\' => 7, 'PhpOption\\' => 10, ), 'O' => @@ -125,6 +128,15 @@ class ComposerStaticInitc3f953f8a7291d41a76e1664339777c9 array ( 'Khill\\Lavacharts\\' => 17, ), + 'J' => + array ( + 'Jose\\Easy\\' => 10, + 'Jose\\Component\\Signature\\Algorithm\\' => 35, + 'Jose\\Component\\Signature\\' => 25, + 'Jose\\Component\\Encryption\\' => 26, + 'Jose\\Component\\Core\\' => 20, + 'Jose\\Component\\Checker\\' => 23, + ), 'I' => array ( 'Illuminate\\Foundation\\Auth\\' => 27, @@ -141,6 +153,7 @@ class ComposerStaticInitc3f953f8a7291d41a76e1664339777c9 'Fideloper\\Proxy\\' => 16, 'Faker\\' => 6, 'Facade\\IgnitionContracts\\' => 25, + 'FG\\' => 3, ), 'E' => array ( @@ -165,6 +178,7 @@ class ComposerStaticInitc3f953f8a7291d41a76e1664339777c9 array ( 'Brick\\Math\\' => 11, 'BeyondCode\\DumpServer\\' => 22, + 'Base64Url\\' => 10, ), 'A' => array ( @@ -303,6 +317,10 @@ class ComposerStaticInitc3f953f8a7291d41a76e1664339777c9 array ( 0 => __DIR__ . '/..' . '/symfony/console', ), + 'Seat\\Eseye\\' => + array ( + 0 => __DIR__ . '/..' . '/eveseat/eseye/src', + ), 'Ramsey\\Uuid\\' => array ( 0 => __DIR__ . '/..' . '/ramsey/uuid/src', @@ -335,6 +353,10 @@ class ComposerStaticInitc3f953f8a7291d41a76e1664339777c9 array ( 0 => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy', ), + 'Predis\\' => + array ( + 0 => __DIR__ . '/..' . '/predis/predis/src', + ), 'PhpOption\\' => array ( 0 => __DIR__ . '/..' . '/phpoption/phpoption/src/PhpOption', @@ -383,6 +405,32 @@ class ComposerStaticInitc3f953f8a7291d41a76e1664339777c9 array ( 0 => __DIR__ . '/..' . '/khill/lavacharts/src', ), + 'Jose\\Easy\\' => + array ( + 0 => __DIR__ . '/..' . '/web-token/jwt-easy', + ), + 'Jose\\Component\\Signature\\Algorithm\\' => + array ( + 0 => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-rsa', + 1 => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-hmac', + 2 => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-ecdsa', + ), + 'Jose\\Component\\Signature\\' => + array ( + 0 => __DIR__ . '/..' . '/web-token/jwt-signature', + ), + 'Jose\\Component\\Encryption\\' => + array ( + 0 => __DIR__ . '/..' . '/web-token/jwt-encryption', + ), + 'Jose\\Component\\Core\\' => + array ( + 0 => __DIR__ . '/..' . '/web-token/jwt-core', + ), + 'Jose\\Component\\Checker\\' => + array ( + 0 => __DIR__ . '/..' . '/web-token/jwt-checker', + ), 'Illuminate\\Foundation\\Auth\\' => array ( 0 => __DIR__ . '/..' . '/laravel/ui/auth-backend', @@ -415,6 +463,10 @@ class ComposerStaticInitc3f953f8a7291d41a76e1664339777c9 array ( 0 => __DIR__ . '/..' . '/facade/ignition-contracts/src', ), + 'FG\\' => + array ( + 0 => __DIR__ . '/..' . '/fgrosse/phpasn1/lib', + ), 'Egulias\\EmailValidator\\' => array ( 0 => __DIR__ . '/..' . '/egulias/email-validator/src', @@ -463,6 +515,10 @@ class ComposerStaticInitc3f953f8a7291d41a76e1664339777c9 array ( 0 => __DIR__ . '/..' . '/beyondcode/laravel-dump-server/src', ), + 'Base64Url\\' => + array ( + 0 => __DIR__ . '/..' . '/spomky-labs/base64url/src', + ), 'App\\' => array ( 0 => __DIR__ . '/../..' . '/app', @@ -639,6 +695,7 @@ class ComposerStaticInitc3f953f8a7291d41a76e1664339777c9 'App\\Traits\\EveOAuth' => __DIR__ . '/../..' . '/app/Traits/EveOAuth.php', 'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', 'AvailableUserPermissions' => __DIR__ . '/../..' . '/database/seeds/AvailableUserPermissions.php', + 'Base64Url\\Base64Url' => __DIR__ . '/..' . '/spomky-labs/base64url/src/Base64Url.php', 'BeyondCode\\DumpServer\\DumpServerCommand' => __DIR__ . '/..' . '/beyondcode/laravel-dump-server/src/DumpServerCommand.php', 'BeyondCode\\DumpServer\\DumpServerServiceProvider' => __DIR__ . '/..' . '/beyondcode/laravel-dump-server/src/DumpServerServiceProvider.php', 'BeyondCode\\DumpServer\\Dumper' => __DIR__ . '/..' . '/beyondcode/laravel-dump-server/src/Dumper.php', @@ -938,6 +995,60 @@ class ComposerStaticInitc3f953f8a7291d41a76e1664339777c9 'Egulias\\EmailValidator\\Warning\\QuotedString' => __DIR__ . '/..' . '/egulias/email-validator/src/Warning/QuotedString.php', 'Egulias\\EmailValidator\\Warning\\TLD' => __DIR__ . '/..' . '/egulias/email-validator/src/Warning/TLD.php', 'Egulias\\EmailValidator\\Warning\\Warning' => __DIR__ . '/..' . '/egulias/email-validator/src/Warning/Warning.php', + 'FG\\ASN1\\ASNObject' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/ASNObject.php', + 'FG\\ASN1\\AbstractString' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/AbstractString.php', + 'FG\\ASN1\\AbstractTime' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/AbstractTime.php', + 'FG\\ASN1\\Base128' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Base128.php', + 'FG\\ASN1\\Composite\\AttributeTypeAndValue' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Composite/AttributeTypeAndValue.php', + 'FG\\ASN1\\Composite\\RDNString' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Composite/RDNString.php', + 'FG\\ASN1\\Composite\\RelativeDistinguishedName' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Composite/RelativeDistinguishedName.php', + 'FG\\ASN1\\Construct' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Construct.php', + 'FG\\ASN1\\Exception\\NotImplementedException' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Exception/NotImplementedException.php', + 'FG\\ASN1\\Exception\\ParserException' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Exception/ParserException.php', + 'FG\\ASN1\\ExplicitlyTaggedObject' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/ExplicitlyTaggedObject.php', + 'FG\\ASN1\\Identifier' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Identifier.php', + 'FG\\ASN1\\OID' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/OID.php', + 'FG\\ASN1\\Parsable' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Parsable.php', + 'FG\\ASN1\\TemplateParser' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/TemplateParser.php', + 'FG\\ASN1\\Universal\\BMPString' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/BMPString.php', + 'FG\\ASN1\\Universal\\BitString' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/BitString.php', + 'FG\\ASN1\\Universal\\Boolean' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/Boolean.php', + 'FG\\ASN1\\Universal\\CharacterString' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/CharacterString.php', + 'FG\\ASN1\\Universal\\Enumerated' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/Enumerated.php', + 'FG\\ASN1\\Universal\\GeneralString' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/GeneralString.php', + 'FG\\ASN1\\Universal\\GeneralizedTime' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/GeneralizedTime.php', + 'FG\\ASN1\\Universal\\GraphicString' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/GraphicString.php', + 'FG\\ASN1\\Universal\\IA5String' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/IA5String.php', + 'FG\\ASN1\\Universal\\Integer' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/Integer.php', + 'FG\\ASN1\\Universal\\NullObject' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/NullObject.php', + 'FG\\ASN1\\Universal\\NumericString' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/NumericString.php', + 'FG\\ASN1\\Universal\\ObjectDescriptor' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/ObjectDescriptor.php', + 'FG\\ASN1\\Universal\\ObjectIdentifier' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/ObjectIdentifier.php', + 'FG\\ASN1\\Universal\\OctetString' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/OctetString.php', + 'FG\\ASN1\\Universal\\PrintableString' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/PrintableString.php', + 'FG\\ASN1\\Universal\\RelativeObjectIdentifier' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/RelativeObjectIdentifier.php', + 'FG\\ASN1\\Universal\\Sequence' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/Sequence.php', + 'FG\\ASN1\\Universal\\Set' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/Set.php', + 'FG\\ASN1\\Universal\\T61String' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/T61String.php', + 'FG\\ASN1\\Universal\\UTCTime' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/UTCTime.php', + 'FG\\ASN1\\Universal\\UTF8String' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/UTF8String.php', + 'FG\\ASN1\\Universal\\UniversalString' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/UniversalString.php', + 'FG\\ASN1\\Universal\\VisibleString' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/VisibleString.php', + 'FG\\ASN1\\UnknownConstructedObject' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/UnknownConstructedObject.php', + 'FG\\ASN1\\UnknownObject' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/UnknownObject.php', + 'FG\\Utility\\BigInteger' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/Utility/BigInteger.php', + 'FG\\Utility\\BigIntegerBcmath' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/Utility/BigIntegerBcmath.php', + 'FG\\Utility\\BigIntegerGmp' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/Utility/BigIntegerGmp.php', + 'FG\\X509\\AlgorithmIdentifier' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/X509/AlgorithmIdentifier.php', + 'FG\\X509\\CSR\\Attributes' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/X509/CSR/Attributes.php', + 'FG\\X509\\CSR\\CSR' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/X509/CSR/CSR.php', + 'FG\\X509\\CertificateExtensions' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/X509/CertificateExtensions.php', + 'FG\\X509\\CertificateSubject' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/X509/CertificateSubject.php', + 'FG\\X509\\PrivateKey' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/X509/PrivateKey.php', + 'FG\\X509\\PublicKey' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/X509/PublicKey.php', + 'FG\\X509\\SAN\\DNSName' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/X509/SAN/DNSName.php', + 'FG\\X509\\SAN\\IPAddress' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/X509/SAN/IPAddress.php', + 'FG\\X509\\SAN\\SubjectAlternativeNames' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/X509/SAN/SubjectAlternativeNames.php', 'Facade\\IgnitionContracts\\BaseSolution' => __DIR__ . '/..' . '/facade/ignition-contracts/src/BaseSolution.php', 'Facade\\IgnitionContracts\\HasSolutionsForThrowable' => __DIR__ . '/..' . '/facade/ignition-contracts/src/HasSolutionsForThrowable.php', 'Facade\\IgnitionContracts\\ProvidesSolution' => __DIR__ . '/..' . '/facade/ignition-contracts/src/ProvidesSolution.php', @@ -2424,6 +2535,177 @@ class ComposerStaticInitc3f953f8a7291d41a76e1664339777c9 'Illuminate\\View\\ViewFinderInterface' => __DIR__ . '/..' . '/laravel/framework/src/Illuminate/View/ViewFinderInterface.php', 'Illuminate\\View\\ViewName' => __DIR__ . '/..' . '/laravel/framework/src/Illuminate/View/ViewName.php', 'Illuminate\\View\\ViewServiceProvider' => __DIR__ . '/..' . '/laravel/framework/src/Illuminate/View/ViewServiceProvider.php', + 'Jose\\Component\\Checker\\AlgorithmChecker' => __DIR__ . '/..' . '/web-token/jwt-checker/AlgorithmChecker.php', + 'Jose\\Component\\Checker\\AudienceChecker' => __DIR__ . '/..' . '/web-token/jwt-checker/AudienceChecker.php', + 'Jose\\Component\\Checker\\ClaimChecker' => __DIR__ . '/..' . '/web-token/jwt-checker/ClaimChecker.php', + 'Jose\\Component\\Checker\\ClaimCheckerManager' => __DIR__ . '/..' . '/web-token/jwt-checker/ClaimCheckerManager.php', + 'Jose\\Component\\Checker\\ClaimCheckerManagerFactory' => __DIR__ . '/..' . '/web-token/jwt-checker/ClaimCheckerManagerFactory.php', + 'Jose\\Component\\Checker\\ClaimExceptionInterface' => __DIR__ . '/..' . '/web-token/jwt-checker/ClaimExceptionInterface.php', + 'Jose\\Component\\Checker\\ExpirationTimeChecker' => __DIR__ . '/..' . '/web-token/jwt-checker/ExpirationTimeChecker.php', + 'Jose\\Component\\Checker\\HeaderChecker' => __DIR__ . '/..' . '/web-token/jwt-checker/HeaderChecker.php', + 'Jose\\Component\\Checker\\HeaderCheckerManager' => __DIR__ . '/..' . '/web-token/jwt-checker/HeaderCheckerManager.php', + 'Jose\\Component\\Checker\\HeaderCheckerManagerFactory' => __DIR__ . '/..' . '/web-token/jwt-checker/HeaderCheckerManagerFactory.php', + 'Jose\\Component\\Checker\\InvalidClaimException' => __DIR__ . '/..' . '/web-token/jwt-checker/InvalidClaimException.php', + 'Jose\\Component\\Checker\\InvalidHeaderException' => __DIR__ . '/..' . '/web-token/jwt-checker/InvalidHeaderException.php', + 'Jose\\Component\\Checker\\IssuedAtChecker' => __DIR__ . '/..' . '/web-token/jwt-checker/IssuedAtChecker.php', + 'Jose\\Component\\Checker\\IssuerChecker' => __DIR__ . '/..' . '/web-token/jwt-checker/IssuerChecker.php', + 'Jose\\Component\\Checker\\MissingMandatoryClaimException' => __DIR__ . '/..' . '/web-token/jwt-checker/MissingMandatoryClaimException.php', + 'Jose\\Component\\Checker\\MissingMandatoryHeaderParameterException' => __DIR__ . '/..' . '/web-token/jwt-checker/MissingMandatoryHeaderParameterException.php', + 'Jose\\Component\\Checker\\NotBeforeChecker' => __DIR__ . '/..' . '/web-token/jwt-checker/NotBeforeChecker.php', + 'Jose\\Component\\Checker\\Tests\\AlgorithmHeaderCheckerTest' => __DIR__ . '/..' . '/web-token/jwt-checker/Tests/AlgorithmHeaderCheckerTest.php', + 'Jose\\Component\\Checker\\Tests\\AudienceClaimCheckerTest' => __DIR__ . '/..' . '/web-token/jwt-checker/Tests/AudienceClaimCheckerTest.php', + 'Jose\\Component\\Checker\\Tests\\AudienceHeaderCheckerTest' => __DIR__ . '/..' . '/web-token/jwt-checker/Tests/AudienceHeaderCheckerTest.php', + 'Jose\\Component\\Checker\\Tests\\ClaimCheckerManagerFactoryTest' => __DIR__ . '/..' . '/web-token/jwt-checker/Tests/ClaimCheckerManagerFactoryTest.php', + 'Jose\\Component\\Checker\\Tests\\ClaimCheckerManagerTest' => __DIR__ . '/..' . '/web-token/jwt-checker/Tests/ClaimCheckerManagerTest.php', + 'Jose\\Component\\Checker\\Tests\\ExpirationTimeClaimCheckerTest' => __DIR__ . '/..' . '/web-token/jwt-checker/Tests/ExpirationTimeClaimCheckerTest.php', + 'Jose\\Component\\Checker\\Tests\\HeaderCheckerManagerFactoryTest' => __DIR__ . '/..' . '/web-token/jwt-checker/Tests/HeaderCheckerManagerFactoryTest.php', + 'Jose\\Component\\Checker\\Tests\\IssuedAtClaimCheckerTest' => __DIR__ . '/..' . '/web-token/jwt-checker/Tests/IssuedAtClaimCheckerTest.php', + 'Jose\\Component\\Checker\\Tests\\NotBeforeClaimCheckerTest' => __DIR__ . '/..' . '/web-token/jwt-checker/Tests/NotBeforeClaimCheckerTest.php', + 'Jose\\Component\\Checker\\Tests\\Stub\\OtherToken' => __DIR__ . '/..' . '/web-token/jwt-checker/Tests/Stub/OtherToken.php', + 'Jose\\Component\\Checker\\Tests\\Stub\\Token' => __DIR__ . '/..' . '/web-token/jwt-checker/Tests/Stub/Token.php', + 'Jose\\Component\\Checker\\Tests\\Stub\\TokenSupport' => __DIR__ . '/..' . '/web-token/jwt-checker/Tests/Stub/TokenSupport.php', + 'Jose\\Component\\Checker\\Tests\\UnencodedPayloadHeaderCheckerTest' => __DIR__ . '/..' . '/web-token/jwt-checker/Tests/UnencodedPayloadHeaderCheckerTest.php', + 'Jose\\Component\\Checker\\TokenTypeSupport' => __DIR__ . '/..' . '/web-token/jwt-checker/TokenTypeSupport.php', + 'Jose\\Component\\Checker\\UnencodedPayloadChecker' => __DIR__ . '/..' . '/web-token/jwt-checker/UnencodedPayloadChecker.php', + 'Jose\\Component\\Core\\Algorithm' => __DIR__ . '/..' . '/web-token/jwt-core/Algorithm.php', + 'Jose\\Component\\Core\\AlgorithmManager' => __DIR__ . '/..' . '/web-token/jwt-core/AlgorithmManager.php', + 'Jose\\Component\\Core\\AlgorithmManagerFactory' => __DIR__ . '/..' . '/web-token/jwt-core/AlgorithmManagerFactory.php', + 'Jose\\Component\\Core\\JWK' => __DIR__ . '/..' . '/web-token/jwt-core/JWK.php', + 'Jose\\Component\\Core\\JWKSet' => __DIR__ . '/..' . '/web-token/jwt-core/JWKSet.php', + 'Jose\\Component\\Core\\JWT' => __DIR__ . '/..' . '/web-token/jwt-core/JWT.php', + 'Jose\\Component\\Core\\Tests\\AlgorithmManagerFactoryTest' => __DIR__ . '/..' . '/web-token/jwt-core/Tests/AlgorithmManagerFactoryTest.php', + 'Jose\\Component\\Core\\Tests\\FooAlgorithm' => __DIR__ . '/..' . '/web-token/jwt-core/Tests/FooAlgorithm.php', + 'Jose\\Component\\Core\\Tests\\JWKSetTest' => __DIR__ . '/..' . '/web-token/jwt-core/Tests/JWKSetTest.php', + 'Jose\\Component\\Core\\Tests\\JWKTest' => __DIR__ . '/..' . '/web-token/jwt-core/Tests/JWKTest.php', + 'Jose\\Component\\Core\\Tests\\JsonConverterTest' => __DIR__ . '/..' . '/web-token/jwt-core/Tests/JsonConverterTest.php', + 'Jose\\Component\\Core\\Util\\BigInteger' => __DIR__ . '/..' . '/web-token/jwt-core/Util/BigInteger.php', + 'Jose\\Component\\Core\\Util\\ECKey' => __DIR__ . '/..' . '/web-token/jwt-core/Util/ECKey.php', + 'Jose\\Component\\Core\\Util\\ECSignature' => __DIR__ . '/..' . '/web-token/jwt-core/Util/ECSignature.php', + 'Jose\\Component\\Core\\Util\\Hash' => __DIR__ . '/..' . '/web-token/jwt-core/Util/Hash.php', + 'Jose\\Component\\Core\\Util\\JsonConverter' => __DIR__ . '/..' . '/web-token/jwt-core/Util/JsonConverter.php', + 'Jose\\Component\\Core\\Util\\KeyChecker' => __DIR__ . '/..' . '/web-token/jwt-core/Util/KeyChecker.php', + 'Jose\\Component\\Core\\Util\\RSAKey' => __DIR__ . '/..' . '/web-token/jwt-core/Util/RSAKey.php', + 'Jose\\Component\\Encryption\\Algorithm\\ContentEncryptionAlgorithm' => __DIR__ . '/..' . '/web-token/jwt-encryption/Algorithm/ContentEncryptionAlgorithm.php', + 'Jose\\Component\\Encryption\\Algorithm\\KeyEncryptionAlgorithm' => __DIR__ . '/..' . '/web-token/jwt-encryption/Algorithm/KeyEncryptionAlgorithm.php', + 'Jose\\Component\\Encryption\\Algorithm\\KeyEncryption\\DirectEncryption' => __DIR__ . '/..' . '/web-token/jwt-encryption/Algorithm/KeyEncryption/DirectEncryption.php', + 'Jose\\Component\\Encryption\\Algorithm\\KeyEncryption\\KeyAgreement' => __DIR__ . '/..' . '/web-token/jwt-encryption/Algorithm/KeyEncryption/KeyAgreement.php', + 'Jose\\Component\\Encryption\\Algorithm\\KeyEncryption\\KeyAgreementWithKeyWrapping' => __DIR__ . '/..' . '/web-token/jwt-encryption/Algorithm/KeyEncryption/KeyAgreementWithKeyWrapping.php', + 'Jose\\Component\\Encryption\\Algorithm\\KeyEncryption\\KeyEncryption' => __DIR__ . '/..' . '/web-token/jwt-encryption/Algorithm/KeyEncryption/KeyEncryption.php', + 'Jose\\Component\\Encryption\\Algorithm\\KeyEncryption\\KeyWrapping' => __DIR__ . '/..' . '/web-token/jwt-encryption/Algorithm/KeyEncryption/KeyWrapping.php', + 'Jose\\Component\\Encryption\\Compression\\CompressionMethod' => __DIR__ . '/..' . '/web-token/jwt-encryption/Compression/CompressionMethod.php', + 'Jose\\Component\\Encryption\\Compression\\CompressionMethodManager' => __DIR__ . '/..' . '/web-token/jwt-encryption/Compression/CompressionMethodManager.php', + 'Jose\\Component\\Encryption\\Compression\\CompressionMethodManagerFactory' => __DIR__ . '/..' . '/web-token/jwt-encryption/Compression/CompressionMethodManagerFactory.php', + 'Jose\\Component\\Encryption\\Compression\\Deflate' => __DIR__ . '/..' . '/web-token/jwt-encryption/Compression/Deflate.php', + 'Jose\\Component\\Encryption\\JWE' => __DIR__ . '/..' . '/web-token/jwt-encryption/JWE.php', + 'Jose\\Component\\Encryption\\JWEBuilder' => __DIR__ . '/..' . '/web-token/jwt-encryption/JWEBuilder.php', + 'Jose\\Component\\Encryption\\JWEBuilderFactory' => __DIR__ . '/..' . '/web-token/jwt-encryption/JWEBuilderFactory.php', + 'Jose\\Component\\Encryption\\JWEDecrypter' => __DIR__ . '/..' . '/web-token/jwt-encryption/JWEDecrypter.php', + 'Jose\\Component\\Encryption\\JWEDecrypterFactory' => __DIR__ . '/..' . '/web-token/jwt-encryption/JWEDecrypterFactory.php', + 'Jose\\Component\\Encryption\\JWELoader' => __DIR__ . '/..' . '/web-token/jwt-encryption/JWELoader.php', + 'Jose\\Component\\Encryption\\JWELoaderFactory' => __DIR__ . '/..' . '/web-token/jwt-encryption/JWELoaderFactory.php', + 'Jose\\Component\\Encryption\\JWETokenSupport' => __DIR__ . '/..' . '/web-token/jwt-encryption/JWETokenSupport.php', + 'Jose\\Component\\Encryption\\Recipient' => __DIR__ . '/..' . '/web-token/jwt-encryption/Recipient.php', + 'Jose\\Component\\Encryption\\Serializer\\CompactSerializer' => __DIR__ . '/..' . '/web-token/jwt-encryption/Serializer/CompactSerializer.php', + 'Jose\\Component\\Encryption\\Serializer\\JSONFlattenedSerializer' => __DIR__ . '/..' . '/web-token/jwt-encryption/Serializer/JSONFlattenedSerializer.php', + 'Jose\\Component\\Encryption\\Serializer\\JSONGeneralSerializer' => __DIR__ . '/..' . '/web-token/jwt-encryption/Serializer/JSONGeneralSerializer.php', + 'Jose\\Component\\Encryption\\Serializer\\JWESerializer' => __DIR__ . '/..' . '/web-token/jwt-encryption/Serializer/JWESerializer.php', + 'Jose\\Component\\Encryption\\Serializer\\JWESerializerManager' => __DIR__ . '/..' . '/web-token/jwt-encryption/Serializer/JWESerializerManager.php', + 'Jose\\Component\\Encryption\\Serializer\\JWESerializerManagerFactory' => __DIR__ . '/..' . '/web-token/jwt-encryption/Serializer/JWESerializerManagerFactory.php', + 'Jose\\Component\\Encryption\\Tests\\CompressionTest' => __DIR__ . '/..' . '/web-token/jwt-encryption/Tests/CompressionTest.php', + 'Jose\\Component\\Encryption\\Tests\\ECDHESWithX25519EncryptionTest' => __DIR__ . '/..' . '/web-token/jwt-encryption/Tests/ECDHESWithX25519EncryptionTest.php', + 'Jose\\Component\\Encryption\\Tests\\EncrypterTest' => __DIR__ . '/..' . '/web-token/jwt-encryption/Tests/EncrypterTest.php', + 'Jose\\Component\\Encryption\\Tests\\EncryptionTest' => __DIR__ . '/..' . '/web-token/jwt-encryption/Tests/EncryptionTest.php', + 'Jose\\Component\\Encryption\\Tests\\InvalidCurveAttackTest' => __DIR__ . '/..' . '/web-token/jwt-encryption/Tests/InvalidCurveAttackTest.php', + 'Jose\\Component\\Encryption\\Tests\\JWEFlattenedTest' => __DIR__ . '/..' . '/web-token/jwt-encryption/Tests/JWEFlattenedTest.php', + 'Jose\\Component\\Encryption\\Tests\\JWELoaderTest' => __DIR__ . '/..' . '/web-token/jwt-encryption/Tests/JWELoaderTest.php', + 'Jose\\Component\\Encryption\\Tests\\JWESplitTest' => __DIR__ . '/..' . '/web-token/jwt-encryption/Tests/JWESplitTest.php', + 'Jose\\Component\\Encryption\\Tests\\RFC7520\\A128KWAndA128GCMEncryptionProtectedContentOnlyTest' => __DIR__ . '/..' . '/web-token/jwt-encryption/Tests/RFC7520/A128KWAndA128GCMEncryptionProtectedContentOnlyTest.php', + 'Jose\\Component\\Encryption\\Tests\\RFC7520\\A128KWAndA128GCMEncryptionTest' => __DIR__ . '/..' . '/web-token/jwt-encryption/Tests/RFC7520/A128KWAndA128GCMEncryptionTest.php', + 'Jose\\Component\\Encryption\\Tests\\RFC7520\\A128KWAndA128GCMEncryptionWithAdditionalAuthenticatedDataTest' => __DIR__ . '/..' . '/web-token/jwt-encryption/Tests/RFC7520/A128KWAndA128GCMEncryptionWithAdditionalAuthenticatedDataTest.php', + 'Jose\\Component\\Encryption\\Tests\\RFC7520\\A128KWAndA128GCMEncryptionWithCompressionTest' => __DIR__ . '/..' . '/web-token/jwt-encryption/Tests/RFC7520/A128KWAndA128GCMEncryptionWithCompressionTest.php', + 'Jose\\Component\\Encryption\\Tests\\RFC7520\\A128KWAndA128GCMEncryptionWithSpecificProtectedHeaderValuesTest' => __DIR__ . '/..' . '/web-token/jwt-encryption/Tests/RFC7520/A128KWAndA128GCMEncryptionWithSpecificProtectedHeaderValuesTest.php', + 'Jose\\Component\\Encryption\\Tests\\RFC7520\\A256GCMKWAndA128CBC_HS256EncryptionTest' => __DIR__ . '/..' . '/web-token/jwt-encryption/Tests/RFC7520/A256GCMKWAndA128CBC_HS256EncryptionTest.php', + 'Jose\\Component\\Encryption\\Tests\\RFC7520\\DirAndA128GCMEncryptionTest' => __DIR__ . '/..' . '/web-token/jwt-encryption/Tests/RFC7520/DirAndA128GCMEncryptionTest.php', + 'Jose\\Component\\Encryption\\Tests\\RFC7520\\ECDH_ES_A128KWAndA128GCMEncryptionTest' => __DIR__ . '/..' . '/web-token/jwt-encryption/Tests/RFC7520/ECDH_ES_A128KWAndA128GCMEncryptionTest.php', + 'Jose\\Component\\Encryption\\Tests\\RFC7520\\ECDH_ES_AndA128CBC_HS256EncryptionTest' => __DIR__ . '/..' . '/web-token/jwt-encryption/Tests/RFC7520/ECDH_ES_AndA128CBC_HS256EncryptionTest.php', + 'Jose\\Component\\Encryption\\Tests\\RFC7520\\MultipleRecipientEncryptionTest' => __DIR__ . '/..' . '/web-token/jwt-encryption/Tests/RFC7520/MultipleRecipientEncryptionTest.php', + 'Jose\\Component\\Encryption\\Tests\\RFC7520\\PBES2_HS512_A256KWAndA128CBC_HS256EncryptionTest' => __DIR__ . '/..' . '/web-token/jwt-encryption/Tests/RFC7520/PBES2_HS512_A256KWAndA128CBC_HS256EncryptionTest.php', + 'Jose\\Component\\Encryption\\Tests\\RFC7520\\RSA1_5AndA128CBC_HS256EncryptionTest' => __DIR__ . '/..' . '/web-token/jwt-encryption/Tests/RFC7520/RSA1_5AndA128CBC_HS256EncryptionTest.php', + 'Jose\\Component\\Encryption\\Tests\\RFC7520\\RSA_OAEPAndA256GCMEncryptionTest' => __DIR__ . '/..' . '/web-token/jwt-encryption/Tests/RFC7520/RSA_OAEPAndA256GCMEncryptionTest.php', + 'Jose\\Component\\Encryption\\Tests\\RSAEncryptionTest' => __DIR__ . '/..' . '/web-token/jwt-encryption/Tests/RSAEncryptionTest.php', + 'Jose\\Component\\Encryption\\Tests\\RSAKeyEncryptionTest' => __DIR__ . '/..' . '/web-token/jwt-encryption/Tests/RSAKeyEncryptionTest.php', + 'Jose\\Component\\Encryption\\Tests\\RSAKeyWithoutAllPrimesTest' => __DIR__ . '/..' . '/web-token/jwt-encryption/Tests/RSAKeyWithoutAllPrimesTest.php', + 'Jose\\Component\\Signature\\Algorithm\\ECDSA' => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-ecdsa/ECDSA.php', + 'Jose\\Component\\Signature\\Algorithm\\ES256' => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-ecdsa/ES256.php', + 'Jose\\Component\\Signature\\Algorithm\\ES384' => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-ecdsa/ES384.php', + 'Jose\\Component\\Signature\\Algorithm\\ES512' => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-ecdsa/ES512.php', + 'Jose\\Component\\Signature\\Algorithm\\HMAC' => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-hmac/HMAC.php', + 'Jose\\Component\\Signature\\Algorithm\\HS256' => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-hmac/HS256.php', + 'Jose\\Component\\Signature\\Algorithm\\HS384' => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-hmac/HS384.php', + 'Jose\\Component\\Signature\\Algorithm\\HS512' => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-hmac/HS512.php', + 'Jose\\Component\\Signature\\Algorithm\\MacAlgorithm' => __DIR__ . '/..' . '/web-token/jwt-signature/Algorithm/MacAlgorithm.php', + 'Jose\\Component\\Signature\\Algorithm\\PS256' => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-rsa/PS256.php', + 'Jose\\Component\\Signature\\Algorithm\\PS384' => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-rsa/PS384.php', + 'Jose\\Component\\Signature\\Algorithm\\PS512' => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-rsa/PS512.php', + 'Jose\\Component\\Signature\\Algorithm\\RS256' => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-rsa/RS256.php', + 'Jose\\Component\\Signature\\Algorithm\\RS384' => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-rsa/RS384.php', + 'Jose\\Component\\Signature\\Algorithm\\RS512' => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-rsa/RS512.php', + 'Jose\\Component\\Signature\\Algorithm\\RSA' => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-rsa/RSA.php', + 'Jose\\Component\\Signature\\Algorithm\\RSAPKCS1' => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-rsa/RSAPKCS1.php', + 'Jose\\Component\\Signature\\Algorithm\\RSAPSS' => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-rsa/RSAPSS.php', + 'Jose\\Component\\Signature\\Algorithm\\SignatureAlgorithm' => __DIR__ . '/..' . '/web-token/jwt-signature/Algorithm/SignatureAlgorithm.php', + 'Jose\\Component\\Signature\\Algorithm\\Tests\\ECDSAFromRFC6979Test' => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-ecdsa/Tests/ECDSAFromRFC6979Test.php', + 'Jose\\Component\\Signature\\Algorithm\\Tests\\ECDSAFromRFC7520Test' => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-ecdsa/Tests/ECDSAFromRFC7520Test.php', + 'Jose\\Component\\Signature\\Algorithm\\Tests\\ECDSASignatureTest' => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-ecdsa/Tests/ECDSASignatureTest.php', + 'Jose\\Component\\Signature\\Algorithm\\Tests\\HMACFromRFC7520Test' => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-hmac/Tests/HMACFromRFC7520Test.php', + 'Jose\\Component\\Signature\\Algorithm\\Tests\\HMACSignatureTest' => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-hmac/Tests/HMACSignatureTest.php', + 'Jose\\Component\\Signature\\Algorithm\\Tests\\RSA15SignatureTest' => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-rsa/Tests/RSA15SignatureTest.php', + 'Jose\\Component\\Signature\\Algorithm\\Tests\\RSAKeyWithoutAllPrimesTest' => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-rsa/Tests/RSAKeyWithoutAllPrimesTest.php', + 'Jose\\Component\\Signature\\Algorithm\\Tests\\RSAPSSSignatureTest' => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-rsa/Tests/RSAPSSSignatureTest.php', + 'Jose\\Component\\Signature\\Algorithm\\Tests\\RSASignatureTest' => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-rsa/Tests/RSASignatureTest.php', + 'Jose\\Component\\Signature\\Algorithm\\Util\\RSA' => __DIR__ . '/..' . '/web-token/jwt-signature-algorithm-rsa/Util/RSA.php', + 'Jose\\Component\\Signature\\JWS' => __DIR__ . '/..' . '/web-token/jwt-signature/JWS.php', + 'Jose\\Component\\Signature\\JWSBuilder' => __DIR__ . '/..' . '/web-token/jwt-signature/JWSBuilder.php', + 'Jose\\Component\\Signature\\JWSBuilderFactory' => __DIR__ . '/..' . '/web-token/jwt-signature/JWSBuilderFactory.php', + 'Jose\\Component\\Signature\\JWSLoader' => __DIR__ . '/..' . '/web-token/jwt-signature/JWSLoader.php', + 'Jose\\Component\\Signature\\JWSLoaderFactory' => __DIR__ . '/..' . '/web-token/jwt-signature/JWSLoaderFactory.php', + 'Jose\\Component\\Signature\\JWSTokenSupport' => __DIR__ . '/..' . '/web-token/jwt-signature/JWSTokenSupport.php', + 'Jose\\Component\\Signature\\JWSVerifier' => __DIR__ . '/..' . '/web-token/jwt-signature/JWSVerifier.php', + 'Jose\\Component\\Signature\\JWSVerifierFactory' => __DIR__ . '/..' . '/web-token/jwt-signature/JWSVerifierFactory.php', + 'Jose\\Component\\Signature\\Serializer\\CompactSerializer' => __DIR__ . '/..' . '/web-token/jwt-signature/Serializer/CompactSerializer.php', + 'Jose\\Component\\Signature\\Serializer\\JSONFlattenedSerializer' => __DIR__ . '/..' . '/web-token/jwt-signature/Serializer/JSONFlattenedSerializer.php', + 'Jose\\Component\\Signature\\Serializer\\JSONGeneralSerializer' => __DIR__ . '/..' . '/web-token/jwt-signature/Serializer/JSONGeneralSerializer.php', + 'Jose\\Component\\Signature\\Serializer\\JWSSerializer' => __DIR__ . '/..' . '/web-token/jwt-signature/Serializer/JWSSerializer.php', + 'Jose\\Component\\Signature\\Serializer\\JWSSerializerManager' => __DIR__ . '/..' . '/web-token/jwt-signature/Serializer/JWSSerializerManager.php', + 'Jose\\Component\\Signature\\Serializer\\JWSSerializerManagerFactory' => __DIR__ . '/..' . '/web-token/jwt-signature/Serializer/JWSSerializerManagerFactory.php', + 'Jose\\Component\\Signature\\Serializer\\Serializer' => __DIR__ . '/..' . '/web-token/jwt-signature/Serializer/Serializer.php', + 'Jose\\Component\\Signature\\Signature' => __DIR__ . '/..' . '/web-token/jwt-signature/Signature.php', + 'Jose\\Component\\Signature\\Tests\\ForeignJWTTest' => __DIR__ . '/..' . '/web-token/jwt-signature/Tests/ForeignJWTTest.php', + 'Jose\\Component\\Signature\\Tests\\JWSFlattenedTest' => __DIR__ . '/..' . '/web-token/jwt-signature/Tests/JWSFlattenedTest.php', + 'Jose\\Component\\Signature\\Tests\\JWSLoaderTest' => __DIR__ . '/..' . '/web-token/jwt-signature/Tests/JWSLoaderTest.php', + 'Jose\\Component\\Signature\\Tests\\JWSSplitTest' => __DIR__ . '/..' . '/web-token/jwt-signature/Tests/JWSSplitTest.php', + 'Jose\\Component\\Signature\\Tests\\JWSTest' => __DIR__ . '/..' . '/web-token/jwt-signature/Tests/JWSTest.php', + 'Jose\\Component\\Signature\\Tests\\RFC7520\\MultipleSignaturesTest' => __DIR__ . '/..' . '/web-token/jwt-signature/Tests/RFC7520/MultipleSignaturesTest.php', + 'Jose\\Component\\Signature\\Tests\\RFC7520\\NestingTest' => __DIR__ . '/..' . '/web-token/jwt-signature/Tests/RFC7520/NestingTest.php', + 'Jose\\Component\\Signature\\Tests\\SignatureTest' => __DIR__ . '/..' . '/web-token/jwt-signature/Tests/SignatureTest.php', + 'Jose\\Component\\Signature\\Tests\\SignerTest' => __DIR__ . '/..' . '/web-token/jwt-signature/Tests/SignerTest.php', + 'Jose\\Easy\\AbstractBuilder' => __DIR__ . '/..' . '/web-token/jwt-easy/AbstractBuilder.php', + 'Jose\\Easy\\AbstractLoader' => __DIR__ . '/..' . '/web-token/jwt-easy/AbstractLoader.php', + 'Jose\\Easy\\AlgorithmProvider' => __DIR__ . '/..' . '/web-token/jwt-easy/AlgorithmProvider.php', + 'Jose\\Easy\\Build' => __DIR__ . '/..' . '/web-token/jwt-easy/Build.php', + 'Jose\\Easy\\CallableChecker' => __DIR__ . '/..' . '/web-token/jwt-easy/CallableChecker.php', + 'Jose\\Easy\\ContentEncryptionAlgorithmChecker' => __DIR__ . '/..' . '/web-token/jwt-easy/ContentEncryptionAlgorithmChecker.php', + 'Jose\\Easy\\Decrypt' => __DIR__ . '/..' . '/web-token/jwt-easy/Decrypt.php', + 'Jose\\Easy\\JWEBuilder' => __DIR__ . '/..' . '/web-token/jwt-easy/JWEBuilder.php', + 'Jose\\Easy\\JWSBuilder' => __DIR__ . '/..' . '/web-token/jwt-easy/JWSBuilder.php', + 'Jose\\Easy\\JWT' => __DIR__ . '/..' . '/web-token/jwt-easy/JWT.php', + 'Jose\\Easy\\Load' => __DIR__ . '/..' . '/web-token/jwt-easy/Load.php', + 'Jose\\Easy\\ParameterBag' => __DIR__ . '/..' . '/web-token/jwt-easy/ParameterBag.php', + 'Jose\\Easy\\Tests\\AlgorithmProviderTest' => __DIR__ . '/..' . '/web-token/jwt-easy/Tests/AlgorithmProviderTest.php', + 'Jose\\Easy\\Tests\\EncryptionTest' => __DIR__ . '/..' . '/web-token/jwt-easy/Tests/EncryptionTest.php', + 'Jose\\Easy\\Tests\\ParameterBagTest' => __DIR__ . '/..' . '/web-token/jwt-easy/Tests/ParameterBagTest.php', + 'Jose\\Easy\\Tests\\SignatureTest' => __DIR__ . '/..' . '/web-token/jwt-easy/Tests/SignatureTest.php', + 'Jose\\Easy\\Validate' => __DIR__ . '/..' . '/web-token/jwt-easy/Validate.php', 'JsonException' => __DIR__ . '/..' . '/symfony/polyfill-php73/Resources/stubs/JsonException.php', 'Khill\\Lavacharts\\Builders\\ChartBuilder' => __DIR__ . '/..' . '/khill/lavacharts/src/Builders/ChartBuilder.php', 'Khill\\Lavacharts\\Builders\\DashboardBuilder' => __DIR__ . '/..' . '/khill/lavacharts/src/Builders/DashboardBuilder.php', @@ -3712,6 +3994,277 @@ class ComposerStaticInitc3f953f8a7291d41a76e1664339777c9 'PhpOption\\None' => __DIR__ . '/..' . '/phpoption/phpoption/src/PhpOption/None.php', 'PhpOption\\Option' => __DIR__ . '/..' . '/phpoption/phpoption/src/PhpOption/Option.php', 'PhpOption\\Some' => __DIR__ . '/..' . '/phpoption/phpoption/src/PhpOption/Some.php', + 'Predis\\Autoloader' => __DIR__ . '/..' . '/predis/predis/src/Autoloader.php', + 'Predis\\Client' => __DIR__ . '/..' . '/predis/predis/src/Client.php', + 'Predis\\ClientContextInterface' => __DIR__ . '/..' . '/predis/predis/src/ClientContextInterface.php', + 'Predis\\ClientException' => __DIR__ . '/..' . '/predis/predis/src/ClientException.php', + 'Predis\\ClientInterface' => __DIR__ . '/..' . '/predis/predis/src/ClientInterface.php', + 'Predis\\Cluster\\ClusterStrategy' => __DIR__ . '/..' . '/predis/predis/src/Cluster/ClusterStrategy.php', + 'Predis\\Cluster\\Distributor\\DistributorInterface' => __DIR__ . '/..' . '/predis/predis/src/Cluster/Distributor/DistributorInterface.php', + 'Predis\\Cluster\\Distributor\\EmptyRingException' => __DIR__ . '/..' . '/predis/predis/src/Cluster/Distributor/EmptyRingException.php', + 'Predis\\Cluster\\Distributor\\HashRing' => __DIR__ . '/..' . '/predis/predis/src/Cluster/Distributor/HashRing.php', + 'Predis\\Cluster\\Distributor\\KetamaRing' => __DIR__ . '/..' . '/predis/predis/src/Cluster/Distributor/KetamaRing.php', + 'Predis\\Cluster\\Hash\\CRC16' => __DIR__ . '/..' . '/predis/predis/src/Cluster/Hash/CRC16.php', + 'Predis\\Cluster\\Hash\\HashGeneratorInterface' => __DIR__ . '/..' . '/predis/predis/src/Cluster/Hash/HashGeneratorInterface.php', + 'Predis\\Cluster\\PredisStrategy' => __DIR__ . '/..' . '/predis/predis/src/Cluster/PredisStrategy.php', + 'Predis\\Cluster\\RedisStrategy' => __DIR__ . '/..' . '/predis/predis/src/Cluster/RedisStrategy.php', + 'Predis\\Cluster\\StrategyInterface' => __DIR__ . '/..' . '/predis/predis/src/Cluster/StrategyInterface.php', + 'Predis\\Collection\\Iterator\\CursorBasedIterator' => __DIR__ . '/..' . '/predis/predis/src/Collection/Iterator/CursorBasedIterator.php', + 'Predis\\Collection\\Iterator\\HashKey' => __DIR__ . '/..' . '/predis/predis/src/Collection/Iterator/HashKey.php', + 'Predis\\Collection\\Iterator\\Keyspace' => __DIR__ . '/..' . '/predis/predis/src/Collection/Iterator/Keyspace.php', + 'Predis\\Collection\\Iterator\\ListKey' => __DIR__ . '/..' . '/predis/predis/src/Collection/Iterator/ListKey.php', + 'Predis\\Collection\\Iterator\\SetKey' => __DIR__ . '/..' . '/predis/predis/src/Collection/Iterator/SetKey.php', + 'Predis\\Collection\\Iterator\\SortedSetKey' => __DIR__ . '/..' . '/predis/predis/src/Collection/Iterator/SortedSetKey.php', + 'Predis\\Command\\Command' => __DIR__ . '/..' . '/predis/predis/src/Command/Command.php', + 'Predis\\Command\\CommandInterface' => __DIR__ . '/..' . '/predis/predis/src/Command/CommandInterface.php', + 'Predis\\Command\\ConnectionAuth' => __DIR__ . '/..' . '/predis/predis/src/Command/ConnectionAuth.php', + 'Predis\\Command\\ConnectionEcho' => __DIR__ . '/..' . '/predis/predis/src/Command/ConnectionEcho.php', + 'Predis\\Command\\ConnectionPing' => __DIR__ . '/..' . '/predis/predis/src/Command/ConnectionPing.php', + 'Predis\\Command\\ConnectionQuit' => __DIR__ . '/..' . '/predis/predis/src/Command/ConnectionQuit.php', + 'Predis\\Command\\ConnectionSelect' => __DIR__ . '/..' . '/predis/predis/src/Command/ConnectionSelect.php', + 'Predis\\Command\\GeospatialGeoAdd' => __DIR__ . '/..' . '/predis/predis/src/Command/GeospatialGeoAdd.php', + 'Predis\\Command\\GeospatialGeoDist' => __DIR__ . '/..' . '/predis/predis/src/Command/GeospatialGeoDist.php', + 'Predis\\Command\\GeospatialGeoHash' => __DIR__ . '/..' . '/predis/predis/src/Command/GeospatialGeoHash.php', + 'Predis\\Command\\GeospatialGeoPos' => __DIR__ . '/..' . '/predis/predis/src/Command/GeospatialGeoPos.php', + 'Predis\\Command\\GeospatialGeoRadius' => __DIR__ . '/..' . '/predis/predis/src/Command/GeospatialGeoRadius.php', + 'Predis\\Command\\GeospatialGeoRadiusByMember' => __DIR__ . '/..' . '/predis/predis/src/Command/GeospatialGeoRadiusByMember.php', + 'Predis\\Command\\HashDelete' => __DIR__ . '/..' . '/predis/predis/src/Command/HashDelete.php', + 'Predis\\Command\\HashExists' => __DIR__ . '/..' . '/predis/predis/src/Command/HashExists.php', + 'Predis\\Command\\HashGet' => __DIR__ . '/..' . '/predis/predis/src/Command/HashGet.php', + 'Predis\\Command\\HashGetAll' => __DIR__ . '/..' . '/predis/predis/src/Command/HashGetAll.php', + 'Predis\\Command\\HashGetMultiple' => __DIR__ . '/..' . '/predis/predis/src/Command/HashGetMultiple.php', + 'Predis\\Command\\HashIncrementBy' => __DIR__ . '/..' . '/predis/predis/src/Command/HashIncrementBy.php', + 'Predis\\Command\\HashIncrementByFloat' => __DIR__ . '/..' . '/predis/predis/src/Command/HashIncrementByFloat.php', + 'Predis\\Command\\HashKeys' => __DIR__ . '/..' . '/predis/predis/src/Command/HashKeys.php', + 'Predis\\Command\\HashLength' => __DIR__ . '/..' . '/predis/predis/src/Command/HashLength.php', + 'Predis\\Command\\HashScan' => __DIR__ . '/..' . '/predis/predis/src/Command/HashScan.php', + 'Predis\\Command\\HashSet' => __DIR__ . '/..' . '/predis/predis/src/Command/HashSet.php', + 'Predis\\Command\\HashSetMultiple' => __DIR__ . '/..' . '/predis/predis/src/Command/HashSetMultiple.php', + 'Predis\\Command\\HashSetPreserve' => __DIR__ . '/..' . '/predis/predis/src/Command/HashSetPreserve.php', + 'Predis\\Command\\HashStringLength' => __DIR__ . '/..' . '/predis/predis/src/Command/HashStringLength.php', + 'Predis\\Command\\HashValues' => __DIR__ . '/..' . '/predis/predis/src/Command/HashValues.php', + 'Predis\\Command\\HyperLogLogAdd' => __DIR__ . '/..' . '/predis/predis/src/Command/HyperLogLogAdd.php', + 'Predis\\Command\\HyperLogLogCount' => __DIR__ . '/..' . '/predis/predis/src/Command/HyperLogLogCount.php', + 'Predis\\Command\\HyperLogLogMerge' => __DIR__ . '/..' . '/predis/predis/src/Command/HyperLogLogMerge.php', + 'Predis\\Command\\KeyDelete' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyDelete.php', + 'Predis\\Command\\KeyDump' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyDump.php', + 'Predis\\Command\\KeyExists' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyExists.php', + 'Predis\\Command\\KeyExpire' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyExpire.php', + 'Predis\\Command\\KeyExpireAt' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyExpireAt.php', + 'Predis\\Command\\KeyKeys' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyKeys.php', + 'Predis\\Command\\KeyMigrate' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyMigrate.php', + 'Predis\\Command\\KeyMove' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyMove.php', + 'Predis\\Command\\KeyPersist' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyPersist.php', + 'Predis\\Command\\KeyPreciseExpire' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyPreciseExpire.php', + 'Predis\\Command\\KeyPreciseExpireAt' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyPreciseExpireAt.php', + 'Predis\\Command\\KeyPreciseTimeToLive' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyPreciseTimeToLive.php', + 'Predis\\Command\\KeyRandom' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyRandom.php', + 'Predis\\Command\\KeyRename' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyRename.php', + 'Predis\\Command\\KeyRenamePreserve' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyRenamePreserve.php', + 'Predis\\Command\\KeyRestore' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyRestore.php', + 'Predis\\Command\\KeyScan' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyScan.php', + 'Predis\\Command\\KeySort' => __DIR__ . '/..' . '/predis/predis/src/Command/KeySort.php', + 'Predis\\Command\\KeyTimeToLive' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyTimeToLive.php', + 'Predis\\Command\\KeyType' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyType.php', + 'Predis\\Command\\ListIndex' => __DIR__ . '/..' . '/predis/predis/src/Command/ListIndex.php', + 'Predis\\Command\\ListInsert' => __DIR__ . '/..' . '/predis/predis/src/Command/ListInsert.php', + 'Predis\\Command\\ListLength' => __DIR__ . '/..' . '/predis/predis/src/Command/ListLength.php', + 'Predis\\Command\\ListPopFirst' => __DIR__ . '/..' . '/predis/predis/src/Command/ListPopFirst.php', + 'Predis\\Command\\ListPopFirstBlocking' => __DIR__ . '/..' . '/predis/predis/src/Command/ListPopFirstBlocking.php', + 'Predis\\Command\\ListPopLast' => __DIR__ . '/..' . '/predis/predis/src/Command/ListPopLast.php', + 'Predis\\Command\\ListPopLastBlocking' => __DIR__ . '/..' . '/predis/predis/src/Command/ListPopLastBlocking.php', + 'Predis\\Command\\ListPopLastPushHead' => __DIR__ . '/..' . '/predis/predis/src/Command/ListPopLastPushHead.php', + 'Predis\\Command\\ListPopLastPushHeadBlocking' => __DIR__ . '/..' . '/predis/predis/src/Command/ListPopLastPushHeadBlocking.php', + 'Predis\\Command\\ListPushHead' => __DIR__ . '/..' . '/predis/predis/src/Command/ListPushHead.php', + 'Predis\\Command\\ListPushHeadX' => __DIR__ . '/..' . '/predis/predis/src/Command/ListPushHeadX.php', + 'Predis\\Command\\ListPushTail' => __DIR__ . '/..' . '/predis/predis/src/Command/ListPushTail.php', + 'Predis\\Command\\ListPushTailX' => __DIR__ . '/..' . '/predis/predis/src/Command/ListPushTailX.php', + 'Predis\\Command\\ListRange' => __DIR__ . '/..' . '/predis/predis/src/Command/ListRange.php', + 'Predis\\Command\\ListRemove' => __DIR__ . '/..' . '/predis/predis/src/Command/ListRemove.php', + 'Predis\\Command\\ListSet' => __DIR__ . '/..' . '/predis/predis/src/Command/ListSet.php', + 'Predis\\Command\\ListTrim' => __DIR__ . '/..' . '/predis/predis/src/Command/ListTrim.php', + 'Predis\\Command\\PrefixableCommandInterface' => __DIR__ . '/..' . '/predis/predis/src/Command/PrefixableCommandInterface.php', + 'Predis\\Command\\Processor\\KeyPrefixProcessor' => __DIR__ . '/..' . '/predis/predis/src/Command/Processor/KeyPrefixProcessor.php', + 'Predis\\Command\\Processor\\ProcessorChain' => __DIR__ . '/..' . '/predis/predis/src/Command/Processor/ProcessorChain.php', + 'Predis\\Command\\Processor\\ProcessorInterface' => __DIR__ . '/..' . '/predis/predis/src/Command/Processor/ProcessorInterface.php', + 'Predis\\Command\\PubSubPublish' => __DIR__ . '/..' . '/predis/predis/src/Command/PubSubPublish.php', + 'Predis\\Command\\PubSubPubsub' => __DIR__ . '/..' . '/predis/predis/src/Command/PubSubPubsub.php', + 'Predis\\Command\\PubSubSubscribe' => __DIR__ . '/..' . '/predis/predis/src/Command/PubSubSubscribe.php', + 'Predis\\Command\\PubSubSubscribeByPattern' => __DIR__ . '/..' . '/predis/predis/src/Command/PubSubSubscribeByPattern.php', + 'Predis\\Command\\PubSubUnsubscribe' => __DIR__ . '/..' . '/predis/predis/src/Command/PubSubUnsubscribe.php', + 'Predis\\Command\\PubSubUnsubscribeByPattern' => __DIR__ . '/..' . '/predis/predis/src/Command/PubSubUnsubscribeByPattern.php', + 'Predis\\Command\\RawCommand' => __DIR__ . '/..' . '/predis/predis/src/Command/RawCommand.php', + 'Predis\\Command\\ScriptCommand' => __DIR__ . '/..' . '/predis/predis/src/Command/ScriptCommand.php', + 'Predis\\Command\\ServerBackgroundRewriteAOF' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerBackgroundRewriteAOF.php', + 'Predis\\Command\\ServerBackgroundSave' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerBackgroundSave.php', + 'Predis\\Command\\ServerClient' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerClient.php', + 'Predis\\Command\\ServerCommand' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerCommand.php', + 'Predis\\Command\\ServerConfig' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerConfig.php', + 'Predis\\Command\\ServerDatabaseSize' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerDatabaseSize.php', + 'Predis\\Command\\ServerEval' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerEval.php', + 'Predis\\Command\\ServerEvalSHA' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerEvalSHA.php', + 'Predis\\Command\\ServerFlushAll' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerFlushAll.php', + 'Predis\\Command\\ServerFlushDatabase' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerFlushDatabase.php', + 'Predis\\Command\\ServerInfo' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerInfo.php', + 'Predis\\Command\\ServerInfoV26x' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerInfoV26x.php', + 'Predis\\Command\\ServerLastSave' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerLastSave.php', + 'Predis\\Command\\ServerMonitor' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerMonitor.php', + 'Predis\\Command\\ServerObject' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerObject.php', + 'Predis\\Command\\ServerSave' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerSave.php', + 'Predis\\Command\\ServerScript' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerScript.php', + 'Predis\\Command\\ServerSentinel' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerSentinel.php', + 'Predis\\Command\\ServerShutdown' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerShutdown.php', + 'Predis\\Command\\ServerSlaveOf' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerSlaveOf.php', + 'Predis\\Command\\ServerSlowlog' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerSlowlog.php', + 'Predis\\Command\\ServerTime' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerTime.php', + 'Predis\\Command\\SetAdd' => __DIR__ . '/..' . '/predis/predis/src/Command/SetAdd.php', + 'Predis\\Command\\SetCardinality' => __DIR__ . '/..' . '/predis/predis/src/Command/SetCardinality.php', + 'Predis\\Command\\SetDifference' => __DIR__ . '/..' . '/predis/predis/src/Command/SetDifference.php', + 'Predis\\Command\\SetDifferenceStore' => __DIR__ . '/..' . '/predis/predis/src/Command/SetDifferenceStore.php', + 'Predis\\Command\\SetIntersection' => __DIR__ . '/..' . '/predis/predis/src/Command/SetIntersection.php', + 'Predis\\Command\\SetIntersectionStore' => __DIR__ . '/..' . '/predis/predis/src/Command/SetIntersectionStore.php', + 'Predis\\Command\\SetIsMember' => __DIR__ . '/..' . '/predis/predis/src/Command/SetIsMember.php', + 'Predis\\Command\\SetMembers' => __DIR__ . '/..' . '/predis/predis/src/Command/SetMembers.php', + 'Predis\\Command\\SetMove' => __DIR__ . '/..' . '/predis/predis/src/Command/SetMove.php', + 'Predis\\Command\\SetPop' => __DIR__ . '/..' . '/predis/predis/src/Command/SetPop.php', + 'Predis\\Command\\SetRandomMember' => __DIR__ . '/..' . '/predis/predis/src/Command/SetRandomMember.php', + 'Predis\\Command\\SetRemove' => __DIR__ . '/..' . '/predis/predis/src/Command/SetRemove.php', + 'Predis\\Command\\SetScan' => __DIR__ . '/..' . '/predis/predis/src/Command/SetScan.php', + 'Predis\\Command\\SetUnion' => __DIR__ . '/..' . '/predis/predis/src/Command/SetUnion.php', + 'Predis\\Command\\SetUnionStore' => __DIR__ . '/..' . '/predis/predis/src/Command/SetUnionStore.php', + 'Predis\\Command\\StringAppend' => __DIR__ . '/..' . '/predis/predis/src/Command/StringAppend.php', + 'Predis\\Command\\StringBitCount' => __DIR__ . '/..' . '/predis/predis/src/Command/StringBitCount.php', + 'Predis\\Command\\StringBitField' => __DIR__ . '/..' . '/predis/predis/src/Command/StringBitField.php', + 'Predis\\Command\\StringBitOp' => __DIR__ . '/..' . '/predis/predis/src/Command/StringBitOp.php', + 'Predis\\Command\\StringBitPos' => __DIR__ . '/..' . '/predis/predis/src/Command/StringBitPos.php', + 'Predis\\Command\\StringDecrement' => __DIR__ . '/..' . '/predis/predis/src/Command/StringDecrement.php', + 'Predis\\Command\\StringDecrementBy' => __DIR__ . '/..' . '/predis/predis/src/Command/StringDecrementBy.php', + 'Predis\\Command\\StringGet' => __DIR__ . '/..' . '/predis/predis/src/Command/StringGet.php', + 'Predis\\Command\\StringGetBit' => __DIR__ . '/..' . '/predis/predis/src/Command/StringGetBit.php', + 'Predis\\Command\\StringGetMultiple' => __DIR__ . '/..' . '/predis/predis/src/Command/StringGetMultiple.php', + 'Predis\\Command\\StringGetRange' => __DIR__ . '/..' . '/predis/predis/src/Command/StringGetRange.php', + 'Predis\\Command\\StringGetSet' => __DIR__ . '/..' . '/predis/predis/src/Command/StringGetSet.php', + 'Predis\\Command\\StringIncrement' => __DIR__ . '/..' . '/predis/predis/src/Command/StringIncrement.php', + 'Predis\\Command\\StringIncrementBy' => __DIR__ . '/..' . '/predis/predis/src/Command/StringIncrementBy.php', + 'Predis\\Command\\StringIncrementByFloat' => __DIR__ . '/..' . '/predis/predis/src/Command/StringIncrementByFloat.php', + 'Predis\\Command\\StringPreciseSetExpire' => __DIR__ . '/..' . '/predis/predis/src/Command/StringPreciseSetExpire.php', + 'Predis\\Command\\StringSet' => __DIR__ . '/..' . '/predis/predis/src/Command/StringSet.php', + 'Predis\\Command\\StringSetBit' => __DIR__ . '/..' . '/predis/predis/src/Command/StringSetBit.php', + 'Predis\\Command\\StringSetExpire' => __DIR__ . '/..' . '/predis/predis/src/Command/StringSetExpire.php', + 'Predis\\Command\\StringSetMultiple' => __DIR__ . '/..' . '/predis/predis/src/Command/StringSetMultiple.php', + 'Predis\\Command\\StringSetMultiplePreserve' => __DIR__ . '/..' . '/predis/predis/src/Command/StringSetMultiplePreserve.php', + 'Predis\\Command\\StringSetPreserve' => __DIR__ . '/..' . '/predis/predis/src/Command/StringSetPreserve.php', + 'Predis\\Command\\StringSetRange' => __DIR__ . '/..' . '/predis/predis/src/Command/StringSetRange.php', + 'Predis\\Command\\StringStrlen' => __DIR__ . '/..' . '/predis/predis/src/Command/StringStrlen.php', + 'Predis\\Command\\StringSubstr' => __DIR__ . '/..' . '/predis/predis/src/Command/StringSubstr.php', + 'Predis\\Command\\TransactionDiscard' => __DIR__ . '/..' . '/predis/predis/src/Command/TransactionDiscard.php', + 'Predis\\Command\\TransactionExec' => __DIR__ . '/..' . '/predis/predis/src/Command/TransactionExec.php', + 'Predis\\Command\\TransactionMulti' => __DIR__ . '/..' . '/predis/predis/src/Command/TransactionMulti.php', + 'Predis\\Command\\TransactionUnwatch' => __DIR__ . '/..' . '/predis/predis/src/Command/TransactionUnwatch.php', + 'Predis\\Command\\TransactionWatch' => __DIR__ . '/..' . '/predis/predis/src/Command/TransactionWatch.php', + 'Predis\\Command\\ZSetAdd' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetAdd.php', + 'Predis\\Command\\ZSetCardinality' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetCardinality.php', + 'Predis\\Command\\ZSetCount' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetCount.php', + 'Predis\\Command\\ZSetIncrementBy' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetIncrementBy.php', + 'Predis\\Command\\ZSetIntersectionStore' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetIntersectionStore.php', + 'Predis\\Command\\ZSetLexCount' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetLexCount.php', + 'Predis\\Command\\ZSetRange' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetRange.php', + 'Predis\\Command\\ZSetRangeByLex' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetRangeByLex.php', + 'Predis\\Command\\ZSetRangeByScore' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetRangeByScore.php', + 'Predis\\Command\\ZSetRank' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetRank.php', + 'Predis\\Command\\ZSetRemove' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetRemove.php', + 'Predis\\Command\\ZSetRemoveRangeByLex' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetRemoveRangeByLex.php', + 'Predis\\Command\\ZSetRemoveRangeByRank' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetRemoveRangeByRank.php', + 'Predis\\Command\\ZSetRemoveRangeByScore' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetRemoveRangeByScore.php', + 'Predis\\Command\\ZSetReverseRange' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetReverseRange.php', + 'Predis\\Command\\ZSetReverseRangeByLex' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetReverseRangeByLex.php', + 'Predis\\Command\\ZSetReverseRangeByScore' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetReverseRangeByScore.php', + 'Predis\\Command\\ZSetReverseRank' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetReverseRank.php', + 'Predis\\Command\\ZSetScan' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetScan.php', + 'Predis\\Command\\ZSetScore' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetScore.php', + 'Predis\\Command\\ZSetUnionStore' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetUnionStore.php', + 'Predis\\CommunicationException' => __DIR__ . '/..' . '/predis/predis/src/CommunicationException.php', + 'Predis\\Configuration\\ClusterOption' => __DIR__ . '/..' . '/predis/predis/src/Configuration/ClusterOption.php', + 'Predis\\Configuration\\ConnectionFactoryOption' => __DIR__ . '/..' . '/predis/predis/src/Configuration/ConnectionFactoryOption.php', + 'Predis\\Configuration\\ExceptionsOption' => __DIR__ . '/..' . '/predis/predis/src/Configuration/ExceptionsOption.php', + 'Predis\\Configuration\\OptionInterface' => __DIR__ . '/..' . '/predis/predis/src/Configuration/OptionInterface.php', + 'Predis\\Configuration\\Options' => __DIR__ . '/..' . '/predis/predis/src/Configuration/Options.php', + 'Predis\\Configuration\\OptionsInterface' => __DIR__ . '/..' . '/predis/predis/src/Configuration/OptionsInterface.php', + 'Predis\\Configuration\\PrefixOption' => __DIR__ . '/..' . '/predis/predis/src/Configuration/PrefixOption.php', + 'Predis\\Configuration\\ProfileOption' => __DIR__ . '/..' . '/predis/predis/src/Configuration/ProfileOption.php', + 'Predis\\Configuration\\ReplicationOption' => __DIR__ . '/..' . '/predis/predis/src/Configuration/ReplicationOption.php', + 'Predis\\Connection\\AbstractConnection' => __DIR__ . '/..' . '/predis/predis/src/Connection/AbstractConnection.php', + 'Predis\\Connection\\AggregateConnectionInterface' => __DIR__ . '/..' . '/predis/predis/src/Connection/AggregateConnectionInterface.php', + 'Predis\\Connection\\Aggregate\\ClusterInterface' => __DIR__ . '/..' . '/predis/predis/src/Connection/Aggregate/ClusterInterface.php', + 'Predis\\Connection\\Aggregate\\MasterSlaveReplication' => __DIR__ . '/..' . '/predis/predis/src/Connection/Aggregate/MasterSlaveReplication.php', + 'Predis\\Connection\\Aggregate\\PredisCluster' => __DIR__ . '/..' . '/predis/predis/src/Connection/Aggregate/PredisCluster.php', + 'Predis\\Connection\\Aggregate\\RedisCluster' => __DIR__ . '/..' . '/predis/predis/src/Connection/Aggregate/RedisCluster.php', + 'Predis\\Connection\\Aggregate\\ReplicationInterface' => __DIR__ . '/..' . '/predis/predis/src/Connection/Aggregate/ReplicationInterface.php', + 'Predis\\Connection\\Aggregate\\SentinelReplication' => __DIR__ . '/..' . '/predis/predis/src/Connection/Aggregate/SentinelReplication.php', + 'Predis\\Connection\\CompositeConnectionInterface' => __DIR__ . '/..' . '/predis/predis/src/Connection/CompositeConnectionInterface.php', + 'Predis\\Connection\\CompositeStreamConnection' => __DIR__ . '/..' . '/predis/predis/src/Connection/CompositeStreamConnection.php', + 'Predis\\Connection\\ConnectionException' => __DIR__ . '/..' . '/predis/predis/src/Connection/ConnectionException.php', + 'Predis\\Connection\\ConnectionInterface' => __DIR__ . '/..' . '/predis/predis/src/Connection/ConnectionInterface.php', + 'Predis\\Connection\\Factory' => __DIR__ . '/..' . '/predis/predis/src/Connection/Factory.php', + 'Predis\\Connection\\FactoryInterface' => __DIR__ . '/..' . '/predis/predis/src/Connection/FactoryInterface.php', + 'Predis\\Connection\\NodeConnectionInterface' => __DIR__ . '/..' . '/predis/predis/src/Connection/NodeConnectionInterface.php', + 'Predis\\Connection\\Parameters' => __DIR__ . '/..' . '/predis/predis/src/Connection/Parameters.php', + 'Predis\\Connection\\ParametersInterface' => __DIR__ . '/..' . '/predis/predis/src/Connection/ParametersInterface.php', + 'Predis\\Connection\\PhpiredisSocketConnection' => __DIR__ . '/..' . '/predis/predis/src/Connection/PhpiredisSocketConnection.php', + 'Predis\\Connection\\PhpiredisStreamConnection' => __DIR__ . '/..' . '/predis/predis/src/Connection/PhpiredisStreamConnection.php', + 'Predis\\Connection\\StreamConnection' => __DIR__ . '/..' . '/predis/predis/src/Connection/StreamConnection.php', + 'Predis\\Connection\\WebdisConnection' => __DIR__ . '/..' . '/predis/predis/src/Connection/WebdisConnection.php', + 'Predis\\Monitor\\Consumer' => __DIR__ . '/..' . '/predis/predis/src/Monitor/Consumer.php', + 'Predis\\NotSupportedException' => __DIR__ . '/..' . '/predis/predis/src/NotSupportedException.php', + 'Predis\\Pipeline\\Atomic' => __DIR__ . '/..' . '/predis/predis/src/Pipeline/Atomic.php', + 'Predis\\Pipeline\\ConnectionErrorProof' => __DIR__ . '/..' . '/predis/predis/src/Pipeline/ConnectionErrorProof.php', + 'Predis\\Pipeline\\FireAndForget' => __DIR__ . '/..' . '/predis/predis/src/Pipeline/FireAndForget.php', + 'Predis\\Pipeline\\Pipeline' => __DIR__ . '/..' . '/predis/predis/src/Pipeline/Pipeline.php', + 'Predis\\PredisException' => __DIR__ . '/..' . '/predis/predis/src/PredisException.php', + 'Predis\\Profile\\Factory' => __DIR__ . '/..' . '/predis/predis/src/Profile/Factory.php', + 'Predis\\Profile\\ProfileInterface' => __DIR__ . '/..' . '/predis/predis/src/Profile/ProfileInterface.php', + 'Predis\\Profile\\RedisProfile' => __DIR__ . '/..' . '/predis/predis/src/Profile/RedisProfile.php', + 'Predis\\Profile\\RedisUnstable' => __DIR__ . '/..' . '/predis/predis/src/Profile/RedisUnstable.php', + 'Predis\\Profile\\RedisVersion200' => __DIR__ . '/..' . '/predis/predis/src/Profile/RedisVersion200.php', + 'Predis\\Profile\\RedisVersion220' => __DIR__ . '/..' . '/predis/predis/src/Profile/RedisVersion220.php', + 'Predis\\Profile\\RedisVersion240' => __DIR__ . '/..' . '/predis/predis/src/Profile/RedisVersion240.php', + 'Predis\\Profile\\RedisVersion260' => __DIR__ . '/..' . '/predis/predis/src/Profile/RedisVersion260.php', + 'Predis\\Profile\\RedisVersion280' => __DIR__ . '/..' . '/predis/predis/src/Profile/RedisVersion280.php', + 'Predis\\Profile\\RedisVersion300' => __DIR__ . '/..' . '/predis/predis/src/Profile/RedisVersion300.php', + 'Predis\\Profile\\RedisVersion320' => __DIR__ . '/..' . '/predis/predis/src/Profile/RedisVersion320.php', + 'Predis\\Protocol\\ProtocolException' => __DIR__ . '/..' . '/predis/predis/src/Protocol/ProtocolException.php', + 'Predis\\Protocol\\ProtocolProcessorInterface' => __DIR__ . '/..' . '/predis/predis/src/Protocol/ProtocolProcessorInterface.php', + 'Predis\\Protocol\\RequestSerializerInterface' => __DIR__ . '/..' . '/predis/predis/src/Protocol/RequestSerializerInterface.php', + 'Predis\\Protocol\\ResponseReaderInterface' => __DIR__ . '/..' . '/predis/predis/src/Protocol/ResponseReaderInterface.php', + 'Predis\\Protocol\\Text\\CompositeProtocolProcessor' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/CompositeProtocolProcessor.php', + 'Predis\\Protocol\\Text\\Handler\\BulkResponse' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/Handler/BulkResponse.php', + 'Predis\\Protocol\\Text\\Handler\\ErrorResponse' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/Handler/ErrorResponse.php', + 'Predis\\Protocol\\Text\\Handler\\IntegerResponse' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/Handler/IntegerResponse.php', + 'Predis\\Protocol\\Text\\Handler\\MultiBulkResponse' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/Handler/MultiBulkResponse.php', + 'Predis\\Protocol\\Text\\Handler\\ResponseHandlerInterface' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/Handler/ResponseHandlerInterface.php', + 'Predis\\Protocol\\Text\\Handler\\StatusResponse' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/Handler/StatusResponse.php', + 'Predis\\Protocol\\Text\\Handler\\StreamableMultiBulkResponse' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/Handler/StreamableMultiBulkResponse.php', + 'Predis\\Protocol\\Text\\ProtocolProcessor' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/ProtocolProcessor.php', + 'Predis\\Protocol\\Text\\RequestSerializer' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/RequestSerializer.php', + 'Predis\\Protocol\\Text\\ResponseReader' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/ResponseReader.php', + 'Predis\\PubSub\\AbstractConsumer' => __DIR__ . '/..' . '/predis/predis/src/PubSub/AbstractConsumer.php', + 'Predis\\PubSub\\Consumer' => __DIR__ . '/..' . '/predis/predis/src/PubSub/Consumer.php', + 'Predis\\PubSub\\DispatcherLoop' => __DIR__ . '/..' . '/predis/predis/src/PubSub/DispatcherLoop.php', + 'Predis\\Replication\\MissingMasterException' => __DIR__ . '/..' . '/predis/predis/src/Replication/MissingMasterException.php', + 'Predis\\Replication\\ReplicationStrategy' => __DIR__ . '/..' . '/predis/predis/src/Replication/ReplicationStrategy.php', + 'Predis\\Replication\\RoleException' => __DIR__ . '/..' . '/predis/predis/src/Replication/RoleException.php', + 'Predis\\Response\\Error' => __DIR__ . '/..' . '/predis/predis/src/Response/Error.php', + 'Predis\\Response\\ErrorInterface' => __DIR__ . '/..' . '/predis/predis/src/Response/ErrorInterface.php', + 'Predis\\Response\\Iterator\\MultiBulk' => __DIR__ . '/..' . '/predis/predis/src/Response/Iterator/MultiBulk.php', + 'Predis\\Response\\Iterator\\MultiBulkIterator' => __DIR__ . '/..' . '/predis/predis/src/Response/Iterator/MultiBulkIterator.php', + 'Predis\\Response\\Iterator\\MultiBulkTuple' => __DIR__ . '/..' . '/predis/predis/src/Response/Iterator/MultiBulkTuple.php', + 'Predis\\Response\\ResponseInterface' => __DIR__ . '/..' . '/predis/predis/src/Response/ResponseInterface.php', + 'Predis\\Response\\ServerException' => __DIR__ . '/..' . '/predis/predis/src/Response/ServerException.php', + 'Predis\\Response\\Status' => __DIR__ . '/..' . '/predis/predis/src/Response/Status.php', + 'Predis\\Session\\Handler' => __DIR__ . '/..' . '/predis/predis/src/Session/Handler.php', + 'Predis\\Transaction\\AbortedMultiExecException' => __DIR__ . '/..' . '/predis/predis/src/Transaction/AbortedMultiExecException.php', + 'Predis\\Transaction\\MultiExec' => __DIR__ . '/..' . '/predis/predis/src/Transaction/MultiExec.php', + 'Predis\\Transaction\\MultiExecState' => __DIR__ . '/..' . '/predis/predis/src/Transaction/MultiExecState.php', 'Prophecy\\Argument' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Argument.php', 'Prophecy\\Argument\\ArgumentsWildcard' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Argument/ArgumentsWildcard.php', 'Prophecy\\Argument\\Token\\AnyValueToken' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Argument/Token/AnyValueToken.php', @@ -3965,6 +4518,41 @@ class ComposerStaticInitc3f953f8a7291d41a76e1664339777c9 'Ramsey\\Uuid\\UuidInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/UuidInterface.php', 'Ramsey\\Uuid\\Validator\\GenericValidator' => __DIR__ . '/..' . '/ramsey/uuid/src/Validator/GenericValidator.php', 'Ramsey\\Uuid\\Validator\\ValidatorInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Validator/ValidatorInterface.php', + 'Seat\\Eseye\\Access\\AccessInterface' => __DIR__ . '/..' . '/eveseat/eseye/src/Access/AccessInterface.php', + 'Seat\\Eseye\\Access\\CheckAccess' => __DIR__ . '/..' . '/eveseat/eseye/src/Access/CheckAccess.php', + 'Seat\\Eseye\\Cache\\CacheInterface' => __DIR__ . '/..' . '/eveseat/eseye/src/Cache/CacheInterface.php', + 'Seat\\Eseye\\Cache\\FileCache' => __DIR__ . '/..' . '/eveseat/eseye/src/Cache/FileCache.php', + 'Seat\\Eseye\\Cache\\HashesStrings' => __DIR__ . '/..' . '/eveseat/eseye/src/Cache/HashesStrings.php', + 'Seat\\Eseye\\Cache\\MemcachedCache' => __DIR__ . '/..' . '/eveseat/eseye/src/Cache/MemcachedCache.php', + 'Seat\\Eseye\\Cache\\NullCache' => __DIR__ . '/..' . '/eveseat/eseye/src/Cache/NullCache.php', + 'Seat\\Eseye\\Cache\\RedisCache' => __DIR__ . '/..' . '/eveseat/eseye/src/Cache/RedisCache.php', + 'Seat\\Eseye\\Checker\\Claim\\AzpChecker' => __DIR__ . '/..' . '/eveseat/eseye/src/Checker/Claim/AzpChecker.php', + 'Seat\\Eseye\\Checker\\Claim\\NameChecker' => __DIR__ . '/..' . '/eveseat/eseye/src/Checker/Claim/NameChecker.php', + 'Seat\\Eseye\\Checker\\Claim\\OwnerChecker' => __DIR__ . '/..' . '/eveseat/eseye/src/Checker/Claim/OwnerChecker.php', + 'Seat\\Eseye\\Checker\\Claim\\SubEveCharacterChecker' => __DIR__ . '/..' . '/eveseat/eseye/src/Checker/Claim/SubEveCharacterChecker.php', + 'Seat\\Eseye\\Checker\\Header\\TypeChecker' => __DIR__ . '/..' . '/eveseat/eseye/src/Checker/Header/TypeChecker.php', + 'Seat\\Eseye\\Configuration' => __DIR__ . '/..' . '/eveseat/eseye/src/Configuration.php', + 'Seat\\Eseye\\Containers\\AbstractArrayAccess' => __DIR__ . '/..' . '/eveseat/eseye/src/Containers/AbstractArrayAccess.php', + 'Seat\\Eseye\\Containers\\EsiAuthentication' => __DIR__ . '/..' . '/eveseat/eseye/src/Containers/EsiAuthentication.php', + 'Seat\\Eseye\\Containers\\EsiConfiguration' => __DIR__ . '/..' . '/eveseat/eseye/src/Containers/EsiConfiguration.php', + 'Seat\\Eseye\\Containers\\EsiResponse' => __DIR__ . '/..' . '/eveseat/eseye/src/Containers/EsiResponse.php', + 'Seat\\Eseye\\Eseye' => __DIR__ . '/..' . '/eveseat/eseye/src/Eseye.php', + 'Seat\\Eseye\\Exceptions\\CachePathException' => __DIR__ . '/..' . '/eveseat/eseye/src/Exceptions/CachePathException.php', + 'Seat\\Eseye\\Exceptions\\EsiScopeAccessDeniedException' => __DIR__ . '/..' . '/eveseat/eseye/src/Exceptions/EsiScopeAccessDeniedException.php', + 'Seat\\Eseye\\Exceptions\\InvalidAuthenticationException' => __DIR__ . '/..' . '/eveseat/eseye/src/Exceptions/InvalidAuthenticationException.php', + 'Seat\\Eseye\\Exceptions\\InvalidConfigurationException' => __DIR__ . '/..' . '/eveseat/eseye/src/Exceptions/InvalidConfigurationException.php', + 'Seat\\Eseye\\Exceptions\\InvalidContainerDataException' => __DIR__ . '/..' . '/eveseat/eseye/src/Exceptions/InvalidContainerDataException.php', + 'Seat\\Eseye\\Exceptions\\InvalidEsiSpecException' => __DIR__ . '/..' . '/eveseat/eseye/src/Exceptions/InvalidEsiSpecException.php', + 'Seat\\Eseye\\Exceptions\\RequestFailedException' => __DIR__ . '/..' . '/eveseat/eseye/src/Exceptions/RequestFailedException.php', + 'Seat\\Eseye\\Exceptions\\UriDataMissingException' => __DIR__ . '/..' . '/eveseat/eseye/src/Exceptions/UriDataMissingException.php', + 'Seat\\Eseye\\Fetchers\\FetcherInterface' => __DIR__ . '/..' . '/eveseat/eseye/src/Fetchers/FetcherInterface.php', + 'Seat\\Eseye\\Fetchers\\GuzzleFetcher' => __DIR__ . '/..' . '/eveseat/eseye/src/Fetchers/GuzzleFetcher.php', + 'Seat\\Eseye\\Log\\FileLogger' => __DIR__ . '/..' . '/eveseat/eseye/src/Log/FileLogger.php', + 'Seat\\Eseye\\Log\\LogInterface' => __DIR__ . '/..' . '/eveseat/eseye/src/Log/LogInterface.php', + 'Seat\\Eseye\\Log\\NullLogger' => __DIR__ . '/..' . '/eveseat/eseye/src/Log/NullLogger.php', + 'Seat\\Eseye\\Log\\RotatingFileLogger' => __DIR__ . '/..' . '/eveseat/eseye/src/Log/RotatingFileLogger.php', + 'Seat\\Eseye\\Traits\\ConstructsContainers' => __DIR__ . '/..' . '/eveseat/eseye/src/Traits/ConstructsContainers.php', + 'Seat\\Eseye\\Traits\\ValidatesContainers' => __DIR__ . '/..' . '/eveseat/eseye/src/Traits/ValidatesContainers.php', 'SebastianBergmann\\CodeCoverage\\CodeCoverage' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage.php', 'SebastianBergmann\\CodeCoverage\\CoveredCodeNotExecutedException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/CoveredCodeNotExecutedException.php', 'SebastianBergmann\\CodeCoverage\\Driver\\Driver' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Driver/Driver.php', diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index a260f40da..6922ac2fe 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -525,6 +525,72 @@ ], "install-path": "../egulias/email-validator" }, + { + "name": "eveseat/eseye", + "version": "2.3.0", + "version_normalized": "2.3.0.0", + "source": { + "type": "git", + "url": "https://github.com/eveseat/eseye.git", + "reference": "e1e310592ea03aa8a68728d8c15d7f04cf29f957" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/eveseat/eseye/zipball/e1e310592ea03aa8a68728d8c15d7f04cf29f957", + "reference": "e1e310592ea03aa8a68728d8c15d7f04cf29f957", + "shasum": "" + }, + "require": { + "ext-gmp": "*", + "ext-json": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "guzzlehttp/guzzle": "^6.2", + "monolog/monolog": "^2.0", + "nesbot/carbon": "^2.0", + "php": ">= 7.2", + "predis/predis": "^1.1", + "web-token/jwt-easy": "^2.1", + "web-token/jwt-signature-algorithm-ecdsa": "^2.1", + "web-token/jwt-signature-algorithm-hmac": "^2.1", + "web-token/jwt-signature-algorithm-rsa": "^2.1" + }, + "require-dev": { + "codeclimate/php-test-reporter": "dev-master", + "mikey179/vfsstream": "~1", + "phpunit/phpunit": "^5.7" + }, + "time": "2020-11-14T15:15:05+00:00", + "bin": [ + "bin/tokengenerator" + ], + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Seat\\Eseye\\": "src/" + }, + "files": [ + "src/Helpers/helpers.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0" + ], + "authors": [ + { + "name": "Leon Jacobs", + "email": "leonja511@gmail.com" + } + ], + "description": "A Standalone PHP ESI (EVE Swagger Interface) Client Library", + "support": { + "issues": "https://github.com/eveseat/eseye/issues", + "source": "https://github.com/eveseat/eseye/tree/2.3.0" + }, + "install-path": "../eveseat/eseye" + }, { "name": "facade/ignition-contracts", "version": "1.0.2", @@ -581,6 +647,84 @@ }, "install-path": "../facade/ignition-contracts" }, + { + "name": "fgrosse/phpasn1", + "version": "v2.2.0", + "version_normalized": "2.2.0.0", + "source": { + "type": "git", + "url": "https://github.com/fgrosse/PHPASN1.git", + "reference": "d1978f7abd580f3fc33561e7f71d4c12c7531fad" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fgrosse/PHPASN1/zipball/d1978f7abd580f3fc33561e7f71d4c12c7531fad", + "reference": "d1978f7abd580f3fc33561e7f71d4c12c7531fad", + "shasum": "" + }, + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": "~6.3", + "satooshi/php-coveralls": "~2.0" + }, + "suggest": { + "ext-bcmath": "BCmath is the fallback extension for big integer calculations", + "ext-curl": "For loading OID information from the web if they have not bee defined statically", + "ext-gmp": "GMP is the preferred extension for big integer calculations", + "phpseclib/bcmath_compat": "BCmath polyfill for servers where neither GMP nor BCmath is available" + }, + "time": "2020-10-11T16:28:18+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "FG\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Friedrich Große", + "email": "friedrich.grosse@gmail.com", + "homepage": "https://github.com/FGrosse", + "role": "Author" + }, + { + "name": "All contributors", + "homepage": "https://github.com/FGrosse/PHPASN1/contributors" + } + ], + "description": "A PHP Framework that allows you to encode and decode arbitrary ASN.1 structures using the ITU-T X.690 Encoding Rules.", + "homepage": "https://github.com/FGrosse/PHPASN1", + "keywords": [ + "DER", + "asn.1", + "asn1", + "ber", + "binary", + "decoding", + "encoding", + "x.509", + "x.690", + "x509", + "x690" + ], + "support": { + "issues": "https://github.com/fgrosse/PHPASN1/issues", + "source": "https://github.com/fgrosse/PHPASN1/tree/v2.2.0" + }, + "install-path": "../fgrosse/phpasn1" + }, { "name": "fideloper/proxy", "version": "4.4.1", @@ -3142,6 +3286,88 @@ ], "install-path": "../phpunit/phpunit" }, + { + "name": "predis/predis", + "version": "v1.1.6", + "version_normalized": "1.1.6.0", + "source": { + "type": "git", + "url": "https://github.com/predis/predis.git", + "reference": "9930e933c67446962997b05201c69c2319bf26de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/predis/predis/zipball/9930e933c67446962997b05201c69c2319bf26de", + "reference": "9930e933c67446962997b05201c69c2319bf26de", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "cweagans/composer-patches": "^1.6", + "phpunit/phpunit": "~4.8" + }, + "suggest": { + "ext-curl": "Allows access to Webdis when paired with phpiredis", + "ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol" + }, + "time": "2020-09-11T19:18:05+00:00", + "type": "library", + "extra": { + "composer-exit-on-patch-failure": true, + "patches": { + "phpunit/phpunit-mock-objects": { + "Fix PHP 7 and 8 compatibility": "./tests/phpunit_mock_objects.patch" + }, + "phpunit/phpunit": { + "Fix PHP 7 compatibility": "./tests/phpunit_php7.patch", + "Fix PHP 8 compatibility": "./tests/phpunit_php8.patch" + } + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Predis\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniele Alessandri", + "email": "suppakilla@gmail.com", + "homepage": "http://clorophilla.net", + "role": "Creator & Maintainer" + }, + { + "name": "Till Krüss", + "homepage": "https://till.im", + "role": "Maintainer" + } + ], + "description": "Flexible and feature-complete Redis client for PHP and HHVM", + "homepage": "http://github.com/predis/predis", + "keywords": [ + "nosql", + "predis", + "redis" + ], + "support": { + "issues": "https://github.com/predis/predis/issues", + "source": "https://github.com/predis/predis/tree/v1.1.6" + }, + "funding": [ + { + "url": "https://github.com/sponsors/tillkruss", + "type": "github" + } + ], + "install-path": "../predis/predis" + }, { "name": "psr/container", "version": "1.0.0", @@ -4286,6 +4512,74 @@ "homepage": "https://github.com/sebastianbergmann/version", "install-path": "../sebastian/version" }, + { + "name": "spomky-labs/base64url", + "version": "v2.0.4", + "version_normalized": "2.0.4.0", + "source": { + "type": "git", + "url": "https://github.com/Spomky-Labs/base64url.git", + "reference": "7752ce931ec285da4ed1f4c5aa27e45e097be61d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Spomky-Labs/base64url/zipball/7752ce931ec285da4ed1f4c5aa27e45e097be61d", + "reference": "7752ce931ec285da4ed1f4c5aa27e45e097be61d", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.11|^0.12", + "phpstan/phpstan-beberlei-assert": "^0.11|^0.12", + "phpstan/phpstan-deprecation-rules": "^0.11|^0.12", + "phpstan/phpstan-phpunit": "^0.11|^0.12", + "phpstan/phpstan-strict-rules": "^0.11|^0.12" + }, + "time": "2020-11-03T09:10:25+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Base64Url\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky-Labs/base64url/contributors" + } + ], + "description": "Base 64 URL Safe Encoding/Decoding PHP Library", + "homepage": "https://github.com/Spomky-Labs/base64url", + "keywords": [ + "base64", + "rfc4648", + "safe", + "url" + ], + "support": { + "issues": "https://github.com/Spomky-Labs/base64url/issues", + "source": "https://github.com/Spomky-Labs/base64url/tree/v2.0.4" + }, + "funding": [ + { + "url": "https://github.com/Spomky", + "type": "github" + }, + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "install-path": "../spomky-labs/base64url" + }, { "name": "swiftmailer/swiftmailer", "version": "v6.2.4", @@ -6679,6 +6973,728 @@ ], "install-path": "../voku/portable-ascii" }, + { + "name": "web-token/jwt-checker", + "version": "v2.2.6", + "version_normalized": "2.2.6.0", + "source": { + "type": "git", + "url": "https://github.com/web-token/jwt-checker.git", + "reference": "73ffe42041b0b8d6ddce56e42533a931d249629c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-token/jwt-checker/zipball/73ffe42041b0b8d6ddce56e42533a931d249629c", + "reference": "73ffe42041b0b8d6ddce56e42533a931d249629c", + "shasum": "" + }, + "require": { + "web-token/jwt-core": "^2.1" + }, + "require-dev": { + "phpunit/phpunit": "^8.0" + }, + "time": "2020-09-29T22:16:17+00:00", + "type": "library", + "extra": { + "branch-alias": { + "v1.0": "1.0.x-dev", + "v1.1": "1.1.x-dev", + "v1.2": "1.2.x-dev", + "v1.3": "1.3.x-dev", + "v2.0": "2.0.x-dev", + "v2.1": "2.1.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Jose\\Component\\Checker\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-checker/contributors" + } + ], + "description": "Checker component of the JWT Framework.", + "homepage": "https://github.com/web-token", + "keywords": [ + "JOSE", + "JWE", + "JWK", + "JWKSet", + "JWS", + "Jot", + "RFC7515", + "RFC7516", + "RFC7517", + "RFC7518", + "RFC7519", + "RFC7520", + "bundle", + "jwa", + "jwt", + "symfony" + ], + "support": { + "source": "https://github.com/web-token/jwt-checker/tree/v2.2.6" + }, + "funding": [ + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "install-path": "../web-token/jwt-checker" + }, + { + "name": "web-token/jwt-core", + "version": "v2.2.6", + "version_normalized": "2.2.6.0", + "source": { + "type": "git", + "url": "https://github.com/web-token/jwt-core.git", + "reference": "8115d846f3a9ee817dca03024eb4c17a3c0ff81a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-token/jwt-core/zipball/8115d846f3a9ee817dca03024eb4c17a3c0ff81a", + "reference": "8115d846f3a9ee817dca03024eb4c17a3c0ff81a", + "shasum": "" + }, + "require": { + "brick/math": "^0.8.17|^0.9", + "ext-json": "*", + "ext-mbstring": "*", + "fgrosse/phpasn1": "^2.0", + "php": ">=7.2", + "spomky-labs/base64url": "^1.0|^2.0" + }, + "conflict": { + "spomky-labs/jose": "*" + }, + "require-dev": { + "phpunit/phpunit": "^8.0" + }, + "time": "2020-09-29T21:35:58+00:00", + "type": "library", + "extra": { + "branch-alias": { + "v1.0": "1.0.x-dev", + "v1.1": "1.1.x-dev", + "v1.2": "1.2.x-dev", + "v1.3": "1.3.x-dev", + "v2.0": "2.0.x-dev", + "v2.1": "2.1.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Jose\\Component\\Core\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-framework/contributors" + } + ], + "description": "Core component of the JWT Framework.", + "homepage": "https://github.com/web-token", + "keywords": [ + "JOSE", + "JWE", + "JWK", + "JWKSet", + "JWS", + "Jot", + "RFC7515", + "RFC7516", + "RFC7517", + "RFC7518", + "RFC7519", + "RFC7520", + "bundle", + "jwa", + "jwt", + "symfony" + ], + "support": { + "source": "https://github.com/web-token/jwt-core/tree/v2.2.6" + }, + "funding": [ + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "install-path": "../web-token/jwt-core" + }, + { + "name": "web-token/jwt-easy", + "version": "v2.2.6", + "version_normalized": "2.2.6.0", + "source": { + "type": "git", + "url": "https://github.com/web-token/jwt-easy.git", + "reference": "cc59f479dd6b5014586be5d2d40ce84bbd9b4539" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-token/jwt-easy/zipball/cc59f479dd6b5014586be5d2d40ce84bbd9b4539", + "reference": "cc59f479dd6b5014586be5d2d40ce84bbd9b4539", + "shasum": "" + }, + "require": { + "web-token/jwt-checker": "^2.1", + "web-token/jwt-encryption": "^2.1", + "web-token/jwt-signature": "^2.1" + }, + "require-dev": { + "phpunit/phpunit": "^8.0" + }, + "suggest": { + "web-token/jwt-encryption-algorithm-aescbc": "Adds AES-CBC based encryption algorithms", + "web-token/jwt-encryption-algorithm-aesgcm": "Adds AES-GCM based encryption algorithms", + "web-token/jwt-encryption-algorithm-aesgcmkw": "Adds AES-GCM Key Wrapping based encryption algorithms", + "web-token/jwt-encryption-algorithm-aeskw": "Adds AES Key Wrapping based encryption algorithms", + "web-token/jwt-encryption-algorithm-dir": "Adds Direct encryption algorithm", + "web-token/jwt-encryption-algorithm-ecdh-es": "Adds ECDH-ES based encryption algorithms", + "web-token/jwt-encryption-algorithm-pbes2": "Adds PBES2 based encryption algorithms", + "web-token/jwt-encryption-algorithm-rsa": "Adds RSA based encryption algorithms", + "web-token/jwt-signature-algorithm-ecdsa": "Adds ECDSA based signature algorithms", + "web-token/jwt-signature-algorithm-eddsa": "Adds EdDSA based signature algorithms", + "web-token/jwt-signature-algorithm-hmac": "Adds HMAC based signature algorithms", + "web-token/jwt-signature-algorithm-none": "Adds none signature algorithms", + "web-token/jwt-signature-algorithm-rsa": "Adds RSA based signature algorithms" + }, + "time": "2020-12-07T21:10:31+00:00", + "type": "library", + "extra": { + "branch-alias": { + "v1.0": "1.0.x-dev", + "v1.1": "1.1.x-dev", + "v1.2": "1.2.x-dev", + "v1.3": "1.3.x-dev", + "v2.0": "2.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Jose\\Easy\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-framework/contributors" + } + ], + "description": "Easy toolset to use the JWT Framework.", + "homepage": "https://github.com/web-token", + "keywords": [ + "JOSE", + "JWE", + "JWK", + "JWKSet", + "JWS", + "Jot", + "RFC7515", + "RFC7516", + "RFC7517", + "RFC7518", + "RFC7519", + "RFC7520", + "bundle", + "jwa", + "jwt", + "symfony" + ], + "support": { + "source": "https://github.com/web-token/jwt-easy/tree/v2.2.6" + }, + "funding": [ + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "install-path": "../web-token/jwt-easy" + }, + { + "name": "web-token/jwt-encryption", + "version": "v2.2.6", + "version_normalized": "2.2.6.0", + "source": { + "type": "git", + "url": "https://github.com/web-token/jwt-encryption.git", + "reference": "36afc9f25fe8546840dc63c3fc1c6fe166879052" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-token/jwt-encryption/zipball/36afc9f25fe8546840dc63c3fc1c6fe166879052", + "reference": "36afc9f25fe8546840dc63c3fc1c6fe166879052", + "shasum": "" + }, + "require": { + "web-token/jwt-core": "^2.1" + }, + "require-dev": { + "phpunit/phpunit": "^8.0" + }, + "suggest": { + "web-token/jwt-encryption-algorithm-aescbc": "AES CBC Based Content Encryption Algorithms", + "web-token/jwt-encryption-algorithm-aesgcm": "AES GCM Based Content Encryption Algorithms", + "web-token/jwt-encryption-algorithm-aesgcmkw": "AES GCM Key Wrapping Based Key Encryption Algorithms", + "web-token/jwt-encryption-algorithm-aeskw": "AES Key Wrapping Based Key Encryption Algorithms", + "web-token/jwt-encryption-algorithm-dir": "Direct Key Encryption Algorithms", + "web-token/jwt-encryption-algorithm-ecdh-es": "ECDH-ES Based Key Encryption Algorithms", + "web-token/jwt-encryption-algorithm-experimental": "Experimental Key and Signature Algorithms", + "web-token/jwt-encryption-algorithm-pbes2": "PBES2 Based Key Encryption Algorithms", + "web-token/jwt-encryption-algorithm-rsa": "RSA Based Key Encryption Algorithms" + }, + "time": "2020-08-01T11:48:26+00:00", + "type": "library", + "extra": { + "branch-alias": { + "v1.0": "1.0.x-dev", + "v1.1": "1.1.x-dev", + "v1.2": "1.2.x-dev", + "v1.3": "1.3.x-dev", + "v2.0": "2.0.x-dev", + "v2.1": "2.1.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Jose\\Component\\Encryption\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-encryption/contributors" + } + ], + "description": "Encryption component of the JWT Framework.", + "homepage": "https://github.com/web-token", + "keywords": [ + "JOSE", + "JWE", + "JWK", + "JWKSet", + "JWS", + "Jot", + "RFC7515", + "RFC7516", + "RFC7517", + "RFC7518", + "RFC7519", + "RFC7520", + "bundle", + "jwa", + "jwt", + "symfony" + ], + "support": { + "source": "https://github.com/web-token/jwt-encryption/tree/v2.2.1" + }, + "funding": [ + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "install-path": "../web-token/jwt-encryption" + }, + { + "name": "web-token/jwt-signature", + "version": "v2.2.6", + "version_normalized": "2.2.6.0", + "source": { + "type": "git", + "url": "https://github.com/web-token/jwt-signature.git", + "reference": "f4d83f77031c9d27edebbea053931074030d3b09" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-token/jwt-signature/zipball/f4d83f77031c9d27edebbea053931074030d3b09", + "reference": "f4d83f77031c9d27edebbea053931074030d3b09", + "shasum": "" + }, + "require": { + "web-token/jwt-core": "^2.1" + }, + "require-dev": { + "phpunit/phpunit": "^8.0" + }, + "suggest": { + "web-token/jwt-signature-algorithm-ecdsa": "ECDSA Based Signature Algorithms", + "web-token/jwt-signature-algorithm-eddsa": "EdDSA Based Signature Algorithms", + "web-token/jwt-signature-algorithm-experimental": "Experimental Signature Algorithms", + "web-token/jwt-signature-algorithm-hmac": "HMAC Based Signature Algorithms", + "web-token/jwt-signature-algorithm-none": "None Signature Algorithm", + "web-token/jwt-signature-algorithm-rsa": "RSA Based Signature Algorithms" + }, + "time": "2020-08-01T11:48:26+00:00", + "type": "library", + "extra": { + "branch-alias": { + "v1.0": "1.0.x-dev", + "v1.1": "1.1.x-dev", + "v1.2": "1.2.x-dev", + "v1.3": "1.3.x-dev", + "v2.0": "2.0.x-dev", + "v2.1": "2.1.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Jose\\Component\\Signature\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-signature/contributors" + } + ], + "description": "Signature component of the JWT Framework.", + "homepage": "https://github.com/web-token", + "keywords": [ + "JOSE", + "JWE", + "JWK", + "JWKSet", + "JWS", + "Jot", + "RFC7515", + "RFC7516", + "RFC7517", + "RFC7518", + "RFC7519", + "RFC7520", + "bundle", + "jwa", + "jwt", + "symfony" + ], + "support": { + "source": "https://github.com/web-token/jwt-signature/tree/v2.2.0" + }, + "funding": [ + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "install-path": "../web-token/jwt-signature" + }, + { + "name": "web-token/jwt-signature-algorithm-ecdsa", + "version": "v2.2.6", + "version_normalized": "2.2.6.0", + "source": { + "type": "git", + "url": "https://github.com/web-token/jwt-signature-algorithm-ecdsa.git", + "reference": "3ae6e56ccf34e8b7b65ec988497d5d1bceca9864" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-token/jwt-signature-algorithm-ecdsa/zipball/3ae6e56ccf34e8b7b65ec988497d5d1bceca9864", + "reference": "3ae6e56ccf34e8b7b65ec988497d5d1bceca9864", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "web-token/jwt-signature": "^2.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.0" + }, + "time": "2020-08-22T13:17:25+00:00", + "type": "library", + "extra": { + "branch-alias": { + "v1.0": "1.0.x-dev", + "v1.1": "1.1.x-dev", + "v1.2": "1.2.x-dev", + "v1.3": "1.3.x-dev", + "v2.0": "2.0.x-dev", + "v2.1": "2.1.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Jose\\Component\\Signature\\Algorithm\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-framework/contributors" + } + ], + "description": "ECDSA Based Signature Algorithms the JWT Framework.", + "homepage": "https://github.com/web-token", + "keywords": [ + "JOSE", + "JWE", + "JWK", + "JWKSet", + "JWS", + "Jot", + "RFC7515", + "RFC7516", + "RFC7517", + "RFC7518", + "RFC7519", + "RFC7520", + "bundle", + "jwa", + "jwt", + "symfony" + ], + "support": { + "source": "https://github.com/web-token/jwt-signature-algorithm-ecdsa/tree/v2.2" + }, + "funding": [ + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "install-path": "../web-token/jwt-signature-algorithm-ecdsa" + }, + { + "name": "web-token/jwt-signature-algorithm-hmac", + "version": "v2.2.6", + "version_normalized": "2.2.6.0", + "source": { + "type": "git", + "url": "https://github.com/web-token/jwt-signature-algorithm-hmac.git", + "reference": "f4fd8166e6830298fc820a8d5776c124cc2496c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-token/jwt-signature-algorithm-hmac/zipball/f4fd8166e6830298fc820a8d5776c124cc2496c4", + "reference": "f4fd8166e6830298fc820a8d5776c124cc2496c4", + "shasum": "" + }, + "require": { + "web-token/jwt-signature": "^2.1" + }, + "require-dev": { + "phpunit/phpunit": "^8.0" + }, + "time": "2020-08-01T11:48:26+00:00", + "type": "library", + "extra": { + "branch-alias": { + "v1.0": "1.0.x-dev", + "v1.1": "1.1.x-dev", + "v1.2": "1.2.x-dev", + "v1.3": "1.3.x-dev", + "v2.0": "2.0.x-dev", + "v2.1": "2.1.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Jose\\Component\\Signature\\Algorithm\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-framework/contributors" + } + ], + "description": "HMAC Based Signature Algorithms the JWT Framework.", + "homepage": "https://github.com/web-token", + "keywords": [ + "JOSE", + "JWE", + "JWK", + "JWKSet", + "JWS", + "Jot", + "RFC7515", + "RFC7516", + "RFC7517", + "RFC7518", + "RFC7519", + "RFC7520", + "bundle", + "jwa", + "jwt", + "symfony" + ], + "support": { + "source": "https://github.com/web-token/jwt-signature-algorithm-hmac/tree/v2.2.2" + }, + "funding": [ + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "install-path": "../web-token/jwt-signature-algorithm-hmac" + }, + { + "name": "web-token/jwt-signature-algorithm-rsa", + "version": "v2.2.6", + "version_normalized": "2.2.6.0", + "source": { + "type": "git", + "url": "https://github.com/web-token/jwt-signature-algorithm-rsa.git", + "reference": "ba94b8868bd8fdd481f448097ab900a8cb2c0eed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/web-token/jwt-signature-algorithm-rsa/zipball/ba94b8868bd8fdd481f448097ab900a8cb2c0eed", + "reference": "ba94b8868bd8fdd481f448097ab900a8cb2c0eed", + "shasum": "" + }, + "require": { + "brick/math": "^0.8.17|^0.9", + "ext-openssl": "*", + "web-token/jwt-signature": "^2.1" + }, + "require-dev": { + "phpunit/phpunit": "^8.0" + }, + "suggest": { + "ext-bcmath": "GMP or BCMath is highly recommended to improve the library performance", + "ext-gmp": "GMP or BCMath is highly recommended to improve the library performance" + }, + "time": "2020-08-22T13:37:56+00:00", + "type": "library", + "extra": { + "branch-alias": { + "v1.0": "1.0.x-dev", + "v1.1": "1.1.x-dev", + "v1.2": "1.2.x-dev", + "v1.3": "1.3.x-dev", + "v2.0": "2.0.x-dev", + "v2.1": "2.1.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Jose\\Component\\Signature\\Algorithm\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-framework/contributors" + } + ], + "description": "RSA Based Signature Algorithms the JWT Framework.", + "homepage": "https://github.com/web-token", + "keywords": [ + "JOSE", + "JWE", + "JWK", + "JWKSet", + "JWS", + "Jot", + "RFC7515", + "RFC7516", + "RFC7517", + "RFC7518", + "RFC7519", + "RFC7520", + "bundle", + "jwa", + "jwt", + "symfony" + ], + "support": { + "source": "https://github.com/web-token/jwt-signature-algorithm-rsa/tree/v2.2" + }, + "funding": [ + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "install-path": "../web-token/jwt-signature-algorithm-rsa" + }, { "name": "webmozart/assert", "version": "1.9.1", diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 10704ec55..cfd783179 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -6,7 +6,7 @@ 'aliases' => array ( ), - 'reference' => '516105c492788aee680ef1dce3d45e514d703747', + 'reference' => '0ddd2983509921acf00db95c3cd019abf733acb1', 'name' => 'laravel/laravel', ), 'versions' => @@ -97,6 +97,15 @@ ), 'reference' => 'ca90a3291eee1538cd48ff25163240695bd95448', ), + 'eveseat/eseye' => + array ( + 'pretty_version' => '2.3.0', + 'version' => '2.3.0.0', + 'aliases' => + array ( + ), + 'reference' => 'e1e310592ea03aa8a68728d8c15d7f04cf29f957', + ), 'facade/ignition-contracts' => array ( 'pretty_version' => '1.0.2', @@ -106,6 +115,15 @@ ), 'reference' => '3c921a1cdba35b68a7f0ccffc6dffc1995b18267', ), + 'fgrosse/phpasn1' => + array ( + 'pretty_version' => 'v2.2.0', + 'version' => '2.2.0.0', + 'aliases' => + array ( + ), + 'reference' => 'd1978f7abd580f3fc33561e7f71d4c12c7531fad', + ), 'fideloper/proxy' => array ( 'pretty_version' => '4.4.1', @@ -422,7 +440,7 @@ 'aliases' => array ( ), - 'reference' => '516105c492788aee680ef1dce3d45e514d703747', + 'reference' => '0ddd2983509921acf00db95c3cd019abf733acb1', ), 'laravel/socialite' => array ( @@ -662,6 +680,15 @@ ), 'reference' => '8e86be391a58104ef86037ba8a846524528d784e', ), + 'predis/predis' => + array ( + 'pretty_version' => 'v1.1.6', + 'version' => '1.1.6.0', + 'aliases' => + array ( + ), + 'reference' => '9930e933c67446962997b05201c69c2319bf26de', + ), 'psr/container' => array ( 'pretty_version' => '1.0.0', @@ -878,6 +905,15 @@ ), 'reference' => '99732be0ddb3361e16ad77b68ba41efc8e979019', ), + 'spomky-labs/base64url' => + array ( + 'pretty_version' => 'v2.0.4', + 'version' => '2.0.4.0', + 'aliases' => + array ( + ), + 'reference' => '7752ce931ec285da4ed1f4c5aa27e45e097be61d', + ), 'swiftmailer/swiftmailer' => array ( 'pretty_version' => 'v6.2.4', @@ -1196,6 +1232,78 @@ ), 'reference' => '80953678b19901e5165c56752d087fc11526017c', ), + 'web-token/jwt-checker' => + array ( + 'pretty_version' => 'v2.2.6', + 'version' => '2.2.6.0', + 'aliases' => + array ( + ), + 'reference' => '73ffe42041b0b8d6ddce56e42533a931d249629c', + ), + 'web-token/jwt-core' => + array ( + 'pretty_version' => 'v2.2.6', + 'version' => '2.2.6.0', + 'aliases' => + array ( + ), + 'reference' => '8115d846f3a9ee817dca03024eb4c17a3c0ff81a', + ), + 'web-token/jwt-easy' => + array ( + 'pretty_version' => 'v2.2.6', + 'version' => '2.2.6.0', + 'aliases' => + array ( + ), + 'reference' => 'cc59f479dd6b5014586be5d2d40ce84bbd9b4539', + ), + 'web-token/jwt-encryption' => + array ( + 'pretty_version' => 'v2.2.6', + 'version' => '2.2.6.0', + 'aliases' => + array ( + ), + 'reference' => '36afc9f25fe8546840dc63c3fc1c6fe166879052', + ), + 'web-token/jwt-signature' => + array ( + 'pretty_version' => 'v2.2.6', + 'version' => '2.2.6.0', + 'aliases' => + array ( + ), + 'reference' => 'f4d83f77031c9d27edebbea053931074030d3b09', + ), + 'web-token/jwt-signature-algorithm-ecdsa' => + array ( + 'pretty_version' => 'v2.2.6', + 'version' => '2.2.6.0', + 'aliases' => + array ( + ), + 'reference' => '3ae6e56ccf34e8b7b65ec988497d5d1bceca9864', + ), + 'web-token/jwt-signature-algorithm-hmac' => + array ( + 'pretty_version' => 'v2.2.6', + 'version' => '2.2.6.0', + 'aliases' => + array ( + ), + 'reference' => 'f4fd8166e6830298fc820a8d5776c124cc2496c4', + ), + 'web-token/jwt-signature-algorithm-rsa' => + array ( + 'pretty_version' => 'v2.2.6', + 'version' => '2.2.6.0', + 'aliases' => + array ( + ), + 'reference' => 'ba94b8868bd8fdd481f448097ab900a8cb2c0eed', + ), 'webmozart/assert' => array ( 'pretty_version' => '1.9.1', diff --git a/vendor/eveseat/eseye/.codeclimate.yml b/vendor/eveseat/eseye/.codeclimate.yml new file mode 100644 index 000000000..b43927404 --- /dev/null +++ b/vendor/eveseat/eseye/.codeclimate.yml @@ -0,0 +1,24 @@ +--- +engines: + duplication: + enabled: true + config: + languages: + - javascript + - php + fixme: + enabled: true + phpmd: + enabled: true + checks: + Controversial/CamelCaseParameterName: + enabled: false + Controversial/CamelCasePropertyName: + enabled: false + Controversial/CamelCaseVariableName: + enabled: false +ratings: + paths: + - "**.php" +exclude_paths: +- tests/ diff --git a/vendor/eveseat/eseye/.gitignore b/vendor/eveseat/eseye/.gitignore new file mode 100644 index 000000000..56e7dac18 --- /dev/null +++ b/vendor/eveseat/eseye/.gitignore @@ -0,0 +1,6 @@ +/vendor/ +composer.lock +.idea/ +/logs/ +/cache/ +/build/ diff --git a/vendor/eveseat/eseye/.styleci.yml b/vendor/eveseat/eseye/.styleci.yml new file mode 100644 index 000000000..6f69d566a --- /dev/null +++ b/vendor/eveseat/eseye/.styleci.yml @@ -0,0 +1,19 @@ +preset: laravel + +risky: false + +enabled: + - no_empty_comment + +disabled: + - psr12_braces + - concat_without_spaces + - phpdoc_no_package + - no_blank_lines_after_class_opening + - no_blank_lines_after_throw + +finder: + exclude: + - "tests" + name: + - "*.php" diff --git a/vendor/eveseat/eseye/.travis.yml b/vendor/eveseat/eseye/.travis.yml new file mode 100644 index 000000000..900f7343c --- /dev/null +++ b/vendor/eveseat/eseye/.travis.yml @@ -0,0 +1,24 @@ +language: php + +php: + - 7.2 + - 7.3 + +before_script: + - echo "extension = memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini + - travis_retry composer self-update + - travis_retry composer install + +notifications: + on_success: never + on_failure: always + +addons: + code_climate: + repo_token: $CODE_CLIMATE_TOKEN + +script: + - vendor/bin/phpunit --verbose --coverage-clover build/logs/clover.xml + +after_success: + - vendor/bin/test-reporter diff --git a/vendor/eveseat/eseye/LICENSE b/vendor/eveseat/eseye/LICENSE new file mode 100644 index 000000000..23cb79033 --- /dev/null +++ b/vendor/eveseat/eseye/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/vendor/eveseat/eseye/README.md b/vendor/eveseat/eseye/README.md new file mode 100644 index 000000000..208ba54a6 --- /dev/null +++ b/vendor/eveseat/eseye/README.md @@ -0,0 +1,38 @@ +![SeAT](http://i.imgur.com/aPPOxSK.png) + +[![Build Status](https://travis-ci.org/eveseat/eseye.svg?branch=master)](https://travis-ci.org/eveseat/eseye) +[![Code Climate](https://codeclimate.com/github/eveseat/eseye/badges/gpa.svg)](https://codeclimate.com/github/eveseat/eseye) +[![Test Coverage](https://codeclimate.com/github/eveseat/eseye/badges/coverage.svg)](https://codeclimate.com/github/eveseat/eseye/coverage) +[![Latest Stable Version](https://poser.pugx.org/eveseat/eseye/v/stable)](https://packagist.org/packages/eveseat/eseye) +[![Total Downloads](https://poser.pugx.org/eveseat/eseye/downloads)](https://packagist.org/packages/eveseat/eseye) +[![Latest Unstable Version](https://poser.pugx.org/eveseat/eseye/v/unstable)](https://packagist.org/packages/eveseat/eseye) +[![License](https://poser.pugx.org/eveseat/eseye/license)](https://packagist.org/packages/eseye/eveapi) +[![StyleCI](https://styleci.io/repos/78866259/shield?branch=master)](https://styleci.io/repos/78866259) + +# eseye +👾 A Standalone, Dynamic ESI (EVE Swagger Interface) Client Library written in PHP + +## example usage +Its supposed to be simple! + +```php +// initialization stuff +$esi = new Eseye(); + +// Optionally, set the ESI endpoint version to use. +// If you dont set this, Eseye will use /latest +$esi->setVersion('v4'); + +// make a call +$character_info = $esi->invoke('get', '/characters/{character_id}/', [ + 'character_id' => 1477919642, +]); + +// get data! +echo $character_info->name; +``` + +For a more complete usage example, please refer to [example.php](example.php) + +## documentation +For up to date documentation, more examples and other goodies, please check out the [project wiki](https://github.com/eveseat/eseye/wiki)! diff --git a/vendor/eveseat/eseye/bin/index.php b/vendor/eveseat/eseye/bin/index.php new file mode 100644 index 000000000..2a7f19c36 --- /dev/null +++ b/vendor/eveseat/eseye/bin/index.php @@ -0,0 +1,400 @@ + + + + + + + + + New ESI Refresh Token + + + + + + + + + + + + + +
+
+

ESI Refresh Token Generator

+
+EOF; + +} + +/** + * @return string + */ +function get_footer() +{ + + return <<<'EOF' +
+ + +EOF; + +} + +// Page contents + +/** + * Fresh, new login page. + */ +function new_login() +{ + + $action = $_SERVER['PHP_SELF'] . '?action=submitsecrets'; + $callback = get_sso_callback_url(); + + echo get_header(); + echo << +

+ Create a new Application on the + EVE Online Developers Site. + Use the resultant Client ID and Secret Key in the form below. +

+

+ The callback url to use in the application form is:

$callback
+

+ + +
+ +
+
+ + +
+ +
+ + ClientID From the EVE Online Developers Site +
+
+ + +
+ +
+ + Secret From the EVE Online Developers Site +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ +
+
+ +
+EOF; + echo get_footer(); + +} + +/** + * @param $url + */ +function print_sso_url($url) +{ + + echo get_header(); + echo << +

+ Click the button below to login with your EVE Online account.
+ + + +

+

+ The generated URL is: +

$url
+

+ +EOF; + echo get_footer(); + +} + +/** + * @param $access_token + * @param $refresh_token + */ +function print_tokens($access_token, $refresh_token) +{ + + $start_again_url = $_SERVER['PHP_SELF'] . '?action=new'; + + echo get_header(); + echo << +

+ Your current access token is:

$access_token

+ Valid for ~20 minutes. +

+

+ Your refresh token is:

$refresh_token

+ Valid until you delete the app from your account + here. +

+ Start Again + +EOF; + echo get_footer(); +} + +// Ensure we have an action! +if (! isset($_GET['action'])) + redirect_to_new(); + +// Worlds most caveman router! + +// Decide where to go based on the value of 'action' +switch ($_GET['action']) { + + // Display the form to create a new login. + case 'new': + $_SESSION['test'] = 'bob'; + new_login(); + break; + + case 'submitsecrets': + // Ensure we got some values + if (! isset($_REQUEST['clientid']) || + ! isset($_REQUEST['secret']) || + ! isset($_REQUEST['scopes']) + ) { + + echo 'All fields are mandatory!
' . PHP_EOL; + echo 'Start again'; + + die(); + } + + $_SESSION['clientid'] = $_REQUEST['clientid']; + $_SESSION['secret'] = $_REQUEST['secret']; + $_SESSION['state'] = uniqid(); + + // Generate the url with the requested scopes + $url = 'https://login.eveonline.com/v2/oauth/authorize/?response_type=code&redirect_uri=' . + urlencode(get_sso_callback_url()) . '&client_id=' . + $_SESSION['clientid'] . '&scope=' . implode(' ', $_REQUEST['scopes']) . ' &state=' . $_SESSION['state']; + + // Print the HTML with the login button. + print_sso_url($url); + break; + + case 'eveonlinecallback': + // Verify the state. + if ($_REQUEST['state'] != $_SESSION['state']) { + + echo 'Invalid State! You will have to start again!
'; + echo 'Start again'; + die(); + } + + // Clear the state value. + $_SESSION['state'] = null; + + // Prep the authentication header. + $headers = [ + 'Authorization: Basic ' . base64_encode($_SESSION['clientid'] . ':' . $_SESSION['secret']), + 'Content-Type: application/json', + ]; + + // Seems like CCP does not mind JSON in the body. Yay. + $fields = json_encode([ + 'grant_type' => 'authorization_code', + 'code' => $_REQUEST['code'], + ]); + + // Start a cURL session + $ch = curl_init('https://login.eveonline.com/v2/oauth/token'); + curl_setopt_array($ch, [ + CURLOPT_URL => 'https://login.eveonline.com/v2/oauth/token', + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => $fields, + CURLOPT_HTTPHEADER => $headers, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_USERAGENT => 'eseye/tokengenerator', + CURLOPT_SSL_VERIFYPEER => true, + CURLOPT_SSL_CIPHER_LIST => 'TLSv1', + ]); + + $result = curl_exec($ch); + + $data = json_decode($result); + + print_tokens($data->access_token, $data->refresh_token); + break; + + // If we dont know what 'action' to perform, then redirect. + default: + redirect_to_new(); + break; +} diff --git a/vendor/eveseat/eseye/bin/tokengenerator b/vendor/eveseat/eseye/bin/tokengenerator new file mode 100755 index 000000000..1fc5831d0 --- /dev/null +++ b/vendor/eveseat/eseye/bin/tokengenerator @@ -0,0 +1,18 @@ +#!/bin/bash + +# 2017 - Leon Jacobs + +# Server the token generator app using PHP's builtin web server. + +# Thanks: http://stackoverflow.com/questions/59895/getting-the-source-directory-of-a-bash-script-from-within?answertab=votes#tab-top +SOURCE="${BASH_SOURCE[0]}" +while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink + + DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + +done +DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" + +php -S 127.0.0.1:9009 -t $DIR diff --git a/vendor/eveseat/eseye/composer.json b/vendor/eveseat/eseye/composer.json new file mode 100644 index 000000000..6bea4d814 --- /dev/null +++ b/vendor/eveseat/eseye/composer.json @@ -0,0 +1,43 @@ +{ + "name": "eveseat/eseye", + "description": "A Standalone PHP ESI (EVE Swagger Interface) Client Library", + "type": "library", + "require": { + "php": ">= 7.2", + "ext-gmp": "*", + "ext-json": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "guzzlehttp/guzzle": "^6.2", + "nesbot/carbon": "^2.0", + "monolog/monolog": "^2.0", + "predis/predis": "^1.1", + "web-token/jwt-easy": "^2.1", + "web-token/jwt-signature-algorithm-hmac": "^2.1", + "web-token/jwt-signature-algorithm-rsa": "^2.1", + "web-token/jwt-signature-algorithm-ecdsa": "^2.1" + }, + "require-dev": { + "phpunit/phpunit": "^5.7", + "mikey179/vfsstream": "~1", + "codeclimate/php-test-reporter": "dev-master" + }, + "license": "GPL-2.0", + "authors": [ + { + "name": "Leon Jacobs", + "email": "leonja511@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Seat\\Eseye\\": "src/" + }, + "files": [ + "src/Helpers/helpers.php" + ] + }, + "bin": [ + "bin/tokengenerator" + ] +} diff --git a/vendor/eveseat/eseye/example.php b/vendor/eveseat/eseye/example.php new file mode 100644 index 000000000..b9e625007 --- /dev/null +++ b/vendor/eveseat/eseye/example.php @@ -0,0 +1,74 @@ +cache = NullCache::class; + +// Prepare an authentication container for ESI +$authentication = new EsiAuthentication([ + 'client_id' => 'SSO_CLIENT_ID', + 'secret' => 'SSO_SECRET', + 'refresh_token' => 'CHARACTER_REFRESH_TOKEN', +]); + +// Instantiate a new ESI instance. +$esi = new Eseye($authentication); + +// Get character information. This is a public call to the EVE +// Swagger Interface +$character_info = $esi->invoke('get', '/characters/{character_id}/', [ + 'character_id' => 1477919642, +]); + +// Get the location information for a character. This is an authenticated +// call to the EVE Swagger Interface. +$location = $esi->invoke('get', '/characters/{character_id}/location/', [ + 'character_id' => 1477919642, +]); + +$clones = $esi->invoke('get', '/characters/{character_id}/clones/', [ + 'character_id' => 1477919642, +]); + +// Print some information from the calls we have made. +echo 'Character Name is: ' . $character_info->name . PHP_EOL; +echo 'Character was born: ' . carbon($character_info->birthday) + ->diffForHumans() . PHP_EOL; // The 'carbon' helper is included in the package. +echo 'Home Solar System ID is: ' . $location->solar_system_id . PHP_EOL; +echo 'Home Station ID is: ' . $location->station_id . PHP_EOL; + +echo 'You have the following clones: ' . PHP_EOL; +foreach ($clones->jump_clones as $jump_clone) { + + echo 'Clone at a ' . $jump_clone->location_type . + ' with ' . count($jump_clone->implants) . ' implants' . PHP_EOL; +} diff --git a/vendor/eveseat/eseye/phpunit.xml b/vendor/eveseat/eseye/phpunit.xml new file mode 100644 index 000000000..02dc69671 --- /dev/null +++ b/vendor/eveseat/eseye/phpunit.xml @@ -0,0 +1,17 @@ + + + + + ./tests/ + + + + + ./src/ + + + diff --git a/vendor/eveseat/eseye/src/Access/AccessInterface.php b/vendor/eveseat/eseye/src/Access/AccessInterface.php new file mode 100644 index 000000000..fd6999e6d --- /dev/null +++ b/vendor/eveseat/eseye/src/Access/AccessInterface.php @@ -0,0 +1,39 @@ + [ + + // 'meta' URI's. see: https://esi.evetech.net/ui/?version=meta + '/ping' => 'public', + + // Generated using tools: php get_endpoints_and_scopes.php + '/alliances/{alliance_id}/' => 'public', + '/alliances/{alliance_id}/corporations/' => 'public', + '/alliances/names/' => 'public', + '/alliances/{alliance_id}/icons/' => 'public', + '/alliances/' => 'public', + '/characters/{character_id}/assets/' => 'esi-assets.read_assets.v1', + '/corporations/{corporation_id}/assets/' => 'esi-assets.read_corporation_assets.v1', + '/characters/{character_id}/bookmarks/' => 'esi-bookmarks.read_character_bookmarks.v1', + '/characters/{character_id}/bookmarks/folders/' => 'esi-bookmarks.read_character_bookmarks.v1', + '/corporations/{corporation_id}/bookmarks/' => 'esi-bookmarks.read_corporation_bookmarks.v1', + '/corporations/{corporation_id}/bookmarks/folders/' => 'esi-bookmarks.read_corporation_bookmarks.v1', + '/characters/{character_id}/calendar/' => 'esi-calendar.read_calendar_events.v1', + '/characters/{character_id}/calendar/{event_id}/' => 'esi-calendar.read_calendar_events.v1', + '/characters/{character_id}/calendar/{event_id}/attendees/' => 'esi-calendar.read_calendar_events.v1', + '/characters/{character_id}/stats/' => 'esi-characterstats.read.v1', + '/characters/{character_id}/' => 'public', + '/characters/names/' => 'public', + '/characters/{character_id}/portrait/' => 'public', + '/characters/{character_id}/corporationhistory/' => 'public', + '/characters/{character_id}/chat_channels/' => 'esi-characters.read_chat_channels.v1', + '/characters/{character_id}/medals/' => 'esi-characters.read_medals.v1', + '/characters/{character_id}/standings/' => 'esi-characters.read_standings.v1', + '/characters/{character_id}/agents_research/' => 'esi-characters.read_agents_research.v1', + '/characters/{character_id}/blueprints/' => 'esi-characters.read_blueprints.v1', + '/characters/{character_id}/fatigue/' => 'esi-characters.read_fatigue.v1', + '/characters/{character_id}/notifications/contacts/' => 'esi-characters.read_notifications.v1', + '/characters/{character_id}/notifications/' => 'esi-characters.read_notifications.v1', + '/characters/{character_id}/roles/' => 'esi-characters.read_corporation_roles.v1', + '/characters/{character_id}/titles/' => 'esi-characters.read_titles.v1', + '/characters/{character_id}/clones/' => 'esi-clones.read_clones.v1', + '/characters/{character_id}/implants/' => 'esi-clones.read_implants.v1', + '/characters/{character_id}/contacts/' => 'esi-characters.read_contacts.v1', + '/corporations/{corporation_id}/contacts/' => 'esi-corporations.read_contacts.v1', + '/alliances/{alliance_id}/contacts/' => 'esi-alliances.read_contacts.v1', + '/characters/{character_id}/contacts/labels/' => 'esi-characters.read_contacts.v1', + '/corporations/{corporation_id}/contacts/labels/' => 'esi-corporations.read_contacts.v1', + '/characters/{character_id}/contracts/' => 'esi-contracts.read_character_contracts.v1', + '/characters/{character_id}/contracts/{contract_id}/items/' => 'esi-contracts.read_character_contracts.v1', + '/characters/{character_id}/contracts/{contract_id}/bids/' => 'esi-contracts.read_character_contracts.v1', + '/corporations/{corporation_id}/contracts/' => 'esi-contracts.read_corporation_contracts.v1', + '/corporations/{corporation_id}/contracts/{contract_id}/items/' => 'esi-contracts.read_corporation_contracts.v1', + '/corporations/{corporation_id}/contracts/{contract_id}/bids/' => 'esi-contracts.read_corporation_contracts.v1', + '/corporations/{corporation_id}/shareholders/' => 'esi-wallet.read_corporation_wallets.v1', + '/corporations/{corporation_id}/' => 'public', + '/corporations/{corporation_id}/alliancehistory/' => 'public', + '/corporations/names/' => 'public', + '/corporations/{corporation_id}/members/' => 'esi-corporations.read_corporation_membership.v1', + '/corporations/{corporation_id}/roles/' => 'esi-corporations.read_corporation_membership.v1', + '/corporations/{corporation_id}/roles/history/' => 'esi-corporations.read_corporation_membership.v1', + '/corporations/{corporation_id}/icons/' => 'public', + '/corporations/npccorps/' => 'public', + '/corporations/{corporation_id}/structures/' => 'esi-corporations.read_structures.v1', + '/corporations/{corporation_id}/membertracking/' => 'esi-corporations.track_members.v1', + '/corporations/{corporation_id}/divisions/' => 'esi-corporations.read_divisions.v1', + '/corporations/{corporation_id}/members/limit/' => 'esi-corporations.track_members.v1', + '/corporations/{corporation_id}/titles/' => 'esi-corporations.read_titles.v1', + '/corporations/{corporation_id}/members/titles/' => 'esi-corporations.read_titles.v1', + '/corporations/{corporation_id}/blueprints/' => 'esi-corporations.read_blueprints.v1', + '/corporations/{corporation_id}/standings/' => 'esi-corporations.read_standings.v1', + '/corporations/{corporation_id}/starbases/' => 'esi-corporations.read_starbases.v1', + '/corporations/{corporation_id}/starbases/{starbase_id}/' => 'esi-corporations.read_starbases.v1', + '/corporations/{corporation_id}/containers/logs/' => 'esi-corporations.read_container_logs.v1', + '/corporations/{corporation_id}/facilities/' => 'esi-corporations.read_facilities.v1', + '/corporations/{corporation_id}/medals/' => 'esi-corporations.read_medals.v1', + '/corporations/{corporation_id}/medals/issued/' => 'esi-corporations.read_medals.v1', + '/dogma/attributes/' => 'public', + '/dogma/attributes/{attribute_id}/' => 'public', + '/dogma/effects/' => 'public', + '/dogma/effects/{effect_id}/' => 'public', + '/fw/wars/' => 'public', + '/fw/stats/' => 'public', + '/fw/systems/' => 'public', + '/fw/leaderboards/' => 'public', + '/fw/leaderboards/characters/' => 'public', + '/fw/leaderboards/corporations/' => 'public', + '/corporations/{corporation_id}/fw/stats/' => 'esi-corporations.read_fw_stats.v1', + '/characters/{character_id}/fw/stats/' => 'esi-characters.read_fw_stats.v1', + '/characters/{character_id}/fittings/' => 'esi-fittings.read_fittings.v1', + '/fleets/{fleet_id}/' => 'esi-fleets.read_fleet.v1', + '/characters/{character_id}/fleet/' => 'esi-fleets.read_fleet.v1', + '/fleets/{fleet_id}/members/' => 'esi-fleets.read_fleet.v1', + '/fleets/{fleet_id}/wings/' => 'esi-fleets.read_fleet.v1', + '/incursions/' => 'public', + '/industry/facilities/' => 'public', + '/industry/systems/' => 'public', + '/characters/{character_id}/industry/jobs/' => 'esi-industry.read_character_jobs.v1', + '/characters/{character_id}/mining/' => 'esi-industry.read_character_mining.v1', + '/corporation/{corporation_id}/mining/observers/' => 'esi-industry.read_corporation_mining.v1', + '/corporation/{corporation_id}/mining/observers/{observer_id}/' => 'esi-industry.read_corporation_mining.v1', + '/corporations/{corporation_id}/industry/jobs/' => 'esi-industry.read_corporation_jobs.v1', + '/corporation/{corporation_id}/mining/extractions/' => 'esi-industry.read_corporation_mining.v1', + '/insurance/prices/' => 'public', + '/killmails/{killmail_id}/{killmail_hash}/' => 'public', + '/characters/{character_id}/killmails/recent/' => 'esi-killmails.read_killmails.v1', + '/corporations/{corporation_id}/killmails/recent/' => 'esi-killmails.read_corporation_killmails.v1', + '/characters/{character_id}/location/' => 'esi-location.read_location.v1', + '/characters/{character_id}/ship/' => 'esi-location.read_ship_type.v1', + '/characters/{character_id}/online/' => 'esi-location.read_online.v1', + '/loyalty/stores/{corporation_id}/offers/' => 'public', + '/characters/{character_id}/loyalty/points/' => 'esi-characters.read_loyalty.v1', + '/characters/{character_id}/mail/' => 'esi-mail.read_mail.v1', + '/characters/{character_id}/mail/labels/' => 'esi-mail.read_mail.v1', + '/characters/{character_id}/mail/lists/' => 'esi-mail.read_mail.v1', + '/characters/{character_id}/mail/{mail_id}/' => 'esi-mail.read_mail.v1', + '/markets/prices/' => 'public', + '/markets/{region_id}/orders/' => 'public', + '/markets/{region_id}/history/' => 'public', + '/markets/structures/{structure_id}/' => 'esi-markets.structure_markets.v1', + '/markets/groups/' => 'public', + '/markets/groups/{market_group_id}/' => 'public', + '/characters/{character_id}/orders/' => 'esi-markets.read_character_orders.v1', + '/markets/{region_id}/types/' => 'public', + '/corporations/{corporation_id}/orders/' => 'esi-markets.read_corporation_orders.v1', + '/opportunities/groups/' => 'public', + '/opportunities/groups/{group_id}/' => 'public', + '/opportunities/tasks/' => 'public', + '/opportunities/tasks/{task_id}/' => 'public', + '/characters/{character_id}/opportunities/' => 'esi-characters.read_opportunities.v1', + '/characters/{character_id}/planets/' => 'esi-planets.manage_planets.v1', + '/characters/{character_id}/planets/{planet_id}/' => 'esi-planets.manage_planets.v1', + '/universe/schematics/{schematic_id}/' => 'public', + '/corporations/{corporation_id}/customs_offices/' => 'esi-planets.read_customs_offices.v1', + '/route/{origin}/{destination}/' => 'public', + '/characters/{character_id}/search/' => 'esi-search.search_structures.v1', + '/search/' => 'public', + '/characters/{character_id}/skillqueue/' => 'esi-skills.read_skillqueue.v1', + '/characters/{character_id}/skills/' => 'esi-skills.read_skills.v1', + '/characters/{character_id}/attributes/' => 'esi-skills.read_skills.v1', + '/sovereignty/structures/' => 'public', + '/sovereignty/campaigns/' => 'public', + '/sovereignty/map/' => 'public', + '/status/' => 'public', + '/universe/planets/{planet_id}/' => 'public', + '/universe/stations/{station_id}/' => 'public', + '/universe/structures/{structure_id}/' => 'esi-universe.read_structures.v1', + '/universe/systems/{system_id}/' => 'public', + '/universe/systems/' => 'public', + '/universe/types/{type_id}/' => 'public', + '/universe/types/' => 'public', + '/universe/groups/' => 'public', + '/universe/groups/{group_id}/' => 'public', + '/universe/categories/' => 'public', + '/universe/categories/{category_id}/' => 'public', + '/universe/structures/' => 'public', + '/universe/races/' => 'public', + '/universe/factions/' => 'public', + '/universe/bloodlines/' => 'public', + '/universe/regions/' => 'public', + '/universe/regions/{region_id}/' => 'public', + '/universe/constellations/' => 'public', + '/universe/constellations/{constellation_id}/' => 'public', + '/universe/moons/{moon_id}/' => 'public', + '/universe/stargates/{stargate_id}/' => 'public', + '/universe/graphics/' => 'public', + '/universe/graphics/{graphic_id}/' => 'public', + '/universe/system_jumps/' => 'public', + '/universe/system_kills/' => 'public', + '/universe/stars/{star_id}/' => 'public', + '/characters/{character_id}/wallet/' => 'esi-wallet.read_character_wallet.v1', + '/characters/{character_id}/wallet/journal/' => 'esi-wallet.read_character_wallet.v1', + '/characters/{character_id}/wallet/transactions/' => 'esi-wallet.read_character_wallet.v1', + '/corporations/{corporation_id}/wallets/' => 'esi-wallet.read_corporation_wallets.v1', + '/corporations/{corporation_id}/wallets/{division}/journal/' => 'esi-wallet.read_corporation_wallets.v1', + '/corporations/{corporation_id}/wallets/{division}/transactions/' => 'esi-wallet.read_corporation_wallets.v1', + '/wars/' => 'public', + '/wars/{war_id}/' => 'public', + '/wars/{war_id}/killmails/' => 'public', + ], + 'post' => [ + '/characters/{character_id}/assets/names/' => 'esi-assets.read_assets.v1', + '/characters/{character_id}/assets/locations/' => 'esi-assets.read_assets.v1', + '/corporations/{corporation_id}/assets/names/' => 'esi-assets.read_corporation_assets.v1', + '/corporations/{corporation_id}/assets/locations/' => 'esi-assets.read_corporation_assets.v1', + '/characters/affiliation/' => 'public', + '/characters/{character_id}/cspa/' => 'esi-characters.read_contacts.v1', + '/characters/{character_id}/contacts/' => 'esi-characters.write_contacts.v1', + '/characters/{character_id}/fittings/' => 'esi-fittings.write_fittings.v1', + '/fleets/{fleet_id}/members/' => 'esi-fleets.write_fleet.v1', + '/fleets/{fleet_id}/wings/' => 'esi-fleets.write_fleet.v1', + '/fleets/{fleet_id}/wings/{wing_id}/squads/' => 'esi-fleets.write_fleet.v1', + '/characters/{character_id}/mail/' => 'esi-mail.send_mail.v1', + '/characters/{character_id}/mail/labels/' => 'esi-mail.organize_mail.v1', + '/universe/names/' => 'public', + '/universe/ids/' => 'public', + '/ui/openwindow/marketdetails/' => 'esi-ui.open_window.v1', + '/ui/openwindow/contract/' => 'esi-ui.open_window.v1', + '/ui/openwindow/information/' => 'esi-ui.open_window.v1', + '/ui/autopilot/waypoint/' => 'esi-ui.write_waypoint.v1', + '/ui/openwindow/newmail/' => 'esi-ui.open_window.v1', + ], + 'put' => [ + '/characters/{character_id}/calendar/{event_id}/' => 'esi-calendar.respond_calendar_events.v1', + '/characters/{character_id}/contacts/' => 'esi-characters.write_contacts.v1', + '/corporations/{corporation_id}/structures/{structure_id}/' => 'esi-corporations.write_structures.v1', + '/fleets/{fleet_id}/' => 'esi-fleets.write_fleet.v1', + '/fleets/{fleet_id}/members/{member_id}/' => 'esi-fleets.write_fleet.v1', + '/fleets/{fleet_id}/wings/{wing_id}/' => 'esi-fleets.write_fleet.v1', + '/fleets/{fleet_id}/squads/{squad_id}/' => 'esi-fleets.write_fleet.v1', + '/characters/{character_id}/mail/{mail_id}/' => 'esi-mail.organize_mail.v1', + ], + 'delete' => [ + '/characters/{character_id}/contacts/' => 'esi-characters.write_contacts.v1', + '/characters/{character_id}/fittings/{fitting_id}/' => 'esi-fittings.write_fittings.v1', + '/fleets/{fleet_id}/members/{member_id}/' => 'esi-fleets.write_fleet.v1', + '/fleets/{fleet_id}/wings/{wing_id}/' => 'esi-fleets.write_fleet.v1', + '/fleets/{fleet_id}/squads/{squad_id}/' => 'esi-fleets.write_fleet.v1', + '/characters/{character_id}/mail/labels/{label_id}/' => 'esi-mail.organize_mail.v1', + '/characters/{character_id}/mail/{mail_id}/' => 'esi-mail.organize_mail.v1', + ], + 'patch' => [ + ], + ]; + + /** + * @param string $method + * @param string $uri + * @param array $scopes + * + * @return bool|mixed + * @throws \Seat\Eseye\Exceptions\InvalidContainerDataException + */ + public function can(string $method, string $uri, array $scopes): bool + { + + if (! array_key_exists($uri, $this->scope_map[$method])) { + + Configuration::getInstance()->getLogger() + ->warning('An unknown URI was called. Allowing ' . $uri); + + return true; + } + + $required_scope = $this->scope_map[$method][$uri]; + + // Public scopes require no authentication! + if ($required_scope == 'public') + return true; + + if (! in_array($required_scope, $scopes)) + return false; + + return true; + } +} diff --git a/vendor/eveseat/eseye/src/Cache/CacheInterface.php b/vendor/eveseat/eseye/src/Cache/CacheInterface.php new file mode 100644 index 000000000..fc1cf0e8f --- /dev/null +++ b/vendor/eveseat/eseye/src/Cache/CacheInterface.php @@ -0,0 +1,65 @@ +cache_path = Configuration::getInstance() + ->file_cache_location; + + // Ensure the cache directory is OK + $this->checkCacheDirectory(); + } + + /** + * @return bool + * @throws \Seat\Eseye\Exceptions\CachePathException + */ + public function checkCacheDirectory() + { + + // Ensure the cache path exists + if (! is_dir($this->cache_path) && + ! @mkdir($this->cache_path, 0775, true) + ) { + throw new CachePathException( + 'Unable to create cache directory ' . $this->cache_path); + } + + // Ensure the cache directory is readable/writable + if (! is_readable($this->cache_path) || + ! is_writable($this->cache_path) + ) { + + if (! chmod($this->getCachePath(), 0775)) + throw new CachePathException( + $this->cache_path . ' must be readable and writable'); + } + + return true; + } + + /** + * @return string + */ + public function getCachePath(): string + { + + return $this->cache_path; + } + + /** + * @param string $uri + * @param string $query + * @param \Seat\Eseye\Containers\EsiResponse $data + * + * @return mixed|void + */ + public function set(string $uri, string $query, EsiResponse $data) + { + + $path = $this->buildRelativePath($this->safePath($uri), $query); + + // Create the subpath if that does not already exist + if (! file_exists($path)) + @mkdir($path, 0775, true); + + // Dump the contents to file + file_put_contents($path . $this->results_filename, serialize($data)); + } + + /** + * @param string $path + * @param string $query + * + * @return string + */ + public function buildRelativePath(string $path, string $query = ''): string + { + + // If the query string has data, hash it. + if ($query != '') + $query = $this->hashString($query); + + return rtrim(rtrim($this->cache_path, '/') . rtrim($path), '/') . + '/' . $query . '/'; + } + + /** + * @param string $uri + * + * @return string + */ + public function safePath(string $uri): string + { + + return preg_replace('/[^A-Za-z0-9\/]/', '', $uri); + } + + /** + * @param string $uri + * @param string $query + * + * @return bool|mixed + */ + public function has(string $uri, string $query = ''): bool + { + + if ($status = $this->get($uri, $query)) + return true; + + return false; + } + + /** + * @param string $uri + * @param string $query + * + * @return \Seat\Eseye\Containers\EsiResponse|bool + */ + public function get(string $uri, string $query = '') + { + + $path = $this->buildRelativePath($this->safePath($uri), $query); + $cache_file_path = $path . $this->results_filename; + + // If we cant read from the cache, then just return false. + if (! is_readable($cache_file_path)) + return false; + + // Get the data from the file and unserialize it + $data = unserialize(file_get_contents($cache_file_path)); + + // If the cached entry is expired and does not have any ETag, remove it. + if ($data->expired() && ! $data->hasHeader('ETag')) { + + $this->forget($uri, $query); + + return false; + } + + return $data; + } + + /** + * @param string $uri + * @param string $query + * + * @return void + */ + public function forget(string $uri, string $query = '') + { + + $path = $this->buildRelativePath($uri, $query); + $cache_file_path = $path . $this->results_filename; + + @unlink($cache_file_path); + } +} diff --git a/vendor/eveseat/eseye/src/Cache/HashesStrings.php b/vendor/eveseat/eseye/src/Cache/HashesStrings.php new file mode 100644 index 000000000..cda3f7df7 --- /dev/null +++ b/vendor/eveseat/eseye/src/Cache/HashesStrings.php @@ -0,0 +1,41 @@ +memcached = $instance; + + $this->is_memcached = class_exists('Memcached', false); + + $configuration = Configuration::getInstance(); + $this->prefix = $configuration->memcached_cache_prefix; + + if (is_null($this->memcached)) { + if ($this->is_memcached) + $this->memcached = new \Memcached(); + else + $this->memcached = new \Memcache(); + + $this->memcached->addServer($configuration->memcached_cache_host, $configuration->memcached_cache_port, 0); + + if ($this->is_memcached) + $this->memcached->setOption(\Memcached::OPT_COMPRESSION, ($configuration->memcached_cache_compressed)); + else + $this->flags = ($configuration->memcached_cache_compressed) ? MEMCACHE_COMPRESSED : 0; + } + + } + + /** + * @param string $uri + * @param string $query + * @param \Seat\Eseye\Containers\EsiResponse $data + * + * @return void + */ + public function set(string $uri, string $query, EsiResponse $data) + { + + if ($this->is_memcached) + $this->memcached->set($this->buildCacheKey($uri, $query), serialize($data), 0); + else + $this->memcached->set($this->buildCacheKey($uri, $query), serialize($data), $this->flags, 0); + } + + /** + * @param string $uri + * @param string $query + * + * @return string + */ + public function buildCacheKey(string $uri, string $query = ''): string + { + + if ($query != '') + $query = $this->hashString($query); + + return $this->prefix . $this->hashString($uri . $query); + } + + /** + * @param string $uri + * @param string $query + * + * @return \Seat\Eseye\Containers\EsiResponse|bool + */ + public function get(string $uri, string $query = '') + { + + $value = $this->memcached->get($this->buildCacheKey($uri, $query)); + if ($value === false) + return false; + + $data = unserialize($value); + + // If the cached entry is expired and does not have any ETag, remove it. + if ($data->expired() && ! $data->hasHeader('ETag')) { + $this->forget($uri, $query); + + return false; + } + + return $data; + } + + /** + * @param string $uri + * @param string $query + * + * @return mixed + */ + public function forget(string $uri, string $query = '') + { + + return $this->memcached->delete($this->buildCacheKey($uri, $query)); + } + + /** + * @param string $uri + * @param string $query + * + * @return bool|mixed + */ + public function has(string $uri, string $query = ''): bool + { + + return $this->memcached->get($this->buildCacheKey($uri, $query)) !== false; + } +} diff --git a/vendor/eveseat/eseye/src/Cache/NullCache.php b/vendor/eveseat/eseye/src/Cache/NullCache.php new file mode 100644 index 000000000..54a2001f1 --- /dev/null +++ b/vendor/eveseat/eseye/src/Cache/NullCache.php @@ -0,0 +1,79 @@ +redis = new Client($configuration->redis_cache_location, [ + 'prefix' => $configuration->redis_cache_prefix, + ]); + + return; + } + + $this->redis = $redis; + } + + /** + * @param string $uri + * @param string $query + * @param \Seat\Eseye\Containers\EsiResponse $data + * + * @return void + */ + public function set(string $uri, string $query, EsiResponse $data) + { + + $this->redis->set($this->buildCacheKey($uri, $query), serialize($data)); + } + + /** + * @param string $uri + * @param string $query + * + * @return string + */ + public function buildCacheKey(string $uri, string $query = ''): string + { + + if ($query != '') + $query = $this->hashString($query); + + return $this->hashString($uri . $query); + } + + /** + * @param string $uri + * @param string $query + * + * @return \Seat\Eseye\Containers\EsiResponse|bool + */ + public function get(string $uri, string $query = '') + { + + if (! $this->has($uri, $query)) + return false; + + $data = unserialize($this->redis + ->get($this->buildCacheKey($uri, $query))); + + // If the cached entry is expired and does not have any ETag, remove it. + if ($data->expired() && ! $data->hasHeader('ETag')) { + + $this->forget($uri, $query); + + return false; + } + + return $data; + } + + /** + * @param string $uri + * @param string $query + * + * @return bool|mixed + */ + public function has(string $uri, string $query = ''): bool + { + + return $this->redis->exists($this->buildCacheKey($uri, $query)); + } + + /** + * @param string $uri + * @param string $query + * + * @return mixed + */ + public function forget(string $uri, string $query = '') + { + + return $this->redis->del([$this->buildCacheKey($uri, $query)]); + } +} diff --git a/vendor/eveseat/eseye/src/Checker/Claim/AzpChecker.php b/vendor/eveseat/eseye/src/Checker/Claim/AzpChecker.php new file mode 100644 index 000000000..b61899f32 --- /dev/null +++ b/vendor/eveseat/eseye/src/Checker/Claim/AzpChecker.php @@ -0,0 +1,71 @@ +client_id = $client_id; + } + + /** + * {@inheritdoc} + */ + public function checkClaim($value): void + { + if (! is_string($value)) + throw new InvalidClaimException('"azp" must be a string.', self::NAME, $value); + + if ($value !== $this->client_id) + throw new InvalidClaimException('"azp" must match the originating application.', self::NAME, $value); + } + + /** + * {@inheritdoc} + */ + public function supportedClaim(): string + { + return self::NAME; + } +} diff --git a/vendor/eveseat/eseye/src/Checker/Claim/NameChecker.php b/vendor/eveseat/eseye/src/Checker/Claim/NameChecker.php new file mode 100644 index 000000000..6d7a59ca2 --- /dev/null +++ b/vendor/eveseat/eseye/src/Checker/Claim/NameChecker.php @@ -0,0 +1,53 @@ +supported_types = $supported_types; + $this->protected_header = $protected_header; + } + + /** + * {@inheritdoc} + */ + public function checkHeader($value): void + { + if (! is_string($value)) + throw new InvalidHeaderException('"typ" must be a string.', self::HEADER_NAME, $value); + + if (! in_array($value, $this->supported_types, true)) + throw new InvalidHeaderException('Unsupported type.', self::HEADER_NAME, $value); + } + + /** + * {@inheritdoc} + */ + public function supportedHeader(): string + { + return self::HEADER_NAME; + } + + /** + * {@inheritdoc} + */ + public function protectedHeaderOnly(): bool + { + return $this->protected_header; + } +} diff --git a/vendor/eveseat/eseye/src/Configuration.php b/vendor/eveseat/eseye/src/Configuration.php new file mode 100644 index 000000000..5dc732970 --- /dev/null +++ b/vendor/eveseat/eseye/src/Configuration.php @@ -0,0 +1,153 @@ +configuration = new EsiConfiguration; + } + + /** + * @return \Seat\Eseye\Configuration + * @throws \Seat\Eseye\Exceptions\InvalidContainerDataException + */ + public static function getInstance(): self + { + + if (is_null(self::$instance)) + self::$instance = new self(); + + return self::$instance; + } + + /** + * @return \Seat\Eseye\Containers\EsiConfiguration + */ + public function getConfiguration() + { + + return $this->configuration; + } + + /** + * @param \Seat\Eseye\Containers\EsiConfiguration $configuration + * + * @throws \Seat\Eseye\Exceptions\InvalidConfigurationException + */ + public function setConfiguration(EsiConfiguration $configuration) + { + + if (! $configuration->valid()) + throw new InvalidConfigurationException( + 'The configuration is empty/invalid values'); + + $this->configuration = $configuration; + } + + /** + * @return \Seat\Eseye\Log\LogInterface + */ + public function getLogger(): LogInterface + { + + if (! $this->logger) + $this->logger = new $this->configuration->logger; + + return $this->logger; + } + + /** + * @return \Seat\Eseye\Cache\CacheInterface + */ + public function getCache(): CacheInterface + { + + if (! $this->cache) + $this->cache = new $this->configuration->cache; + + return $this->cache; + } + + /** + * Magic method to get the configuration from the configuration + * property. + * + * @param $name + * + * @return mixed + */ + public function __get(string $name) + { + + return $this->configuration->$name; + } + + /** + * @param string $name + * @param string $value + * + * @return string + */ + public function __set(string $name, string $value) + { + + return $this->configuration->$name = $value; + } +} diff --git a/vendor/eveseat/eseye/src/Containers/AbstractArrayAccess.php b/vendor/eveseat/eseye/src/Containers/AbstractArrayAccess.php new file mode 100644 index 000000000..863757c51 --- /dev/null +++ b/vendor/eveseat/eseye/src/Containers/AbstractArrayAccess.php @@ -0,0 +1,98 @@ +data); + } + + /** + * @param mixed $offset + * + * @return mixed + */ + public function offsetGet($offset) + { + + return $this->data[$offset]; + } + + /** + * @param mixed $offset + * @param mixed $value + */ + public function offsetSet($offset, $value) + { + + $this->data[$offset] = $value; + } + + /** + * @param mixed $offset + */ + public function offsetUnset($offset) + { + + unset($this->data[$offset]); + } + + /** + * @param $key + * + * @return mixed + */ + public function __get($key) + { + + return $this[$key]; + } + + /** + * @param $key + * @param $val + */ + public function __set($key, $val) + { + + $this[$key] = $val; + } +} diff --git a/vendor/eveseat/eseye/src/Containers/EsiAuthentication.php b/vendor/eveseat/eseye/src/Containers/EsiAuthentication.php new file mode 100644 index 000000000..06ebe84c9 --- /dev/null +++ b/vendor/eveseat/eseye/src/Containers/EsiAuthentication.php @@ -0,0 +1,56 @@ + null, + 'secret' => null, + 'access_token' => '_', + 'refresh_token' => null, + 'token_expires' => '1970-01-01 00:00:00', + 'scopes' => [], + ]; + + public function setRefreshToken(string $refreshToken): self + { + + $this->data['refresh_token'] = $refreshToken; + + return $this; + } +} diff --git a/vendor/eveseat/eseye/src/Containers/EsiConfiguration.php b/vendor/eveseat/eseye/src/Containers/EsiConfiguration.php new file mode 100644 index 000000000..49744702e --- /dev/null +++ b/vendor/eveseat/eseye/src/Containers/EsiConfiguration.php @@ -0,0 +1,85 @@ + 'Eseye Default Library', + + // Esi + 'datasource' => 'tranquility', + 'esi_scheme' => 'https', + 'esi_host' => 'esi.evetech.net', + 'esi_port' => 443, + + // Eve Online SSO + 'sso_scheme' => 'https', + 'sso_host' => 'login.eveonline.com', + 'sso_port' => 443, + + // Fetcher + 'fetcher' => GuzzleFetcher::class, + + // Logging + 'logger' => RotatingFileLogger::class, + 'logger_level' => 'info', + 'logfile_location' => 'logs/', + + // Rotating Logger Details + 'log_max_files' => 10, + + // Cache + 'cache' => FileCache::class, + + // File Cache + 'file_cache_location' => 'cache/', + + // Redis Cache + 'redis_cache_location' => 'tcp://127.0.0.1', + 'redis_cache_prefix' => 'eseye:', + + // Memcached Cache + 'memcached_cache_host' => '127.0.0.1', + 'memcached_cache_port' => '11211', + 'memcached_cache_prefix' => 'eseye:', + 'memcached_cache_compressed' => false, + ]; + +} diff --git a/vendor/eveseat/eseye/src/Containers/EsiResponse.php b/vendor/eveseat/eseye/src/Containers/EsiResponse.php new file mode 100644 index 000000000..8175981c7 --- /dev/null +++ b/vendor/eveseat/eseye/src/Containers/EsiResponse.php @@ -0,0 +1,294 @@ +raw = $data; + + // Normalize and parse the response headers + $this->parseHeaders($headers); + + // decode and create an object from the data + $data = json_decode($data); + + // Ensure that the value for 'expires' is longer than + // 2 characters. The shortest expected value is 'now'. If it + // is not longer than 2 characters it might be empty. + $this->expires_at = strlen($expires) > 2 ? $expires : 'now'; + $this->response_code = $response_code; + + if (is_object($data)) { + + // If there is an error, set that. + if (property_exists($data, 'error')) + $this->error_message = $data->error; + + // If there is an error description, set that. + if (property_exists($data, 'error_description')) + $this->error_message .= ': ' . $data->error_description; + } + + // Run the parent constructor + parent::__construct(is_array($data) ? (array) $data : (object) $data, ArrayObject::ARRAY_AS_PROPS); + } + + /** + * Parse an array of header key value pairs. + * + * Interesting header values such as X-Esi-Error-Limit-Remain + * and X-Pages are automatically mapped to properties in this + * object. + * + * @param array $headers + */ + private function parseHeaders(array $headers) + { + + // Set the raw headers as we got from the constructor. + $this->raw_headers = $headers; + + // flatten the headers array so that values are not arrays themselves + // but rather simple key value pairs. + $headers = array_map(function ($value) { + + if (! is_array($value)) + return $value; + + return implode(';', $value); + }, $headers); + + // Set the parsed headers. + $this->headers = $headers; + + // Check for some header values that might be interesting + // such as the current error limit and number of pages + // available. + $this->hasHeader('X-Esi-Error-Limit-Remain') ? + $this->error_limit = (int) $this->getHeader('X-Esi-Error-Limit-Remain') : null; + + $this->hasHeader('X-Pages') ? $this->pages = (int) $this->getHeader('X-Pages') : null; + } + + /** + * A helper method when a key might not exist within the + * response object. + * + * @param string $index + * + * @return mixed + */ + public function optional(string $index) + { + + if (! $this->offsetExists($index)) + return null; + + return $this->$index; + } + + /** + * Determine if this containers data should be considered + * expired. + * + * Expiry is calculated by taking the expiry time and comparing + * that to the local time. Before comparison though, the local + * time is converted to the timezone in which the expiry time + * is recorded. The resultant local time is then checked to + * ensure that the expiry is not less than local time. + * + * @return bool + */ + public function expired(): bool + { + + if ($this->expires()->lte( + carbon()->now($this->expires()->timezoneName)) + ) + return true; + + return false; + } + + /** + * @return \Carbon\Carbon + */ + public function expires(): Carbon + { + + return carbon($this->expires_at); + } + + /** + * @return null|string + */ + public function error() + { + + return $this->error_message; + } + + /** + * @return int + */ + public function getErrorCode(): int + { + + return $this->response_code; + } + + /** + * @return bool + */ + public function setIsCachedLoad(): bool + { + + return $this->cached_load = true; + } + + /** + * @return bool + */ + public function isCachedLoad(): bool + { + + return $this->cached_load; + } + + /** + * @param string $name + * @return bool + */ + public function hasHeader(string $name) + { + // turn headers into case insensitive array + $key_map = array_change_key_case($this->headers, CASE_LOWER); + + // track for the requested header name + return array_key_exists(strtolower($name), $key_map); + } + + /** + * @param string $name + * @return mixed|null + */ + public function getHeader(string $name) + { + // turn header name into case insensitive + $insensitive_key = strtolower($name); + + // turn headers into case insensitive array + $key_map = array_change_key_case($this->headers, CASE_LOWER); + + // track for the requested header name and return its value if exists + if (array_key_exists($insensitive_key, $key_map)) + return $key_map[$insensitive_key]; + + return null; + } + + /** + * @param \Carbon\Carbon $date + */ + public function setExpires(Carbon $date) + { + // turn headers into case insensitive array + $key_map = array_change_key_case($this->headers, CASE_LOWER); + + // update expires header with provided date + $key_map['expires'] = $date->toRfc7231String(); + $this->expires_at = strlen($key_map['expires']) > 2 ? $key_map['expires'] : 'now'; + $this->headers = $key_map; + } +} diff --git a/vendor/eveseat/eseye/src/Eseye.php b/vendor/eveseat/eseye/src/Eseye.php new file mode 100644 index 000000000..21cc9edda --- /dev/null +++ b/vendor/eveseat/eseye/src/Eseye.php @@ -0,0 +1,544 @@ +authentication = $authentication; + + // Setup the logger + $this->logger = $this->getLogger(); + + return $this; + } + + /** + * @return \Seat\Eseye\Log\LogInterface + * @throws \Seat\Eseye\Exceptions\InvalidContainerDataException + */ + public function getLogger(): LogInterface + { + + return $this->getConfiguration()->getLogger(); + } + + /** + * @return \Seat\Eseye\Configuration + * @throws \Seat\Eseye\Exceptions\InvalidContainerDataException + */ + public function getConfiguration(): Configuration + { + + return Configuration::getInstance(); + } + + /** + * @return \Seat\Eseye\Containers\EsiAuthentication + * @throws \Seat\Eseye\Exceptions\InvalidAuthenticationException + */ + public function getAuthentication(): EsiAuthentication + { + + if (is_null($this->authentication)) + throw new InvalidAuthenticationException('Authentication data not set.'); + + return $this->authentication; + } + + /** + * @param \Seat\Eseye\Containers\EsiAuthentication $authentication + * + * @return \Seat\Eseye\Eseye + * @throws \Seat\Eseye\Exceptions\InvalidContainerDataException + */ + public function setAuthentication(EsiAuthentication $authentication): self + { + + if (! $authentication->valid()) + throw new InvalidContainerDataException('Authentication data invalid/empty'); + + $this->authentication = $authentication; + + return $this; + } + + /** + * @return bool + */ + public function isAuthenticated(): bool + { + return ! is_null($this->authentication); + } + + /** + * @param string $refreshToken + * + * @return \Seat\Eseye\Eseye + */ + public function setRefreshToken(string $refreshToken): self + { + + $this->authentication = $this->authentication->setRefreshToken($refreshToken); + + return $this; + } + + /** + * @param \Seat\Eseye\Fetchers\FetcherInterface $fetcher + */ + public function setFetcher(FetcherInterface $fetcher) + { + + $this->fetcher = $fetcher; + } + + /** + * @param array $body + * + * @return \Seat\Eseye\Eseye + */ + public function setBody(array $body): self + { + + $this->request_body = $body; + + return $this; + } + + /** + * @param string $method + * @param string $uri + * @param array $uri_data + * + * @return \Seat\Eseye\Containers\EsiResponse + * @throws \Seat\Eseye\Exceptions\EsiScopeAccessDeniedException + * @throws \Seat\Eseye\Exceptions\RequestFailedException + * @throws \Seat\Eseye\Exceptions\InvalidAuthenticationException + * @throws \Seat\Eseye\Exceptions\InvalidContainerDataException + * @throws \Seat\Eseye\Exceptions\UriDataMissingException + */ + public function invoke(string $method, string $uri, array $uri_data = []): EsiResponse + { + + // Check the Access Requirement + if (! $this->getAccessChecker()->can( + $method, $uri, $this->getFetcher()->getAuthenticationScopes()) + ) { + + // Build the uri so that there is context around what is denied. + $uri = $this->buildDataUri($uri, $uri_data); + + // Log the deny. + $this->logger->warning('Access denied to ' . $uri . ' due to ' . + 'missing scopes.'); + + throw new EsiScopeAccessDeniedException('Access denied to ' . $uri); + } + + // Build the URI from the parts we have. + $uri = $this->buildDataUri($uri, $uri_data); + + // Check if there is a cached response we can return + if (in_array(strtolower($method), $this->cachable_verb) && + $cached = $this->getCache()->get($uri->getPath(), $uri->getQuery()) + ) { + + // In case the cached entry is still valid, mark content as being loaded from cache. + if (! $cached->expired()) + $cached->setIsCachedLoad(); + + // Handling ETag marked response specifically (ignoring the expired time) + // Sending a request with the stored ETag in header - if we have a 304 response, data has not been altered. + if ($cached->hasHeader('ETag') && $cached->expired()) { + + $result = $this->rawFetch($method, $uri, $this->getBody(), ['If-None-Match' => $cached->getHeader('ETag')]); + + if ($result->getErrorCode() == 304) { + + // update expires header with newly provided value + $cached->setExpires($result->expires()); + + // store updated response in cache to renew internal cache duration + $this->getCache()->set($uri->getPath(), $uri->getQuery(), $cached); + + $cached->setIsCachedLoad(); + } + } + + // In case the result is effectively retrieved from cache, + // return the cached element. + if ($cached->isCachedLoad()) { + + // Perform some debug logging + $logging_msg = 'Loaded cached response for ' . $method . ' -> ' . $uri; + + if ($cached->hasHeader('ETag')) + $logging_msg = sprintf('%s [%s]', $logging_msg, $cached->getHeader('ETag')); + + $this->getLogger()->debug($logging_msg); + + return $cached; + } + } + + // Call ESI itself and get the EsiResponse in case it has not already been handled with cache control + if (! isset($result)) + $result = $this->rawFetch($method, $uri, $this->getBody()); + + // Cache the response if it was a get and is not already expired + if (in_array(strtolower($method), $this->cachable_verb) && ! $result->expired()) + $this->getCache()->set($uri->getPath(), $uri->getQuery(), $result); + + // In preparation for the next request, perform some + // self cleanups of this objects request data such as + // query string parameters and post bodies. + $this->cleanupRequestData(); + + return $result; + } + + /** + * @return \Seat\Eseye\Access\CheckAccess + */ + public function getAccessChecker() + { + + if (! $this->access_checker) + $this->access_checker = new CheckAccess; + + return $this->access_checker; + } + + /** + * @param \Seat\Eseye\Access\AccessInterface $checker + * + * @return \Seat\Eseye\Eseye + */ + public function setAccessChecker(AccessInterface $checker): self + { + + $this->access_checker = $checker; + + return $this; + } + + /** + * @return \Seat\Eseye\Fetchers\FetcherInterface + * @throws \Seat\Eseye\Exceptions\InvalidContainerDataException + */ + private function getFetcher(): FetcherInterface + { + + if (! $this->fetcher) { + + $fetcher_class = $this->getConfiguration()->fetcher; + $this->fetcher = new $fetcher_class(...[$this->authentication]); + + } + + return $this->fetcher; + } + + /** + * @param string $uri + * @param array $data + * + * @return \Psr\Http\Message\UriInterface + * @throws \Seat\Eseye\Exceptions\UriDataMissingException + * @throws \Seat\Eseye\Exceptions\InvalidContainerDataException + */ + public function buildDataUri(string $uri, array $data): Uri + { + + // Create a query string for the URI. We automatically + // include the datasource value from the configuration. + $query_params = array_merge([ + 'datasource' => $this->getConfiguration()->datasource, + ], $this->getQueryString()); + + return Uri::fromParts([ + 'scheme' => $this->getConfiguration()->esi_scheme, + 'host' => $this->getConfiguration()->esi_host, + 'port' => $this->getConfiguration()->esi_port, + 'path' => rtrim($this->getVersion(), '/') . + $this->mapDataToUri($uri, $data), + 'query' => http_build_query($query_params), + ]); + } + + /** + * @return array + */ + public function getQueryString(): array + { + + return $this->query_string; + } + + /** + * @param array $query + * + * @return \Seat\Eseye\Eseye + */ + public function setQueryString(array $query): self + { + + foreach ($query as $key => $value) { + if (is_array($value)) { + $query[$key] = implode(',', $value); + } + } + + $this->query_string = array_merge($this->query_string, $query); + + return $this; + } + + /** + * Get the versioned baseURI to use. + * + * @return string + */ + public function getVersion(): string + { + + return $this->version; + } + + /** + * Set the version of the API endpoints base URI. + * + * @param string $version + * + * @return \Seat\Eseye\Eseye + */ + public function setVersion(string $version) + { + + if (substr($version, 0, 1) !== '/') + $version = '/' . $version; + + $this->version = $version; + + return $this; + } + + /** + * @param string $uri + * @param array $data + * + * @return string + * @throws \Seat\Eseye\Exceptions\UriDataMissingException + */ + private function mapDataToUri(string $uri, array $data): string + { + + // Extract fields in curly braces. If there are fields, + // replace the data with those in the URI + if (preg_match_all('/{+(.*?)}/', $uri, $matches)) { + + if (empty($data)) + throw new UriDataMissingException( + 'The data array for the uri ' . $uri . ' is empty. Please provide data to use.'); + + foreach ($matches[1] as $match) { + + if (! array_key_exists($match, $data)) + throw new UriDataMissingException( + 'Data for ' . $match . ' is missing. Please provide this by setting a value ' . + 'for ' . $match . '.'); + + $uri = str_replace('{' . $match . '}', $data[$match], $uri); + } + } + + return $uri; + } + + /** + * @return \Seat\Eseye\Cache\CacheInterface + * @throws \Seat\Eseye\Exceptions\InvalidContainerDataException + */ + private function getCache(): CacheInterface + { + + return $this->getConfiguration()->getCache(); + } + + /** + * @param string $method + * @param string $uri + * @param array $body + * @param array $headers + * + * @return \Seat\Eseye\Containers\EsiResponse + * @throws \Seat\Eseye\Exceptions\InvalidAuthenticationException + * @throws \Seat\Eseye\Exceptions\RequestFailedException + * @throws \Seat\Eseye\Exceptions\InvalidContainerDataException + */ + public function rawFetch(string $method, string $uri, array $body, array $headers = []) + { + + return $this->getFetcher()->call($method, $uri, $body, $headers); + } + + /** + * @return array + */ + public function getBody(): array + { + + return $this->request_body; + } + + /** + * @return \Seat\Eseye\Eseye + */ + public function cleanupRequestData(): self + { + + $this->unsetBody(); + $this->unsetQueryString(); + + return $this; + } + + /** + * @return \Seat\Eseye\Eseye + */ + public function unsetBody(): self + { + + $this->request_body = []; + + return $this; + } + + /** + * @return \Seat\Eseye\Eseye + */ + public function unsetQueryString(): self + { + + $this->query_string = []; + + return $this; + } + + /** + * A helper method to specify the page to retrieve. + * + * @param int $page + * + * @return \Seat\Eseye\Eseye + */ + public function page(int $page): self + { + + $this->setQueryString(['page' => $page]); + + return $this; + } +} diff --git a/vendor/eveseat/eseye/src/Exceptions/CachePathException.php b/vendor/eveseat/eseye/src/Exceptions/CachePathException.php new file mode 100644 index 000000000..bb8753f25 --- /dev/null +++ b/vendor/eveseat/eseye/src/Exceptions/CachePathException.php @@ -0,0 +1,34 @@ +esi_response = $esi_response; + $this->original_exception = $exception; + + // Finish constructing the exception + parent::__construct( + $this->getError(), + $this->getEsiResponse()->getErrorCode(), + $exception->getPrevious() + ); + } + + /** + * @return null|string + */ + public function getError() + { + + return $this->getEsiResponse()->error(); + } + + /** + * @return \Seat\Eseye\Containers\EsiResponse + */ + public function getEsiResponse(): EsiResponse + { + + return $this->esi_response; + } + + /** + * @return \Exception + */ + public function getOriginalException(): Exception + { + + return $this->original_exception; + } +} diff --git a/vendor/eveseat/eseye/src/Exceptions/UriDataMissingException.php b/vendor/eveseat/eseye/src/Exceptions/UriDataMissingException.php new file mode 100644 index 000000000..ed04007f0 --- /dev/null +++ b/vendor/eveseat/eseye/src/Exceptions/UriDataMissingException.php @@ -0,0 +1,34 @@ +authentication = $authentication; + + // Setup the logger + $this->logger = Configuration::getInstance()->getLogger(); + $this->sso_base = sprintf('%s://%s:%d/v2/oauth', + Configuration::getInstance()->sso_scheme, + Configuration::getInstance()->sso_host, + Configuration::getInstance()->sso_port); + } + + /** + * @param string $method + * @param string $uri + * @param array $body + * @param array $headers + * + * @return \Seat\Eseye\Containers\EsiResponse + * @throws \Seat\Eseye\Exceptions\InvalidAuthenticationException + * @throws \Seat\Eseye\Exceptions\RequestFailedException + * @throws \Seat\Eseye\Exceptions\InvalidContainerDataException + */ + public function call( + string $method, string $uri, array $body, array $headers = []): EsiResponse + { + + // If we have authentication data, add the + // Authorization header. + if ($this->getAuthentication()) + $headers = array_merge($headers, [ + 'Authorization' => 'Bearer ' . $this->getToken(), + ]); + + return $this->httpRequest($method, $uri, $headers, $body); + } + + /** + * @return \Seat\Eseye\Containers\EsiAuthentication|null + */ + public function getAuthentication() + { + + return $this->authentication; + } + + /** + * @param \Seat\Eseye\Containers\EsiAuthentication $authentication + * + * @throws \Seat\Eseye\Exceptions\InvalidAuthenticationException + */ + public function setAuthentication(EsiAuthentication $authentication) + { + + if (! $authentication->valid()) + throw new InvalidAuthenticationException('Authentication data invalid/empty'); + + $this->authentication = $authentication; + } + + /** + * @return string + * @throws \Seat\Eseye\Exceptions\InvalidAuthenticationException + * @throws \Seat\Eseye\Exceptions\RequestFailedException + * @throws \Seat\Eseye\Exceptions\InvalidContainerDataException + */ + private function getToken(): string + { + + // Ensure that we have authentication data before we try + // and get a token. + if (! $this->getAuthentication()) + throw new InvalidAuthenticationException( + 'Trying to get a token without authentication data.'); + + // Check the expiry date. + $expires = carbon($this->getAuthentication()->token_expires); + + // If the token expires in the next minute, refresh it. + if ($expires->lte(carbon('now')->addMinute())) + $this->refreshToken(); + + return $this->getAuthentication()->access_token; + } + + /** + * Refresh the Access token that we have in the EsiAccess container. + * + * @throws \Seat\Eseye\Exceptions\RequestFailedException + * @throws \Seat\Eseye\Exceptions\InvalidAuthenticationException + * @throws \Seat\Eseye\Exceptions\InvalidContainerDataException + */ + private function refreshToken() + { + + // Make the post request for a new access_token + try { + + $response = $this->getClient()->post($this->sso_base . '/token', + [ + 'form_params' => [ + 'grant_type' => 'refresh_token', + 'refresh_token' => $this->authentication->refresh_token, + ], + 'headers' => [ + 'Authorization' => 'Basic ' . base64_encode( + $this->authentication->client_id . ':' . $this->authentication->secret), + 'User-Agent' => 'Eseye/' . Eseye::VERSION . '/' . + Configuration::getInstance()->http_user_agent, + ], + ] + ); + + } catch (ClientException | ServerException $e) { + + // Log the event as failed + $this->logger->error('[http ' . $e->getResponse()->getStatusCode() . ', ' . + strtolower($e->getResponse()->getReasonPhrase()) . '] ' . + 'get -> ' . $this->sso_base . '/token' + ); + + // Grab the body from the StreamInterface intance. + $responseBody = $e->getResponse()->getBody()->getContents(); + + // For debugging purposes, log the response body + $this->logger->debug('Request for get -> ' . $this->sso_base . '/token failed. Response body was: ' . + $responseBody); + + // Raise the exception that should be handled by the caller + throw new RequestFailedException($e, $this->makeEsiResponse( + $responseBody, + $e->getResponse()->getHeaders(), + 'now', + $e->getResponse()->getStatusCode()) + ); + } + + $response = json_decode($response->getBody()->getContents()); + + // Get the current EsiAuth container + $authentication = $this->getAuthentication(); + + $jws_token = $this->verifyToken($response->access_token); + + $this->logger->debug(json_encode($jws_token)); + + // Set the new authentication values from the request + $authentication->access_token = $response->access_token; + $authentication->refresh_token = $response->refresh_token; + $authentication->token_expires = $jws_token['exp']; + + // ... and update the container + $this->setAuthentication($authentication); + } + + /** + * @param string $method + * @param string $uri + * @param array $headers + * @param array $body + * + * @return mixed|\Seat\Eseye\Containers\EsiResponse + * @throws \Seat\Eseye\Exceptions\RequestFailedException + * @throws \Seat\Eseye\Exceptions\InvalidContainerDataException + */ + public function httpRequest( + string $method, string $uri, array $headers = [], array $body = []): EsiResponse + { + + // Include some basic headers to those already passed in. Everything + // is considered to be json. + $headers = array_merge($headers, [ + 'Accept' => 'application/json', + 'Content-Type' => 'application/json', + ]); + + // Add some debug logging and start measuring how long the request took. + $this->logger->debug('Making ' . $method . ' request to ' . $uri); + $start = microtime(true); + + // Json encode the body if it has data, else just null it + if (count($body) > 0) + $body = json_encode($body); + else + $body = null; + + try { + + // Make the _actual_ request to ESI + $response = $this->getClient()->send( + new Request($method, $uri, $headers, $body)); + + } catch (ClientException | ServerException $e) { + + // Log the event as failed + $this->logger->error('[http ' . $e->getResponse()->getStatusCode() . ', ' . + strtolower($e->getResponse()->getReasonPhrase()) . '] ' . + $method . ' -> ' . $this->stripRefreshTokenValue($uri) . ' [t/e: ' . + number_format(microtime(true) - $start, 2) . 's/' . + implode(' ', $e->getResponse()->getHeader('X-Esi-Error-Limit-Remain')) . ']' + ); + + // Grab the body from the StreamInterface intance. + $responseBody = $e->getResponse()->getBody()->getContents(); + + // For debugging purposes, log the response body + $this->logger->debug('Request for ' . $method . ' -> ' . $uri . ' failed. Response body was: ' . + $responseBody); + + // Raise the exception that should be handled by the caller + throw new RequestFailedException($e, $this->makeEsiResponse( + $responseBody, + $e->getResponse()->getHeaders(), + 'now', + $e->getResponse()->getStatusCode()) + ); + } + + // Log the successful request. + $this->logger->log('[http ' . $response->getStatusCode() . ', ' . + strtolower($response->getReasonPhrase()) . '] ' . + $method . ' -> ' . $this->stripRefreshTokenValue($uri) . ' [t/e: ' . + number_format(microtime(true) - $start, 2) . 's/' . + implode(' ', $response->getHeader('X-Esi-Error-Limit-Remain')) . ']' + ); + + // Return a container response that can be parsed. + return $this->makeEsiResponse( + $response->getBody()->getContents(), + $response->getHeaders(), + $response->hasHeader('Expires') ? $response->getHeader('Expires')[0] : 'now', + $response->getStatusCode() + ); + } + + /** + * @return \GuzzleHttp\Client + * @throws \Seat\Eseye\Exceptions\InvalidContainerDataException + */ + public function getClient(): Client + { + + if (! $this->client) + $this->client = new Client([ + 'timeout' => 30, + 'headers' => [ + 'User-Agent' => 'Eseye/' . Eseye::VERSION . '/' . + Configuration::getInstance()->http_user_agent, + ], + ]); + + return $this->client; + } + + /** + * @param \GuzzleHttp\Client $client + */ + public function setClient(Client $client) + { + + $this->client = $client; + } + + /** + * @param string $uri + * + * @return string + */ + public function stripRefreshTokenValue(string $uri): string + { + + // If we have 'refresh_token' in the URI, strip it. + if (strpos($uri, 'refresh_token')) + return Uri::withoutQueryValue((new Uri($uri)), 'refresh_token') + ->__toString(); + + return $uri; + } + + /** + * @param string $body + * @param array $headers + * @param string $expires + * @param int $status_code + * + * @return \Seat\Eseye\Containers\EsiResponse + */ + public function makeEsiResponse( + string $body, array $headers, string $expires, int $status_code): EsiResponse + { + + return new EsiResponse($body, $headers, $expires, $status_code); + } + + /** + * @return array + * + * @throws \Seat\Eseye\Exceptions\InvalidAuthenticationException + * @throws \Seat\Eseye\Exceptions\InvalidContainerDataException + * @throws \Seat\Eseye\Exceptions\RequestFailedException + */ + public function getAuthenticationScopes(): array + { + + // If we don't have any authentication data, then + // only public calls can be made. + if (is_null($this->getAuthentication())) + return ['public']; + + // If there are no scopes that we know of, update them. + // There will always be at least 1 as we add the internal + // 'public' scope. + if (count($this->getAuthentication()->scopes) <= 0) + $this->setAuthenticationScopes(); + + return $this->getAuthentication()->scopes; + } + + /** + * Query the eveseat/resources repository for SDE + * related information. + * + * @throws \Seat\Eseye\Exceptions\InvalidAuthenticationException + * @throws \Seat\Eseye\Exceptions\InvalidContainerDataException + * @throws \Seat\Eseye\Exceptions\RequestFailedException + */ + public function setAuthenticationScopes() + { + + $jws_token = $this->verifyToken($this->authentication->access_token); + + $this->authentication->scopes = $jws_token['scp']; + } + + /** + * Verify that an access_token is still valid. + * + * @param string $access_token + * @return array + * @throws \Seat\Eseye\Exceptions\InvalidAuthenticationException + * @throws \Seat\Eseye\Exceptions\InvalidContainerDataException + * @throws \Seat\Eseye\Exceptions\RequestFailedException + * @throws \Exception + */ + private function verifyToken(string $access_token) + { + + $sets = $this->getJwkSets(); + + $jwk_sets = JWKSet::createFromKeyData($sets); + + $jws = Load::jws($access_token) + ->algs(['RS256', 'ES256', 'HS256']) + ->exp() + ->iss(Configuration::getInstance()->sso_host) + ->header('typ', new TypeChecker(['JWT'], true)) + ->claim('sub', new SubEveCharacterChecker()) + ->claim('azp', new AzpChecker($this->authentication->client_id)) + ->claim('name', new NameChecker()) + ->claim('owner', new OwnerChecker()) + ->keyset($jwk_sets) + ->run(); + + return $jws->claims->all(); + } + + /** + * @return array + * @throws \Seat\Eseye\Exceptions\InvalidContainerDataException + */ + private function getJwkSets(): array + { + $jwk_uri = $this->getJwkUri(); + + $response = $this->getClient()->get($jwk_uri); + + return json_decode($response->getBody(), true); + } + + /** + * @return string + * @throws \Seat\Eseye\Exceptions\InvalidContainerDataException + */ + private function getJwkUri(): string + { + $oauth_discovery = sprintf('%s://%s:%d/.well-known/oauth-authorization-server', + Configuration::getInstance()->sso_scheme, + Configuration::getInstance()->sso_host, + Configuration::getInstance()->sso_port); + + $response = $this->getClient()->get($oauth_discovery); + + $metadata = json_decode($response->getBody()); + + return $metadata->jwks_uri; + } +} diff --git a/vendor/eveseat/eseye/src/Helpers/helpers.php b/vendor/eveseat/eseye/src/Helpers/helpers.php new file mode 100644 index 000000000..d192032f2 --- /dev/null +++ b/vendor/eveseat/eseye/src/Helpers/helpers.php @@ -0,0 +1,40 @@ +logfile_location, '/') . '/eseye.log', + $configuration->logger_level + ); + $stream->setFormatter($formatter); + + $this->logger = new Logger('eseye'); + $this->logger->pushHandler($stream); + } + + /** + * @param string $message + * + * @return mixed|void + */ + public function log(string $message) + { + + $this->logger->info($message); + } + + /** + * @param string $message + * + * @return mixed|void + */ + public function debug(string $message) + { + + $this->logger->debug($message); + } + + /** + * @param string $message + * + * @return mixed|void + */ + public function warning(string $message) + { + + $this->logger->warning($message); + } + + /** + * @param string $message + * + * @return mixed|void + */ + public function error(string $message) + { + + $this->logger->error($message); + } +} diff --git a/vendor/eveseat/eseye/src/Log/LogInterface.php b/vendor/eveseat/eseye/src/Log/LogInterface.php new file mode 100644 index 000000000..b8ca8cacc --- /dev/null +++ b/vendor/eveseat/eseye/src/Log/LogInterface.php @@ -0,0 +1,58 @@ +logfile_location, '/') . '/eseye.log', + $configuration->log_max_files, + $configuration->logger_level + ); + $stream->setFormatter($formatter); + + $this->logger = new Logger('eseye'); + $this->logger->pushHandler($stream); + } + + /** + * @param string $message + * + * @return mixed|void + */ + public function log(string $message) + { + + $this->logger->info($message); + } + + /** + * @param string $message + * + * @return mixed|void + */ + public function debug(string $message) + { + + $this->logger->debug($message); + } + + /** + * @param string $message + * + * @return mixed|void + */ + public function warning(string $message) + { + + $this->logger->warning($message); + } + + /** + * @param string $message + * + * @return mixed|void + */ + public function error(string $message) + { + + $this->logger->error($message); + } +} diff --git a/vendor/eveseat/eseye/src/Traits/ConstructsContainers.php b/vendor/eveseat/eseye/src/Traits/ConstructsContainers.php new file mode 100644 index 000000000..44f3a9884 --- /dev/null +++ b/vendor/eveseat/eseye/src/Traits/ConstructsContainers.php @@ -0,0 +1,61 @@ + $value) { + + if (! array_key_exists($key, $this->data)) + throw new InvalidContainerDataException( + 'Key ' . $key . ' is not valid for this container' + ); + + $this->$key = $value; + } + } + } +} diff --git a/vendor/eveseat/eseye/src/Traits/ValidatesContainers.php b/vendor/eveseat/eseye/src/Traits/ValidatesContainers.php new file mode 100644 index 000000000..5ecc09e7f --- /dev/null +++ b/vendor/eveseat/eseye/src/Traits/ValidatesContainers.php @@ -0,0 +1,42 @@ +data, true); + } +} diff --git a/vendor/eveseat/eseye/tests/Access/CheckAccessTest.php b/vendor/eveseat/eseye/tests/Access/CheckAccessTest.php new file mode 100644 index 000000000..a004743a3 --- /dev/null +++ b/vendor/eveseat/eseye/tests/Access/CheckAccessTest.php @@ -0,0 +1,86 @@ +check_access = new CheckAccess; + } + + public function testCheckAccessObjectInstantiation() + { + + $this->assertInstanceOf(CheckAccess::class, $this->check_access); + } + + public function testCheckAccessCanShouldGrantAccess() + { + + $scopes = [ + 'esi-assets.read_assets.v1', + ]; + $result = $this->check_access->can('get', '/characters/{character_id}/assets/', $scopes); + + $this->assertTrue($result); + } + + public function testCheckAccessCanShouldDenyAccess() + { + + + $scopes = [ + 'esi-assets.read_assets.v1', + ]; + $result = $this->check_access->can('get', '/characters/{character_id}/bookmarks/', $scopes); + + $this->assertFalse($result); + } + + public function testCheckAccessCanShouldAllowPublicOnlyCall() + { + + $result = $this->check_access->can('get', '/alliances/', []); + + $this->assertTrue($result); + } + + public function testCheckAccessShouldAllowAccessToUnknownUri() + { + + // Disable logging. + Configuration::getInstance()->logger = NullLogger::class; + + $result = $this->check_access->can('get', '/invalid/uri', []); + + $this->assertTrue($result); + } + +} diff --git a/vendor/eveseat/eseye/tests/Cache/FileCacheTest.php b/vendor/eveseat/eseye/tests/Cache/FileCacheTest.php new file mode 100644 index 000000000..98a7d220d --- /dev/null +++ b/vendor/eveseat/eseye/tests/Cache/FileCacheTest.php @@ -0,0 +1,116 @@ +root = vfsStream::setup('cache'); + Configuration::getInstance()->file_cache_location = vfsStream::url('cache'); + + $this->file_cache = new FileCache; + } + + public function testFileCacheCanInstantiate() + { + + $this->assertInstanceOf(FileCache::class, new FileCache); + } + + public function testFileCacheCheckCacheDirectory() + { + + $this->assertTrue($this->file_cache->checkCacheDirectory()); + } + + public function testFileCacheBuildsRelativePathWithoutQueryString() + { + + $path = $this->file_cache->buildRelativePath('/test'); + + $this->assertEquals('vfs://cache/test//', $path); + } + + public function testFileCacheBuildsRelativePathWithQueryString() + { + + $path = $this->file_cache->buildRelativePath('/test', 'foo=bar'); + + $this->assertEquals('vfs://cache/test/2fb8f40115dd1e695cbe23d4f97ce5b1fb697eee/', $path); + } + + public function testFileCacheFailsCreatingDirectoryOnInvalidPath() + { + + $this->expectException(CachePathException::class); + + if (substr(PHP_OS, 0, 3) == 'WIN') + $invalid_path = '/completely:invalid?path'; + else + $invalid_path = '/completely/invalid/path'; + + Configuration::getInstance() + ->file_cache_location = $invalid_path; + new FileCache(); + } + + /** + * @param $input + * @param $output + * + * @dataProvider providerTestFileCacheSafePathValues + */ + public function testFileCacheSafePathValues($input, $output) + { + + $result = $this->file_cache->safePath($input); + + $this->assertEquals($output, $result); + } + + /** + * @return array + */ + public function providerTestFileCacheSafePathValues() + { + + return [ + ['A/B/C', 'A/B/C'], + ['\'A/B/C', 'A/B/C'], + ['`A/B/C`', 'A/B/C'], + ['|&*A%/$B!/C', 'A/B/C'], + ]; + } + +} diff --git a/vendor/eveseat/eseye/tests/Cache/HashesStringsTest.php b/vendor/eveseat/eseye/tests/Cache/HashesStringsTest.php new file mode 100644 index 000000000..f269e824d --- /dev/null +++ b/vendor/eveseat/eseye/tests/Cache/HashesStringsTest.php @@ -0,0 +1,43 @@ +assertEquals('a94a8fe5ccb19ba61c4c0873d391e987982fbbd3', + $this->hashString('test')); + } + + public function testHashesStringsToUnexpectedValue() + { + + $this->assertNotEquals('nope', $this->hashString('test')); + } + +} diff --git a/vendor/eveseat/eseye/tests/Cache/MemcachedCacheTest.php b/vendor/eveseat/eseye/tests/Cache/MemcachedCacheTest.php new file mode 100644 index 000000000..05a255162 --- /dev/null +++ b/vendor/eveseat/eseye/tests/Cache/MemcachedCacheTest.php @@ -0,0 +1,75 @@ +createMock(\Memcached::class); + else + $instance = $this->createMock(\Memcache::class); + + // Set the cache + $this->memcached_cache = new MemcachedCache($instance); + + $this->esi_response_object = new EsiResponse('', [], 'now', 200); + } + + public function testMemcachedCacheInstantiates() + { + + $this->assertInstanceOf(MemcachedCache::class, $this->memcached_cache); + } + + public function testMemcachedCacheBuildsCacheKey() + { + + $key = $this->memcached_cache->buildCacheKey('/test', 'foo=bar'); + $this->assertEquals('eseye:b0f071c288f528954cddef0e1aa24df41de874aa', $key); + } + + public function testMemcachedCacheSetsKey() + { + + $this->memcached_cache->set('/foo', 'foo=bar', $this->esi_response_object); + } + + public function testMemcachedCacheForgetsKey() + { + + $this->memcached_cache->forget('/foo', 'foo=bar'); + } +} diff --git a/vendor/eveseat/eseye/tests/Cache/NullCacheTest.php b/vendor/eveseat/eseye/tests/Cache/NullCacheTest.php new file mode 100644 index 000000000..a564e89fd --- /dev/null +++ b/vendor/eveseat/eseye/tests/Cache/NullCacheTest.php @@ -0,0 +1,70 @@ +null_cache = new NullCache; + } + + public function testNullCacheInstantiates() + { + + $this->assertInstanceOf(NullCache::class, $this->null_cache); + } + + public function testNullCacheSetsValue() + { + + $esi_response = $this->createMock(EsiResponse::class); + $return = $this->null_cache->set('/test', 'foo=bar', $esi_response); + + $this->assertNull($return); + } + + public function testNullCacheGetsValue() + { + + $this->assertFalse($this->null_cache->get('/test', 'foo=bar')); + } + + public function testNullCacheForgetsValues() + { + + $this->assertNull($this->null_cache->forget('/test', 'foo=bar')); + } + + public function testNullCacheHasValue() + { + + $this->assertFalse($this->null_cache->has('/test', 'foo=bar')); + } + +} diff --git a/vendor/eveseat/eseye/tests/Cache/RedisCacheTest.php b/vendor/eveseat/eseye/tests/Cache/RedisCacheTest.php new file mode 100644 index 000000000..a311f6a2a --- /dev/null +++ b/vendor/eveseat/eseye/tests/Cache/RedisCacheTest.php @@ -0,0 +1,78 @@ +createMock(Client::class); + + // Set the cache + $this->redis_cache = new RedisCache($redis); + $this->esi_response_object = new EsiResponse('', [], 'now', 200); + } + + public function testRedisCacheInstantiates() + { + + $this->assertInstanceOf(RedisCache::class, $this->redis_cache); + } + + public function testRedisCacheInstantiatesWithoutArgument() + { + + $this->assertInstanceOf(RedisCache::class, new RedisCache); + } + + public function testRedisCacheBuildsCacheKey() + { + + $key = $this->redis_cache->buildCacheKey('/test', 'foo=bar'); + $this->assertEquals('b0f071c288f528954cddef0e1aa24df41de874aa', $key); + } + + public function testRedisCacheSetsKey() + { + + $this->redis_cache->set('/foo', 'foo=bar', $this->esi_response_object); + } + + public function testRedisCacheForgetsKey() + { + + $this->redis_cache->forget('/foo', 'foo=bar'); + } + +} diff --git a/vendor/eveseat/eseye/tests/ConfigurationTest.php b/vendor/eveseat/eseye/tests/ConfigurationTest.php new file mode 100644 index 000000000..73d4e8376 --- /dev/null +++ b/vendor/eveseat/eseye/tests/ConfigurationTest.php @@ -0,0 +1,102 @@ +assertInstanceOf(Configuration::class, Configuration::getInstance()); + } + + public function testConfigurationSingleton() + { + + $instance1 = Configuration::getInstance(); + $instance2 = Configuration::getInstance(); + + $instance1->setConfiguration(new EsiConfiguration([ + 'datasource' => 'test', + ])); + + // Got a feeling this assert is wrong + $this->assertNotEquals('', $instance2->getConfiguration()->datasource); + } + + public function testConfigurationGetConfigurationValuesContainer() + { + + $this->assertInstanceOf(EsiConfiguration::class, Configuration::getInstance()->getConfiguration()); + } + + public function testConfigurationSetsNewConfigurationContainerWithValidData() + { + + $configuration = new EsiConfiguration(['http_user_agent' => 'Eseye Library']); + $this->assertInstanceOf(EsiConfiguration::class, $configuration); + } + + public function testConfigurationSetsNewConfigurationsContainerWithInvalidData() + { + + $this->expectException(InvalidContainerDataException::class); + new EsiConfiguration(['invalid' => 'invalid']); + } + + public function testConfigurationSetsNewConfigurationContainerWithNullData() + { + + $this->expectException(InvalidContainerDataException::class); + new EsiConfiguration(['value' => null]); + } + + public function testConfigurationGetsLogger() + { + + $logger = Configuration::getInstance()->getLogger(); + $this->assertInstanceOf(LogInterface::class, $logger); + } + + public function testConfigurationGetsCache() + { + + $cache = Configuration::getInstance()->getCache(); + $this->assertInstanceOf(CacheInterface::class, $cache); + } + + public function testConfigurationSetsNewValue() + { + + $configuration = Configuration::getInstance(); + $configuration->test = 'test'; + + $this->assertEquals('test', $configuration->test); + $this->assertEquals('test', $configuration->getConfiguration()->test); + } +} diff --git a/vendor/eveseat/eseye/tests/Containers/EsiAuthenticationTest.php b/vendor/eveseat/eseye/tests/Containers/EsiAuthenticationTest.php new file mode 100644 index 000000000..d38bddb05 --- /dev/null +++ b/vendor/eveseat/eseye/tests/Containers/EsiAuthenticationTest.php @@ -0,0 +1,139 @@ +esi_authentication = new EsiAuthentication; + } + + public function testEsiAuthenticationInstantiation() + { + + $this->assertInstanceOf(EsiAuthentication::class, $this->esi_authentication); + } + + public function testFreshEsiAuthenticationInstanceIsNotValid() + { + + $this->assertFalse($this->esi_authentication->valid()); + } + + public function testEsiAuthenticationCanAccessAsArrayKey() + { + + $this->assertArrayHasKey('client_id', $this->esi_authentication); + } + + public function testEsiAuthenticationCanAccessAsObjectProperty() + { + + $client_id = $this->esi_authentication->client_id; + $this->assertNull($client_id); + } + + public function testCanSetAndAccessConfigurationValueAsArrayKey() + { + + $authentication = new EsiAuthentication; + $authentication['test'] = 'test'; + + $this->assertEquals('test', $authentication['test']); + } + + public function testCanSetAndAccessConfigurationValueAsObjectProperty() + { + + $authentication = new EsiAuthentication; + $authentication->test = 'test'; + + $this->assertEquals('test', $authentication->test); + } + + public function testEsiAuthenticationContainerConstructWithValuePasses() + { + + $authentication = new EsiAuthentication([ + 'client_id' => '123', + ]); + + $this->assertInstanceOf(EsiAuthentication::class, $authentication); + } + + public function testEsiAuthenticationContainerConstructWithUnknownKeyFails() + { + + $this->expectException(InvalidContainerDataException::class); + + new EsiAuthentication([ + 'foo' => 'bar', + ]); + } + + /** + * @param $key The key to check for existence + * + * @dataProvider providerTestRequiredKeysExists + */ + public function testRequiredKeysExists($key) + { + + $authentication = new EsiAuthentication; + $this->assertArrayHasKey($key, $authentication); + } + + /** + * Keys that _should_ exists in a new Configuration instance + * + * @return array + */ + public function providerTestRequiredKeysExists() + { + + return [ + ['client_id'], + ['secret'], + ['access_token'], + ['refresh_token'], + ['token_expires'], + ['scopes'], + ]; + } + + public function testEsiAuthenticationContainerSetRefreshToken() + { + + $authentication = new EsiAuthentication; + $authentication->setRefreshToken('REFRESH_TOKEN'); + + $this->assertEquals('REFRESH_TOKEN', $authentication->refresh_token); + } + +} diff --git a/vendor/eveseat/eseye/tests/Containers/EsiConfigurationTest.php b/vendor/eveseat/eseye/tests/Containers/EsiConfigurationTest.php new file mode 100644 index 000000000..a6c4f9901 --- /dev/null +++ b/vendor/eveseat/eseye/tests/Containers/EsiConfigurationTest.php @@ -0,0 +1,133 @@ +esi_configuration = new EsiConfiguration; + } + + public function testEsiConfigurationInstantiation() + { + + $instance = new EsiConfiguration; + $this->assertInstanceOf(EsiConfiguration::class, $instance); + } + + public function testFreshEsiConfigurationInstanceIsValid() + { + + $this->assertTrue($this->esi_configuration->valid()); + } + + public function testEsiConfigurationCanAccessAsArrayKey() + { + + $this->assertArrayHasKey('datasource', $this->esi_configuration); + } + + public function testEsiConfigurationCanAccessAsObjectProperty() + { + + $datasource = $this->esi_configuration->datasource; + $this->assertEquals('tranquility', $datasource); + } + + public function testCanSetAndAccessConfigurationValueAsArrayKey() + { + + $configuration = new EsiConfiguration; + $configuration['test'] = 'test'; + + $this->assertEquals('test', $configuration['test']); + } + + public function testCanSetAndAccessConfigurationValueAsObjectProperty() + { + + $configuration = new EsiConfiguration; + $configuration->test = 'test'; + + $this->assertEquals('test', $configuration->test); + } + + public function testEsiConfigurationContainerConstructWithValuePasses() + { + + $configuration = new EsiConfiguration([ + 'datasource' => 'tranquility', + ]); + + $this->assertInstanceOf(EsiConfiguration::class, $configuration); + } + + public function testEsiConfigurationContainerConstructWithUnknownKeyFails() + { + + $this->expectException(InvalidContainerDataException::class); + + new EsiConfiguration([ + 'foo' => 'bar', + ]); + } + + /** + * @param $key The key to check for existence + * + * @dataProvider providerTestRequiredKeysExists + */ + public function testRequiredKeysExists($key) + { + + $configuration = new EsiConfiguration; + $this->assertArrayHasKey($key, $configuration); + } + + /** + * Keys that _should_ exists in a new Configuration instance + * + * @return array + */ + public function providerTestRequiredKeysExists() + { + + return [ + ['http_user_agent'], + ['datasource'], + ['logger'], + ['logger_level'], + ['logfile_location'], + ['cache'], + ]; + } +} diff --git a/vendor/eveseat/eseye/tests/Containers/EsiResponseTest.php b/vendor/eveseat/eseye/tests/Containers/EsiResponseTest.php new file mode 100644 index 000000000..cd9a71fe7 --- /dev/null +++ b/vendor/eveseat/eseye/tests/Containers/EsiResponseTest.php @@ -0,0 +1,241 @@ + 'Foo', + 'details' => [ + 'age' => 40, + 'human' => 'yes', + ], + ]); + + // Sample response headers + $this->headers = $headers = [ + "Access-Control-Allow-Credentials" => [ + 0 => "true", + ], + "Access-Control-Allow-Headers" => [ + 0 => "Content-Type,Authorization,X-User-Agent", + ], + "Content-Type" => [ + 0 => "application/json", + ], + "Expires" => [ + 0 => "Sat, 30 Dec 2017 09:00:32 GMT", + ], + + "Strict-Transport-Security" => [ + 0 => "max-age=31536000", + ], + "X-Esi-Error-Limit-Remain" => [ + 0 => "64", + ], + "X-Esi-Error-Limit-Reset" => [ + 0 => "52", + ], + "X-Pages" => [ + 0 => "4", + ], + "Date" => [ + 0 => "Sat, 30 Dec 2017 08:23:08 GMT", + ], + ]; + + $this->esi_response = new EsiResponse($data, $headers, 'now', 200); + } + + public function testEsiResponseInstantiation() + { + + $this->assertInstanceOf(EsiResponse::class, $this->esi_response); + } + + public function testEsiResponseTestPayloadIsExpired() + { + + $this->assertTrue($this->esi_response->expired()); + } + + public function testEsiResponseTestPayloadIsNotExpired() + { + + $data = json_encode(['foo' => 'bar']); + $esi = new EsiResponse($data, [], '3000-01-01 00:00:00', 200); + + $this->assertFalse($esi->expired()); + } + + public function testEsiResponseDoesNotHaveError() + { + + $this->assertNull($this->esi_response->error()); + } + + public function testEsiResponseDoesHaveError() + { + + $data = json_encode(['error' => 'Test Error']); + $esi = new EsiResponse($data, [], 'now', 500); + + $this->assertEquals('Test Error', $esi->error()); + } + + public function testEsiResponseDoesHaveErrorAndDescription() + { + + $data = json_encode(['error' => 'Test Error', 'error_description' => 'Test Description']); + $esi = new EsiResponse($data, [], 'now', 500); + + $this->assertEquals('Test Error: Test Description', $esi->error()); + } + + public function testEsiResponseSerializableSingleValueResponse() + { + $data = '29500.01'; + $esi = new EsiResponse($data, [], 'now', 200); + + $serialized = serialize($esi); + $esi = unserialize($serialized); + + $this->assertInstanceOf(EsiResponse::class, $esi); + } + + public function testEsiResponseSerializableObjectResponse() + { + $data = '{"foo": "bar"}'; + $esi = new EsiResponse($data, [], 'now', 200); + + $serialized = serialize($esi); + $esi = unserialize($serialized); + + $this->assertInstanceOf(EsiResponse::class, $esi); + } + + public function testEsiResponseSerializableArrayResponse() + { + $data = '[1, 2, 3, 4, 5, 6, 7, 8]'; + $esi = new EsiResponse($data, [], 'now', 200); + + $serialized = serialize($esi); + $esi = unserialize($serialized); + + $this->assertInstanceOf(EsiResponse::class, $esi); + } + + public function testEsiResponseSerializableArrayObjectResponse() + { + $data = '[{"id": 646465444, "amount": 6546546.33}, {"id": 6568794131, "amount": 464657896584.17}]'; + $esi = new EsiResponse($data, [], 'now', 200); + + $serialized = serialize($esi); + $esi = unserialize($serialized); + + $this->assertInstanceOf(EsiResponse::class, $esi); + } + + public function testEsiResponseCanGetErrorCode() + { + + $this->assertEquals(200, $this->esi_response->getErrorCode()); + } + + public function testEsiResponseCanGetDataValue() + { + + $this->assertEquals('Foo', $this->esi_response->name); + } + + public function testEsiResponseCanGetNestedDataValue() + { + + $this->assertEquals('yes', $this->esi_response->details->human); + } + + public function testEsiResponseCanGetRawDataFromContainer() + { + + $this->assertEquals('{"name":"Foo","details":{"age":40,"human":"yes"}}', + $this->esi_response->raw); + } + + public function testEsiResponseCanGetRawResponseHeaders() + { + + $this->assertEquals($this->headers, $this->esi_response->raw_headers); + } + + public function testEsiResponseCanGetParseHeaderValue() + { + + $this->assertEquals('Content-Type,Authorization,X-User-Agent', + $this->esi_response->headers['Access-Control-Allow-Headers']); + } + + public function testEsiResponseCanGetParsedPagesFromHeaders() + { + + $this->assertEquals(4, $this->esi_response->pages); + } + + public function testEsiResponseCanGetParsedErrorLomitFromHeaders() + { + + $this->assertEquals(64, $this->esi_response->error_limit); + } + + public function testEsiResponseIsNotCachedByDefault() + { + + $this->assertFalse($this->esi_response->isCachedLoad()); + } + + public function testEsiResponseMarksResponseAsCached() + { + + $this->esi_response->setIsCachedload(); + $this->assertTrue($this->esi_response->isCachedLoad()); + } + + public function testEsiResponseCanSetExpires() + { + $old_expires = $this->esi_response->expires(); + $new_expires = carbon()->addHour(); + + $this->esi_response->setExpires($new_expires); + + $this->assertNotEquals($old_expires, $this->esi_response->expires()); + $this->assertNotEquals($old_expires->toRfc7231String(), $this->esi_response->getHeader('expires')); + } +} diff --git a/vendor/eveseat/eseye/tests/EseyeTest.php b/vendor/eveseat/eseye/tests/EseyeTest.php new file mode 100644 index 000000000..e55ae2bef --- /dev/null +++ b/vendor/eveseat/eseye/tests/EseyeTest.php @@ -0,0 +1,393 @@ +logger = NullLogger::class; + + // Remove caching + $configuration->cache = NullCache::class; + + // Force ESI data-source to be singularity + $configuration->datasource = 'singularity'; + + $this->esi = new Eseye; + } + + public function testEseyeInstantiation() + { + + $this->assertInstanceOf(Eseye::class, $this->esi); + } + + public function testEseyeInstantiateWithInvalidAuthenticationData() + { + + $this->expectException(InvalidContainerDataException::class); + + $authentication = new EsiAuthentication([ + 'foo' => 'bar', + ]); + new Eseye($authentication); + } + + public function testEseyeInstantiateWithValidAuthenticationData() + { + + $authentication = new EsiAuthentication([ + 'client_id' => 'SSO_CLIENT_ID', + 'secret' => 'SSO_SECRET', + 'refresh_token' => 'CHARACTER_REFRESH_TOKEN', + ]); + new Eseye($authentication); + } + + public function testEseyeSetNewInvalidAuthenticationData() + { + + $this->expectException(InvalidContainerDataException::class); + + $authentication = new EsiAuthentication([ + 'foo' => 'bar', + 'baz' => null, + ]); + $this->esi->setAuthentication($authentication); + } + + public function testEseyeSetNewValidAuthenticationData() + { + + $authentication = new EsiAuthentication([ + 'client_id' => 'SSO_CLIENT_ID', + 'secret' => 'SSO_SECRET', + 'access_token' => 'ACCESS_TOKEN', + 'refresh_token' => 'CHARACTER_REFRESH_TOKEN', + 'token_expires' => '1970-01-01 00:00:00', + 'scopes' => ['public'], + ]); + $this->esi->setAuthentication($authentication); + } + + public function testEseyeGetAuthenticationBeforeSet() + { + + $this->expectException(InvalidAuthenticationException::class); + + $this->esi->getAuthentication(); + } + + public function testEseyeGetAuthenticationAfterSet() + { + + $authentication = new EsiAuthentication([ + 'client_id' => 'SSO_CLIENT_ID', + 'secret' => 'SSO_SECRET', + 'access_token' => 'ACCESS_TOKEN', + 'refresh_token' => 'CHARACTER_REFRESH_TOKEN', + 'token_expires' => '1970-01-01 00:00:00', + 'scopes' => ['public'], + ]); + $this->esi->setAuthentication($authentication); + + $this->assertInstanceOf(EsiAuthentication::class, $this->esi->getAuthentication()); + } + + public function testEseyeGetConfigurationInstance() + { + + $this->assertInstanceOf(Configuration::class, $this->esi->getConfiguration()); + } + + public function testEseyeGetLogger() + { + + $this->assertInstanceOf(LogInterface::class, $this->esi->getLogger()); + } + + public function testEseyeSetAccessChecker() + { + + $access = $this->createMock(CheckAccess::class); + + $this->assertInstanceOf(Eseye::class, $this->esi->setAccessChecker($access)); + } + + public function testEseyeGetAccessChecker() + { + + $this->assertInstanceOf(CheckAccess::class, $this->esi->getAccessChecker()); + } + + public function testEseyeGetsFetcher() + { + + $get_fetcher = self::getMethod('getFetcher'); + $return = $get_fetcher->invokeArgs(new Eseye, []); + + $this->assertInstanceOf(FetcherInterface::class, $return); + } + + /** + * Helper method to set private methods public. + * + * @param $name + * + * @return \ReflectionMethod + */ + protected static function getMethod($name) + { + + $class = new ReflectionClass('Seat\Eseye\Eseye'); + $method = $class->getMethod($name); + $method->setAccessible(true); + + return $method; + } + + public function testEseyeGetsCache() + { + + $get_fetcher = self::getMethod('getCache'); + $return = $get_fetcher->invokeArgs(new Eseye, []); + + $this->assertInstanceOf(CacheInterface::class, $return); + } + + public function testEseyeGetAndSetQueryString() + { + + $object = $this->esi->setQueryString([ + 'foo' => 'bar', + 'foobar' => ['foo', 'bar'], + ]); + + $this->assertInstanceOf(Eseye::class, $object); + $this->assertEquals([ + 'foo' => 'bar', + 'foobar' => 'foo,bar', + ], $this->esi->getQueryString()); + } + + public function testEseyeGetAndSetBody() + { + + $object = $this->esi->setBody(['foo']); + + $this->assertInstanceOf(Eseye::class, $object); + $this->assertEquals(['foo'], $this->esi->getBody()); + } + + public function testEseyeGetDefaultVersionString() + { + + $version = $this->esi->getVersion(); + + $this->assertEquals('/latest', $version); + } + + public function testEseyeSetIncompleteVersionStringAndGetsCompleteVersionString() + { + + $this->esi->setVersion('v1'); + + $this->assertEquals('/v1', $this->esi->getVersion()); + } + + public function testEseyeReturnsEseyeAfterSettingEsiApiVersion() + { + + $esi = $this->esi->setVersion('v4'); + + $this->assertInstanceOf(Eseye::class, $esi); + } + + public function testEseyeBuildValidDataUri() + { + + $uri = $this->esi->buildDataUri('/{foo}/', ['foo' => 'bar']); + + $this->assertEquals('https://esi.evetech.net/latest/bar/?datasource=singularity', + $uri->__toString()); + } + + public function testEseyeBuildDataUriFailsOnEmptyDataArray() + { + + $this->expectException(UriDataMissingException::class); + + $this->esi->buildDataUri('/{foo}/', []); + } + + public function testEseyeBuildDataUriFailsOnIncompleteDataArray() + { + + $this->expectException(UriDataMissingException::class); + + $this->esi->buildDataUri('/{foo}/', ['bar' => 'baz']); + } + + public function testEseyeMakesEsiApiCallWithCachedResponse() + { + + $mock = new MockHandler([ + new Response(200, ['Expires' => 'Sat, 28 Jan 4017 05:46:49 GMT'], json_encode(['foo' => 'bar'])), + ]); + + $fetcher = new GuzzleFetcher; + $fetcher->setClient(new Client([ + 'handler' => HandlerStack::create($mock), + ])); + + // Update the fetchers client + $this->esi->setFetcher($fetcher); + + $response = $this->esi->invoke('get', '/foo'); + + $this->assertEquals('bar', $response->foo); + + } + + public function testEseyeMakesEsiApiCallWithExpiredCachedResponseAndValidEtag() + { + $mock = new MockHandler([ + new Response(200, [ + 'Expires' => carbon()->addSeconds(3)->toRfc7231String(), + 'ETag' => 'W/"b3ef78b1064a27974cbf18270c1f126d519f7b467ba2e35ccb6f0819"', + ], json_encode(['foo' => 'bar'])), + new Response(304, [ + 'Expires' => carbon()->addHour()->toRfc7231String(), + 'ETag' => 'W/"b3ef78b1064a27974cbf18270c1f126d519f7b467ba2e35ccb6f0819"', + ]), + ]); + + $config = Configuration::getInstance(); + $config->cache = FileCache::class; + + $fetcher = new GuzzleFetcher; + $fetcher->setClient(new Client([ + 'handler' => HandlerStack::create($mock), + ])); + + // Update the fetchers client + $this->esi->setFetcher($fetcher); + + // send an initial call to seed cache + $response = $this->esi->invoke('get', '/foo2'); + $this->assertFalse($response->isCachedLoad()); + + sleep(5); + + // send a new call to trigger cache + $response = $this->esi->invoke('get', '/foo2'); + $this->assertTrue($response->isCachedLoad()); + } + + public function testEseyeMakesEsiApiCallWithoutCachedResponse() + { + + $mock = new MockHandler([ + new Response(200, ['Foo' => 'Bar'], json_encode(['foo' => 'bar'])), + ]); + + $fetcher = new GuzzleFetcher; + $fetcher->setClient(new Client([ + 'handler' => HandlerStack::create($mock), + ])); + + // Update the fetchers client + $this->esi->setFetcher($fetcher); + + $response = $this->esi->invoke('post', '/foo'); + + $this->assertEquals('bar', $response->foo); + + } + + public function testEseyeMakesEsiApiCallToAuthenticatedEndpointWithoutAccess() + { + + $this->expectException(EsiScopeAccessDeniedException::class); + + $mock = new MockHandler([ + new Response(401), + ]); + + // Update the fetchers client + $this->esi->setFetcher(new GuzzleFetcher(null, new Client([ + 'handler' => HandlerStack::create($mock), + ]))); + + $this->esi->invoke('get', '/characters/{character_id}/assets/', [ + 'character_id' => 123, + ]); + } + + public function testEseyeSetRefreshToken() + { + + $authentication = new EsiAuthentication([ + 'client_id' => 'SSO_CLIENT_ID', + 'secret' => 'SSO_SECRET', + 'access_token' => 'ACCESS_TOKEN', + 'refresh_token' => 'CHARACTER_REFRESH_TOKEN', + 'token_expires' => '1970-01-01 00:00:00', + 'scopes' => ['public'], + ]); + $this->esi->setAuthentication($authentication); + + $this->esi->setRefreshToken('ALTERNATE_REFRESH_TOKEN'); + + $this->assertEquals('ALTERNATE_REFRESH_TOKEN', $this->esi->getAuthentication()->refresh_token); + } + +} diff --git a/vendor/eveseat/eseye/tests/Exceptions/RequestFailedExceptionTest.php b/vendor/eveseat/eseye/tests/Exceptions/RequestFailedExceptionTest.php new file mode 100644 index 000000000..cdf3b3f12 --- /dev/null +++ b/vendor/eveseat/eseye/tests/Exceptions/RequestFailedExceptionTest.php @@ -0,0 +1,69 @@ +exception = new RequestFailedException(new Exception('Foo'), new EsiResponse( + json_encode(['error' => 'test']), + [], + 'now', + 500 + )); + } + + public function testRequestFailedGetsErrors() + { + + $error = $this->exception->getError(); + + $this->assertEquals('test', $error); + } + + public function testRequestFailedGetsEsiResponse() + { + + $response = $this->exception->getEsiResponse(); + + $this->assertInstanceOf(EsiResponse::class, $response); + } + + public function testRequestFailedGetsOriginalException() + { + + $response = $this->exception->getOriginalException(); + + $this->assertInstanceOf(Exception::class, $response); + } + +} diff --git a/vendor/eveseat/eseye/tests/Fetchers/GuzzleFetcherTest.php b/vendor/eveseat/eseye/tests/Fetchers/GuzzleFetcherTest.php new file mode 100644 index 000000000..22e340753 --- /dev/null +++ b/vendor/eveseat/eseye/tests/Fetchers/GuzzleFetcherTest.php @@ -0,0 +1,385 @@ +logger = NullLogger::class; + + $this->fetcher = new GuzzleFetcher; + } + + public function testGuzzleFetcherInstantiation() + { + + $this->assertInstanceOf(GuzzleFetcher::class, $this->fetcher); + } + + public function testGuzzleGetsClientIfNoneSet() + { + + $fetcher = new GuzzleFetcher; + $client = $fetcher->getClient(); + + $this->assertInstanceOf(Client::class, $client); + } + + public function testGuzzleFetcherStripRefreshTokenFromUrl() + { + + $url = 'https://esi.url/oauth?type=refresh_token&refresh_token=foo'; + $stripped = $this->fetcher->stripRefreshTokenValue($url); + + $this->assertEquals('https://esi.url/oauth?type=refresh_token', $stripped); + } + + public function testGuzzleFetcherStripRefreshTokenFromUrlWithoutRefreshToken() + { + + $url = 'https://esi.url/type=refresh_token'; + $stripped = $this->fetcher->stripRefreshTokenValue($url); + + $this->assertEquals('https://esi.url/type=refresh_token', $stripped); + } + + public function testGuzzleFetcherStripRefreshTokenNoTokenMention() + { + + $url = 'https://esi.url/foo=bar'; + $stripped = $this->fetcher->stripRefreshTokenValue($url); + + $this->assertEquals($url, $stripped); + } + + public function testGuzzleFetcherMakeEsiResponseContainer() + { + + $response = json_encode(['response' => 'ok']); + + $container = $this->fetcher->makeEsiResponse($response, [], 'now', 200); + + $this->assertInstanceOf(EsiResponse::class, $container); + } + + public function testGuzzleFetcherGetAuthenticationWhenNoneSet() + { + + $authentication = $this->fetcher->getAuthentication(); + + $this->assertNull($authentication); + } + + public function testGuzzleFetcherGetAuthenticationWhenSettingAuthentication() + { + + $fetcher = new GuzzleFetcher(new EsiAuthentication([ + 'client_id' => 'foo', + ])); + + $this->assertInstanceOf(EsiAuthentication::class, $fetcher->getAuthentication()); + } + + public function testGuzzleSetsAuthentication() + { + + $this->fetcher->setAuthentication(new EsiAuthentication([ + 'client_id' => 'foo', + 'secret' => 'bar', + 'access_token' => '_', + 'refresh_token' => 'baz', + 'token_expires' => '1970-01-01 00:00:00', + 'scopes' => ['public'], + ])); + + $this->assertInstanceOf(EsiAuthentication::class, $this->fetcher->getAuthentication()); + } + + public function testGuzzleFailsSettingInvalidAuthentication() + { + + $this->expectException(InvalidAuthenticationException::class); + + $this->fetcher->setAuthentication(new EsiAuthentication([ + 'client_id' => null, + ])); + } + + public function testGuzzleShouldFailGettingTokenWithoutAuthentication() + { + + $this->expectException(InvalidAuthenticationException::class); + + $get_token = self::getMethod('getToken'); + $get_token->invokeArgs(new GuzzleFetcher, []); + } + + /** + * Helper method to set private methods public. + * + * @param $name + * + * @return \ReflectionMethod + */ + protected static function getMethod($name) + { + + $class = new ReflectionClass('Seat\Eseye\Fetchers\GuzzleFetcher'); + $method = $class->getMethod($name); + $method->setAccessible(true); + + return $method; + } + + public function testGuzzleFetcherGetPublicScopeWithoutAuthentication() + { + + $scopes = $this->fetcher->getAuthenticationScopes(); + + $this->assertEquals(1, count($scopes)); + } + + public function testGuzzleCallingWithoutAuthentication() + { + + $mock = new MockHandler([ + new Response(200, ['X-Foo' => 'Bar'], json_encode(['foo' => 'var'])), + ]); + + // Update the fetchers client + $this->fetcher->setClient(new Client([ + 'handler' => HandlerStack::create($mock), + ])); + + $response = $this->fetcher->call('get', '/foo', ['foo' => 'bar']); + + $this->assertInstanceOf(EsiResponse::class, $response); + } + + public function testGuzzleCallingWithAuthentication() + { + + // init a JWK set + $jwk = $this->getJwkSet(); + + // generate a JWS Token mocking standard CCP format + $jws = $this->getJwsToken($jwk); + + $mock = new MockHandler([ + // RefreshToken response + new Response(200, ['X-Foo' => 'Bar'], json_encode([ + 'access_token' => $jws, + 'expires_in' => 1200, + 'token_type' => 'Bearer', + 'refresh_token' => 'bar', + ])), + // JWKS endpoint response + new Response(200, [], json_encode([ + 'jwks_uri' => 'https://login.eveonline.com/oauth/jwks', + ])), + // JWK Sets response + new Response(200, [], json_encode([ + 'keys' => [ + $jwk->jsonSerialize(), + ], + 'SkipUnresolvedJsonWebKeys' => true, + ])), + // ESI response + new Response(200, ['X-Foo' => 'Bar'], json_encode(['foo' => 'var'])), + ]); + + // Update the fetchers client + $this->fetcher->setClient(new Client([ + 'handler' => HandlerStack::create($mock), + ])); + + // Update the fetchers authentication + $this->fetcher->setAuthentication(new EsiAuthentication([ + 'client_id' => 'foo', + 'secret' => 'bar', + 'access_token' => '_', + 'refresh_token' => 'baz', + 'token_expires' => '1970-01-01 00:00:00', + 'scopes' => ['public'], + ])); + + $response = $this->fetcher->call('get', '/foo', ['foo' => 'bar']); + + $this->assertInstanceOf(EsiResponse::class, $response); + } + + public function testGuzzleCallingCatchesRequestAuthenticationFailure() + { + + $this->expectException(RequestFailedException::class); + + $mock = new MockHandler([ + new Response(401), + ]); + + // Update the fetchers client + $this->fetcher->setClient(new Client([ + 'handler' => HandlerStack::create($mock), + ])); + + $this->fetcher->call('get', '/foo', ['foo' => 'bar']); + } + + public function testGuzzleFetcherMakesHttpRequest() + { + + $mock = new MockHandler([ + new Response(200, ['X-Foo' => 'Bar'], json_encode(['foo' => 'var'])), + ]); + + // Update the fetchers client + $this->fetcher->setClient(new Client([ + 'handler' => HandlerStack::create($mock), + ])); + + $response = $this->fetcher->httpRequest('get', '/foo'); + + $this->assertInstanceOf(EsiResponse::class, $response); + + } + + public function testGuzzleConstructsWithClientAndGetsAuthenticationScopes() + { + + // init a JWK Set + $jwk = $this->getJwkSet(); + + // init a JWS Token + $jws = $this->getJwsToken($jwk); + + $mock = new MockHandler([ + // JWK Endpoint + new Response(200, [], json_encode([ + 'jwks_uri' => 'https://login.eveonline.com/oauth/jwks', + ])), + // JWK Sets response + new Response(200, [], json_encode([ + 'keys' => [ + $jwk->jsonSerialize(), + ], + 'SkipUnresolvedJsonWebKeys' => true, + ])), + ]); + + // Update the fetchers client + $client = new Client([ + 'handler' => HandlerStack::create($mock), + ]); + + // Update the fetchers authentication + $authentication = new EsiAuthentication([ + 'client_id' => 'foo', + 'secret' => 'bar', + 'access_token' => $jws, + 'refresh_token' => 'baz', + 'token_expires' => '1970-01-01 00:00:00', + ]); + + $fetcher = new GuzzleFetcher($authentication); + $fetcher->setClient($client); + + $scopes = $fetcher->getAuthenticationScopes(); + + $this->assertEquals(['foo', 'bar', 'baz', 'public'], $scopes); + } + + /** + * @return \Jose\Component\Core\JWK + */ + private function getJwkSet() + { + return new JWK([ + "p" => "1UQV33bi2J-WJ9529sOTuXiAGCh_lcUAgRHayLbBSElC9O_kA8g2ipC0Qu58tpKdKjq2dD7_SfbESqEI0AJD7oMfP1i-Ispn31vjIb7fmnlddF2qflc9SEkWkrZPCntusTzIraxBDUwIlmdOdAI24xHHpGe-DISE4R1LYrQS0m0", + "kty" => "RSA", + "q" => "yaEesJOxfHkeYvlo8f1NVCrCyxfzDl3_-_qDm-bpdUDjemsvolYD6AEb0GGiyjFdMJg29iCke_8nzYIuwDf2QzFS96aU0IpxLwyNsXBdOr7K53WmDj7LU4xFfR-gaOQEp-o2KZ7-1EqpPRgeI12wVpfR3Mi4TZuXlgmeyYpt_BU", + "d" => "iZo3pjr-cegcZN2lk3I3qL8By-8bSO4DaZdih6BnHB-VJbhhmSky64wP34wpKe_G486C1o9IsVZ6zuhXrOJdEOIike3d0IKg6jNY24RsV-AX5hYn44Us8ePHQnhqtxf42GujloroctLkAJAlpnYg6-rWW7kAoCjrNxVIaJ4AOabIcpIQwNwqHNjWifik6WttpC_-u-4HmTLG6f-NrVqrSJkBTReRCjsSYz1GH_snBGx2SFS5XbEfvWe0g-Asu8kZPzYL7y5ahMZ3AT4qOjNA-UFkIzYW0BQjwMwTzRq_30bTDZXBLH8YbDCD6HwBhVThmX4k6AjWr4yG7-uJfvTAgQ", + "e" => "AQAB", + "use" => "sig", + "kid" => "JWK Signature Mock", + "qi" => "baoXJXUnqalu6CMibn1aILUhfDbjW_lVVk-b9ZAK7ZVi8vEKDpvFWTeB-R-kZMaQcYZKSOZshfXSOd8p_hpCY7nL5XuQPndR2jg23vnsddppT8dwOsiOPX1gQufwrtTQX9oibcDX1P5z942esrbHW5ttEkgM4i8Xo5G7tkKed8k", + "dp" => "d2kK8jdn5rDca3Blnd9-HFA7MMukPGC02o_7t3yUlnvm0KxtOCznVQiW1g8gpz1KYLXFKSuI14oi-EJYY9eQ38BtQ5PVyjcYl_ikIWX1X1HrINe9OcZxGsNJr1YCxbS9EuIc3xlexyo2eLhZNh1zTArNhOFNiUa9_Cnh5t861rU", + "alg" => "RS256", + "dq" => "rUXJGfXSkSWE94leppcH3UziGaZ7Od2OHv0qHNBT0G_zDUEPrnI86SQKwwkk3J2PeDNXCC0FLYoYqoM1qfptp1C7_BcrzAstOUGQguwNMm7D8CUqjxNnqGTjUqPbNki9t4-O_DWmyMlgpyASxlG9OK0_rHzR5d_QZR_fVVOhMQ", + "n" => "p_iuj0pLDqQsdtXl6cJ8Gqhtm8F5dUSbmNPYoNbB_uM0oQrBBxvKPH7GzDduFMtS6LfloH3hGryTum-lxU1yQ4PjaN3IEdrGpqS6_OWtqZ6mRWrDDNdgWmkFtq5kPwfR2EXdcygWREJ7w1376WWx9l3tVu6zygfCghTTUhVT65fjmnNUR6zWJn15pxTjnQ-zphKlgvWnCDwJEW9UFXK025ztMQFn8rkTxJV9O3Qu3QS-VRjVicPhV7oOMs2YhiqUAmnzu285nKTaG6N_83NIZ5W2N06JfMt6epvTiC-st2joQp4FCsiVPEEQ6wjZJTA7cpdmIoc05X7gMKdipxeO8Q" + ]); + } + + /** + * @param \Jose\Component\Core\JWK $jwk + * @return string + */ + private function getJwsToken(JWK $jwk): string + { + $time = time(); + + return Build::jws() + ->exp($time + 1200) + ->alg('RS256') + ->payload([ + 'scp' => [ + "foo", + "bar", + "baz", + "public", + ], + "jti" => "7f64ea9f-ee6e-4c4a-9486-d668e8c79f25", + "kid" => "JWT-Signature-Key", + "sub" => "CHARACTER:EVE:90795931", + "azp" => "foo", + "name" => "Warlof Tutsimo", + "owner" => "svnSjVa1uGYyp/ZL3mfkIwkJYzQ=", + "exp" => $time + 3600, + "iss" => "login.eveonline.com", + ]) + ->header('typ', 'JWT') + ->header('kid', 'JWT-Signature-Key') + ->sign($jwk); + } +} diff --git a/vendor/eveseat/eseye/tests/Log/FileLoggerTest.php b/vendor/eveseat/eseye/tests/Log/FileLoggerTest.php new file mode 100644 index 000000000..ba0eb4414 --- /dev/null +++ b/vendor/eveseat/eseye/tests/Log/FileLoggerTest.php @@ -0,0 +1,95 @@ +root = vfsStream::setup('logs/'); + Configuration::getInstance()->logfile_location = vfsStream::url('logs/'); + + $this->logger = new FileLogger; + } + + public function testFileLoggerWritesLogInfo() + { + + $this->logger->log('foo'); + $logfile_content = $this->root->getChild('eseye.log')->getContent(); + + $this->assertContains('eseye.INFO: foo', $logfile_content); + } + + public function testFileLoggerSkipWritesLogDebugWithoutRequiredLevel() + { + + $this->logger->debug('foo'); + $logfile_content = $this->root->getChild('eseye.log'); + + $this->assertNull($logfile_content); + } + + public function testFileLoggerWritesLogDebug() + { + + Configuration::getInstance()->logger_level = 'debug'; + + // Init a new logger with the updated config + $logger = new FileLogger; + + $logger->debug('foo'); + $logfile_content = $this->root->getChild('eseye.log')->getContent(); + + $this->assertContains('eseye.DEBUG: foo', $logfile_content); + } + + public function testFileLoggerWritesLogWarning() + { + + $this->logger->warning('foo'); + $logfile_content = $this->root->getChild('eseye.log')->getContent(); + + $this->assertContains('eseye.WARNING: foo', $logfile_content); + } + + public function testFileLoggerWritesLogError() + { + + $this->logger->error('foo'); + $logfile_content = $this->root->getChild('eseye.log')->getContent(); + + $this->assertContains('eseye.ERROR: foo', $logfile_content); + } + +} diff --git a/vendor/eveseat/eseye/tests/Log/NullLoggerTest.php b/vendor/eveseat/eseye/tests/Log/NullLoggerTest.php new file mode 100644 index 000000000..bd2533b11 --- /dev/null +++ b/vendor/eveseat/eseye/tests/Log/NullLoggerTest.php @@ -0,0 +1,61 @@ +logger = new NullLogger; + } + + public function testNullLoggerIgnoresInfo() + { + + $this->assertNull($this->logger->log('foo')); + } + + public function testNullLoggerIgnoresDebug() + { + + $this->assertNull($this->logger->debug('foo')); + } + + public function testNullLoggerIgnoresWarning() + { + + $this->assertNull($this->logger->warning('foo')); + } + + public function testNullLoggerIgnoresError() + { + + $this->assertNull($this->logger->error('foo')); + } + +} diff --git a/vendor/eveseat/eseye/tests/Log/RotatingFileLoggerTest.php b/vendor/eveseat/eseye/tests/Log/RotatingFileLoggerTest.php new file mode 100644 index 000000000..f388e9c5f --- /dev/null +++ b/vendor/eveseat/eseye/tests/Log/RotatingFileLoggerTest.php @@ -0,0 +1,101 @@ +root = vfsStream::setup('logs'); + Configuration::getInstance()->logfile_location = $this->root->url(); + Configuration::getInstance()->logger_level = 'info'; + + $this->logger = new RotatingFileLogger; + + # Shitty hack to get the filename to expect. Format: eseye-2018-05-06.log + $this->logfile_name = 'eseye-' . date('Y-m-d') . '.log'; + } + + public function testFileLoggerWritesLogInfo() + { + + $this->logger->log('foo'); + $logfile_content = $this->root->getChild($this->logfile_name)->getContent(); + + $this->assertContains('eseye.INFO: foo', $logfile_content); + } + + public function testFileLoggerSkipWritesLogDebugWithoutRequiredLevel() + { + + $this->logger->debug('foo'); + $logfile_content = $this->root->getChild($this->logfile_name); + + $this->assertNull($logfile_content); + } + + public function testFileLoggerWritesLogDebug() + { + + Configuration::getInstance()->logger_level = 'debug'; + + // Init a new logger with the updated config + $logger = new RotatingFileLogger; + + $logger->debug('foo'); + $logfile_content = $this->root->getChild($this->logfile_name)->getContent(); + + $this->assertContains('eseye.DEBUG: foo', $logfile_content); + } + + public function testFileLoggerWritesLogWarning() + { + + $this->logger->warning('foo'); + $logfile_content = $this->root->getChild($this->logfile_name)->getContent(); + + $this->assertContains('eseye.WARNING: foo', $logfile_content); + } + + public function testFileLoggerWritesLogError() + { + + $this->logger->error('foo'); + $logfile_content = $this->root->getChild($this->logfile_name)->getContent(); + + $this->assertContains('eseye.ERROR: foo', $logfile_content); + } + +} diff --git a/vendor/eveseat/eseye/tools/esi.json b/vendor/eveseat/eseye/tools/esi.json new file mode 100644 index 000000000..35c369a88 --- /dev/null +++ b/vendor/eveseat/eseye/tools/esi.json @@ -0,0 +1 @@ +{"swagger":"2.0","info":{"title":"EVE Swagger Interface","description":"An OpenAPI for EVE Online","version":"0.7.3"},"host":"esi.tech.ccp.is","basePath":"/latest","schemes":["https"],"produces":["application/json"],"paths":{"/alliances/{alliance_id}/":{"get":{"description":"Public information about an alliance\n\n---\nAlternate route: `/v2/alliances/{alliance_id}/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\n[This route has an available update](https://esi.tech.ccp.is/diff/latest/dev/#GET-/alliances/{alliance_id}/)","summary":"Get alliance information","tags":["Alliance"],"parameters":[{"$ref":"#/parameters/alliance_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Public data about an alliance","examples":{"application/json":{"alliance_name":"C C P Alliance","ticker":"","executor_corp":98356193,"date_founded":"2016-06-26T21:00:00Z"}},"schema":{"type":"object","required":["alliance_name","ticker","date_founded"],"properties":{"alliance_name":{"type":"string","description":"the full name of the alliance","title":"get_alliances_alliance_id_alliance_name"},"ticker":{"type":"string","description":"the short name of the alliance","title":"get_alliances_alliance_id_ticker"},"executor_corp":{"type":"integer","format":"int32","description":"the executor corporation ID, if this alliance is not closed","title":"get_alliances_alliance_id_executor_corp"},"date_founded":{"type":"string","format":"date-time","title":"get_alliances_alliance_id_date_founded","description":"date_founded string"}},"title":"get_alliances_alliance_id_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"Alliance not found","examples":{"application/json":{"error":"Alliance not found"}},"schema":{"type":"object","description":"Alliance not found","properties":{"error":{"type":"string","description":"error message","title":"get_alliances_alliance_id_error"}},"title":"get_alliances_alliance_id_not_found"}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_alliances_alliance_id","x-cached-seconds":3600,"x-alternate-versions":["v2"]}},"/alliances/{alliance_id}/corporations/":{"get":{"description":"List all current member corporations of an alliance\n\n---\nAlternate route: `/dev/alliances/{alliance_id}/corporations/`\n\nAlternate route: `/legacy/alliances/{alliance_id}/corporations/`\n\nAlternate route: `/v1/alliances/{alliance_id}/corporations/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"List alliance's corporations","tags":["Alliance"],"parameters":[{"$ref":"#/parameters/alliance_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"List of corporation IDs","examples":{"application/json":[98000001]},"schema":{"type":"array","maxItems":1000,"items":{"type":"integer","format":"int32","minimum":0,"uniqueItems":true,"title":"get_alliances_alliance_id_corporations_200_ok","description":"200 ok integer"},"title":"get_alliances_alliance_id_corporations_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_alliances_alliance_id_corporations","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/alliances/names/":{"get":{"description":"Resolve a set of alliance IDs to alliance names\n\n---\nAlternate route: `/legacy/alliances/names/`\n\nAlternate route: `/v1/alliances/names/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\n[This route has an available update](https://esi.tech.ccp.is/diff/latest/dev/#GET-/alliances/names/)","summary":"Get alliance names","tags":["Alliance"],"parameters":[{"name":"alliance_ids","in":"query","description":"A comma separated list of alliance IDs","required":true,"type":"array","minItems":1,"maxItems":100,"items":{"type":"integer","format":"int64"}},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"List of id/name associations","examples":{"application/json":[{"alliance_id":1000171,"alliance_name":"Republic University"}]},"schema":{"type":"array","maxItems":100,"items":{"type":"object","required":["alliance_id","alliance_name"],"properties":{"alliance_id":{"type":"integer","format":"int32","title":"get_alliances_names_alliance_id","description":"alliance_id integer"},"alliance_name":{"type":"string","title":"get_alliances_names_alliance_name","description":"alliance_name string"}},"title":"get_alliances_names_200_ok","description":"200 ok object"},"title":"get_alliances_names_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_alliances_names","x-cached-seconds":3600,"x-alternate-versions":["legacy","v1"]}},"/alliances/{alliance_id}/icons/":{"get":{"description":"Get the icon urls for a alliance\n\n---\nAlternate route: `/dev/alliances/{alliance_id}/icons/`\n\nAlternate route: `/legacy/alliances/{alliance_id}/icons/`\n\nAlternate route: `/v1/alliances/{alliance_id}/icons/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get alliance icon","tags":["Alliance"],"parameters":[{"$ref":"#/parameters/alliance_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Icon URLs for the given alliance id and server","examples":{"application/json":{"px64x64":"https://imageserver.eveonline.com/Alliance/503818424_64.png","px128x128":"https://imageserver.eveonline.com/Alliance/503818424_128.png"}},"schema":{"type":"object","properties":{"px64x64":{"type":"string","title":"get_alliances_alliance_id_icons_px64x64","description":"px64x64 string"},"px128x128":{"type":"string","title":"get_alliances_alliance_id_icons_px128x128","description":"px128x128 string"}},"title":"get_alliances_alliance_id_icons_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"No image server for this datasource","examples":{"application/json":{"error":"No image server for this datasource"}},"schema":{"type":"object","description":"No image server for this datasource","properties":{"error":{"type":"string","description":"error message","title":"get_alliances_alliance_id_icons_error"}},"title":"get_alliances_alliance_id_icons_not_found"}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_alliances_alliance_id_icons","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/alliances/":{"get":{"description":"List all active player alliances\n\n---\nAlternate route: `/dev/alliances/`\n\nAlternate route: `/legacy/alliances/`\n\nAlternate route: `/v1/alliances/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"List all alliances","tags":["Alliance"],"responses":{"200":{"description":"List of Alliance IDs","examples":{"application/json":[99000001,99000002]},"schema":{"type":"array","maxItems":5000,"items":{"type":"integer","format":"int32","minimum":0,"uniqueItems":true,"title":"get_alliances_200_ok","description":"200 ok integer"},"title":"get_alliances_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_alliances","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/assets/":{"get":{"description":"Return a list of the characters assets\n\n---\nAlternate route: `/v2/characters/{character_id}/assets/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\n[This route has an available update](https://esi.tech.ccp.is/diff/latest/dev/#GET-/characters/{character_id}/assets/)","summary":"Get character assets","tags":["Assets"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A flat list of the users assets","examples":{"application/json":[{"location_flag":"Hangar","location_id":60002959,"is_singleton":true,"type_id":3516,"item_id":1000000016835,"location_type":"station"}]},"schema":{"type":"array","maxItems":5000,"items":{"type":"object","required":["type_id","location_id","location_type","item_id","location_flag","is_singleton"],"properties":{"type_id":{"type":"integer","format":"int32","title":"get_characters_character_id_assets_type_id","description":"type_id integer"},"quantity":{"type":"integer","format":"int32","title":"get_characters_character_id_assets_quantity","description":"quantity integer"},"location_id":{"type":"integer","format":"int64","title":"get_characters_character_id_assets_location_id","description":"location_id integer"},"location_type":{"type":"string","enum":["station","solar_system","other"],"title":"get_characters_character_id_assets_location_type","description":"location_type string"},"item_id":{"type":"integer","format":"int64","title":"get_characters_character_id_assets_item_id","description":"item_id integer"},"location_flag":{"type":"string","enum":["AssetSafety","AutoFit","Cargo","CorpseBay","Deliveries","DroneBay","FighterBay","FighterTube0","FighterTube1","FighterTube2","FighterTube3","FighterTube4","FleetHangar","Hangar","HangarAll","HiSlot0","HiSlot1","HiSlot2","HiSlot3","HiSlot4","HiSlot5","HiSlot6","HiSlot7","HiddenModifiers","Implant","LoSlot0","LoSlot1","LoSlot2","LoSlot3","LoSlot4","LoSlot5","LoSlot6","LoSlot7","Locked","MedSlot0","MedSlot1","MedSlot2","MedSlot3","MedSlot4","MedSlot5","MedSlot6","MedSlot7","QuafeBay","RigSlot0","RigSlot1","RigSlot2","RigSlot3","RigSlot4","RigSlot5","RigSlot6","RigSlot7","ShipHangar","Skill","SpecializedAmmoHold","SpecializedCommandCenterHold","SpecializedFuelBay","SpecializedGasHold","SpecializedIndustrialShipHold","SpecializedLargeShipHold","SpecializedMaterialBay","SpecializedMediumShipHold","SpecializedMineralHold","SpecializedOreHold","SpecializedPlanetaryCommoditiesHold","SpecializedSalvageHold","SpecializedShipHold","SpecializedSmallShipHold","SubSystemBay","SubSystemSlot0","SubSystemSlot1","SubSystemSlot2","SubSystemSlot3","SubSystemSlot4","SubSystemSlot5","SubSystemSlot6","SubSystemSlot7","Unlocked","Wardrobe"],"title":"get_characters_character_id_assets_location_flag","description":"location_flag string"},"is_singleton":{"type":"boolean","title":"get_characters_character_id_assets_is_singleton","description":"is_singleton boolean"}},"title":"get_characters_character_id_assets_200_ok","description":"200 ok object"},"title":"get_characters_character_id_assets_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-assets.read_assets.v1"]}],"operationId":"get_characters_character_id_assets","x-cached-seconds":3600,"x-alternate-versions":["v2"]}},"/corporations/{corporation_id}/assets/":{"get":{"description":"Return a list of the corporation assets\n\n---\nAlternate route: `/legacy/corporations/{corporation_id}/assets/`\n\nAlternate route: `/v1/corporations/{corporation_id}/assets/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\nRequires one of the following EVE corporation role(s): Director\n\n\n---\n[This route has an available update](https://esi.tech.ccp.is/diff/latest/dev/#GET-/corporations/{corporation_id}/assets/)","summary":"Get corporation assets","tags":["Assets"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of assets","examples":{"application/json":[{"location_flag":"Hangar","location_id":60002959,"is_singleton":true,"type_id":3516,"item_id":1000000016835,"location_type":"station"}]},"schema":{"type":"array","maxItems":5000,"items":{"type":"object","required":["type_id","location_id","location_type","item_id","location_flag","is_singleton"],"properties":{"type_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_assets_type_id","description":"type_id integer"},"quantity":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_assets_quantity","description":"quantity integer"},"location_id":{"type":"integer","format":"int64","title":"get_corporations_corporation_id_assets_location_id","description":"location_id integer"},"location_type":{"type":"string","enum":["station","solar_system","other"],"title":"get_corporations_corporation_id_assets_location_type","description":"location_type string"},"item_id":{"type":"integer","format":"int64","title":"get_corporations_corporation_id_assets_item_id","description":"item_id integer"},"location_flag":{"type":"string","enum":["AssetSafety","AutoFit","Bonus","Booster","BoosterBay","Capsule","Cargo","CorpDeliveries","CorpSAG1","CorpSAG2","CorpSAG3","CorpSAG4","CorpSAG5","CorpSAG6","CorpSAG7","CrateLoot","Deliveries","DroneBay","DustBattle","DustDatabank","FighterBay","FighterTube0","FighterTube1","FighterTube2","FighterTube3","FighterTube4","FleetHangar","Hangar","HangarAll","HiSlot0","HiSlot1","HiSlot2","HiSlot3","HiSlot4","HiSlot5","HiSlot6","HiSlot7","HiddenModifers","Implant","Impounded","JunkyardReprocessed","JunkyardTrashed","LoSlot0","LoSlot1","LoSlot2","LoSlot3","LoSlot4","LoSlot5","LoSlot6","LoSlot7","Locked","MedSlot0","MedSlot1","MedSlot2","MedSlot3","MedSlot4","MedSlot5","MedSlot6","MedSlot7","OfficeFolder","Pilot","PlanetSurface","QuafeBay","Reward","RigSlot0","RigSlot1","RigSlot2","RigSlot3","RigSlot4","RigSlot5","RigSlot6","RigSlot7","SecondaryStorage","ServiceSlot0","ServiceSlot1","ServiceSlot2","ServiceSlot3","ServiceSlot4","ServiceSlot5","ServiceSlot6","ServiceSlot7","ShipHangar","ShipOffline","Skill","SkillInTraining","SpecializedAmmoHold","SpecializedCommandCenterHold","SpecializedFuelBay","SpecializedGasHold","SpecializedIndustrialShipHold","SpecializedLargeShipHold","SpecializedMaterialBay","SpecializedMediumShipHold","SpecializedMineralHold","SpecializedOreHold","SpecializedPlanetaryCommoditiesHold","SpecializedSalvageHold","SpecializedShipHold","SpecializedSmallShipHold","StructureActive","StructureFuel","StructureInactive","StructureOffline","SubSystemSlot0","SubSystemSlot1","SubSystemSlot2","SubSystemSlot3","SubSystemSlot4","SubSystemSlot5","SubSystemSlot6","SubSystemSlot7","SubsystemBay","Unlocked","Wallet","Wardrobe"],"title":"get_corporations_corporation_id_assets_location_flag","description":"location_flag string"},"is_singleton":{"type":"boolean","title":"get_corporations_corporation_id_assets_is_singleton","description":"is_singleton boolean"}},"title":"get_corporations_corporation_id_assets_200_ok","description":"200 ok object"},"title":"get_corporations_corporation_id_assets_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":["Director"],"security":[{"evesso":["esi-assets.read_corporation_assets.v1"]}],"operationId":"get_corporations_corporation_id_assets","x-cached-seconds":3600,"x-alternate-versions":["legacy","v1"]}},"/characters/{character_id}/assets/names/":{"post":{"description":"Return names for a set of item ids, which you can get from character assets endpoint. Typically used for items that can customize names, like containers or ships.\n\n---\nAlternate route: `/dev/characters/{character_id}/assets/names/`\n\nAlternate route: `/legacy/characters/{character_id}/assets/names/`\n\nAlternate route: `/v1/characters/{character_id}/assets/names/`\n","summary":"Get character asset names","tags":["Assets"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"name":"item_ids","in":"body","description":"A list of item ids","required":true,"schema":{"type":"array","minItems":1,"maxItems":1000,"uniqueItems":true,"items":{"type":"integer","format":"int64","title":"post_characters_character_id_assets_names_item_id","description":"item_id integer"},"title":"post_characters_character_id_assets_names_item_ids","description":"item_ids array"}},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"List of asset names","examples":{"application/json":[{"item_id":12345,"name":"Awesome Name"}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["item_id","name"],"properties":{"item_id":{"type":"integer","format":"int64","title":"post_characters_character_id_assets_names_item_id","description":"item_id integer"},"name":{"type":"string","title":"post_characters_character_id_assets_names_name","description":"name string"}},"title":"post_characters_character_id_assets_names_200_ok","description":"200 ok object"},"title":"post_characters_character_id_assets_names_ok","description":"200 ok array"}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-assets.read_assets.v1"]}],"operationId":"post_characters_character_id_assets_names","x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/assets/locations/":{"post":{"description":"Return locations for a set of item ids, which you can get from character assets endpoint. Coordinates for items in hangars or stations are set to (0,0,0)\n\n---\nAlternate route: `/legacy/characters/{character_id}/assets/locations/`\n\nAlternate route: `/v1/characters/{character_id}/assets/locations/`\n\n\n---\n[This route has an available update](https://esi.tech.ccp.is/diff/latest/dev/#POST-/characters/{character_id}/assets/locations/)","summary":"Get character asset locations","tags":["Assets"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"name":"item_ids","in":"body","description":"A list of item ids","required":true,"schema":{"type":"array","minItems":1,"maxItems":1000,"uniqueItems":true,"items":{"type":"integer","format":"int64","title":"post_characters_character_id_assets_locations_item_id","description":"item_id integer"},"title":"post_characters_character_id_assets_locations_item_ids","description":"item_ids array"}},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"List of asset locations","examples":{"application/json":[{"item_id":12345,"x":1.2,"y":2.3,"z":-3.4}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["item_id","x","y","z"],"properties":{"item_id":{"type":"integer","format":"int64","title":"post_characters_character_id_assets_locations_item_id","description":"item_id integer"},"x":{"type":"number","format":"double","title":"post_characters_character_id_assets_locations_x","description":"x number"},"y":{"type":"number","format":"double","title":"post_characters_character_id_assets_locations_y","description":"y number"},"z":{"type":"number","format":"double","title":"post_characters_character_id_assets_locations_z","description":"z number"}},"title":"post_characters_character_id_assets_locations_200_ok","description":"200 ok object"},"title":"post_characters_character_id_assets_locations_ok","description":"200 ok array"}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-assets.read_assets.v1"]}],"operationId":"post_characters_character_id_assets_locations","x-alternate-versions":["legacy","v1"]}},"/corporations/{corporation_id}/assets/names/":{"post":{"description":"Return names for a set of item ids, which you can get from corporation assets endpoint. Only valid for items that can customize names, like containers or ships.\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/assets/names/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/assets/names/`\n\nAlternate route: `/v1/corporations/{corporation_id}/assets/names/`\n\n\n---\nRequires one of the following EVE corporation role(s): Director\n","summary":"Get coporation asset names","tags":["Assets"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"name":"item_ids","in":"body","description":"A list of item ids","required":true,"schema":{"type":"array","minItems":1,"maxItems":1000,"uniqueItems":true,"items":{"type":"integer","format":"int64","title":"post_corporations_corporation_id_assets_names_item_id","description":"item_id integer"},"title":"post_corporations_corporation_id_assets_names_item_ids","description":"item_ids array"}},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"List of asset names","examples":{"application/json":[{"item_id":12345,"name":"Awesome Name"}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["item_id","name"],"properties":{"item_id":{"type":"integer","format":"int64","title":"post_corporations_corporation_id_assets_names_item_id","description":"item_id integer"},"name":{"type":"string","title":"post_corporations_corporation_id_assets_names_name","description":"name string"}},"title":"post_corporations_corporation_id_assets_names_200_ok","description":"200 ok object"},"title":"post_corporations_corporation_id_assets_names_ok","description":"200 ok array"}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":["Director"],"security":[{"evesso":["esi-assets.read_corporation_assets.v1"]}],"operationId":"post_corporations_corporation_id_assets_names","x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/assets/locations/":{"post":{"description":"Return locations for a set of item ids, which you can get from corporation assets endpoint. Coordinates for items in hangars or stations are set to (0,0,0)\n\n---\nAlternate route: `/legacy/corporations/{corporation_id}/assets/locations/`\n\nAlternate route: `/v1/corporations/{corporation_id}/assets/locations/`\n\n\n---\nRequires one of the following EVE corporation role(s): Director\n\n\n---\n[This route has an available update](https://esi.tech.ccp.is/diff/latest/dev/#POST-/corporations/{corporation_id}/assets/locations/)","summary":"Get corporation asset locations","tags":["Assets"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"name":"item_ids","in":"body","description":"A list of item ids","required":true,"schema":{"type":"array","minItems":1,"maxItems":1000,"uniqueItems":true,"items":{"type":"integer","format":"int64","title":"post_corporations_corporation_id_assets_locations_item_id","description":"item_id integer"},"title":"post_corporations_corporation_id_assets_locations_item_ids","description":"item_ids array"}},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"List of asset locations","examples":{"application/json":[{"item_id":12345,"x":1.2,"y":2.3,"z":-3.4}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["item_id","x","y","z"],"properties":{"item_id":{"type":"integer","format":"int64","title":"post_corporations_corporation_id_assets_locations_item_id","description":"item_id integer"},"x":{"type":"number","format":"double","title":"post_corporations_corporation_id_assets_locations_x","description":"x number"},"y":{"type":"number","format":"double","title":"post_corporations_corporation_id_assets_locations_y","description":"y number"},"z":{"type":"number","format":"double","title":"post_corporations_corporation_id_assets_locations_z","description":"z number"}},"title":"post_corporations_corporation_id_assets_locations_200_ok","description":"200 ok object"},"title":"post_corporations_corporation_id_assets_locations_ok","description":"200 ok array"}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":["Director"],"security":[{"evesso":["esi-assets.read_corporation_assets.v1"]}],"operationId":"post_corporations_corporation_id_assets_locations","x-alternate-versions":["legacy","v1"]}},"/characters/{character_id}/bookmarks/":{"get":{"description":"A list of your character's personal bookmarks\n\n---\nAlternate route: `/dev/characters/{character_id}/bookmarks/`\n\nAlternate route: `/v2/characters/{character_id}/bookmarks/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"List bookmarks","tags":["Bookmarks"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of bookmarks","examples":{"application/json":[{"bookmark_id":4,"location_id":30003430,"item":{"type_id":29633,"item_id":50006722},"folder_id":5,"label":"Stargate","notes":"This is a stargate","created":"2016-08-09T11:57:47Z","creator_id":2112625428},{"bookmark_id":5,"location_id":30003430,"coordinates":{"x":-2958928814000,"y":-338367275823,"z":2114538075090},"folder_id":5,"label":"Random location","notes":"This is a random location in space","created":"2016-08-09T11:57:47Z","creator_id":2112625428}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["bookmark_id","created","label","notes","location_id","creator_id"],"properties":{"bookmark_id":{"type":"integer","format":"int32","title":"get_characters_character_id_bookmarks_bookmark_id","description":"bookmark_id integer"},"folder_id":{"type":"integer","format":"int32","title":"get_characters_character_id_bookmarks_folder_id","description":"folder_id integer"},"created":{"type":"string","format":"date-time","title":"get_characters_character_id_bookmarks_created","description":"created string"},"label":{"type":"string","title":"get_characters_character_id_bookmarks_label","description":"label string"},"notes":{"type":"string","title":"get_characters_character_id_bookmarks_notes","description":"notes string"},"location_id":{"type":"integer","format":"int32","title":"get_characters_character_id_bookmarks_location_id","description":"location_id integer"},"creator_id":{"type":"integer","format":"int32","title":"get_characters_character_id_bookmarks_creator_id","description":"creator_id integer"},"item":{"type":"object","description":"Optional object that is returned if a bookmark was made on a particular item.","required":["item_id","type_id"],"properties":{"item_id":{"type":"integer","format":"int64","title":"get_characters_character_id_bookmarks_item_id","description":"item_id integer"},"type_id":{"type":"integer","format":"int32","title":"get_characters_character_id_bookmarks_type_id","description":"type_id integer"}},"title":"get_characters_character_id_bookmarks_item"},"coordinates":{"type":"object","description":"Optional object that is returned if a bookmark was made on a planet or a random location in space.","required":["x","y","z"],"properties":{"x":{"type":"number","format":"double","title":"get_characters_character_id_bookmarks_x","description":"x number"},"y":{"type":"number","format":"double","title":"get_characters_character_id_bookmarks_y","description":"y number"},"z":{"type":"number","format":"double","title":"get_characters_character_id_bookmarks_z","description":"z number"}},"title":"get_characters_character_id_bookmarks_coordinates"}},"title":"get_characters_character_id_bookmarks_200_ok","description":"200 ok object"},"title":"get_characters_character_id_bookmarks_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-bookmarks.read_character_bookmarks.v1"]}],"operationId":"get_characters_character_id_bookmarks","x-cached-seconds":3600,"x-alternate-versions":["dev","v2"]}},"/characters/{character_id}/bookmarks/folders/":{"get":{"description":"A list of your character's personal bookmark folders\n\n---\nAlternate route: `/dev/characters/{character_id}/bookmarks/folders/`\n\nAlternate route: `/v2/characters/{character_id}/bookmarks/folders/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"List bookmark folders","tags":["Bookmarks"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"List of bookmark folders","examples":{"application/json":[{"folder_id":5,"name":"Icecream"}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["folder_id","name"],"properties":{"folder_id":{"type":"integer","format":"int32","title":"get_characters_character_id_bookmarks_folders_folder_id","description":"folder_id integer"},"name":{"type":"string","title":"get_characters_character_id_bookmarks_folders_name","description":"name string"}},"title":"get_characters_character_id_bookmarks_folders_200_ok","description":"200 ok object"},"title":"get_characters_character_id_bookmarks_folders_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-bookmarks.read_character_bookmarks.v1"]}],"operationId":"get_characters_character_id_bookmarks_folders","x-cached-seconds":3600,"x-alternate-versions":["dev","v2"]}},"/corporations/{corporation_id}/bookmarks/":{"get":{"description":"A list of your corporation's bookmarks\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/bookmarks/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/bookmarks/`\n\nAlternate route: `/v1/corporations/{corporation_id}/bookmarks/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"List corporation bookmarks","tags":["Bookmarks"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"List of corporation owned bookmarks","examples":{"application/json":[{"bookmark_id":4,"location_id":30003430,"item":{"type_id":29633,"item_id":50006722},"creator_id":2112625428,"folder_id":5,"label":"Stargate","notes":"This is a stargate","created":"2016-08-09T11:57:47Z"},{"bookmark_id":5,"location_id":30003430,"coordinates":{"x":-2958928814000,"y":-338367275823,"z":2114538075090},"creator_id":2112625428,"folder_id":5,"label":"Random location","notes":"This is a random location in space","created":"2016-08-09T11:57:47Z"}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["bookmark_id","creator_id","created","label","notes","location_id"],"properties":{"bookmark_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_bookmarks_bookmark_id","description":"bookmark_id integer"},"creator_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_bookmarks_creator_id","description":"creator_id integer"},"folder_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_bookmarks_folder_id","description":"folder_id integer"},"created":{"type":"string","format":"date-time","title":"get_corporations_corporation_id_bookmarks_created","description":"created string"},"label":{"type":"string","title":"get_corporations_corporation_id_bookmarks_label","description":"label string"},"notes":{"type":"string","title":"get_corporations_corporation_id_bookmarks_notes","description":"notes string"},"location_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_bookmarks_location_id","description":"location_id integer"},"item":{"type":"object","description":"Optional object that is returned if a bookmark was made on a particular item.","required":["item_id","type_id"],"properties":{"item_id":{"type":"integer","format":"int64","title":"get_corporations_corporation_id_bookmarks_item_id","description":"item_id integer"},"type_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_bookmarks_type_id","description":"type_id integer"}},"title":"get_corporations_corporation_id_bookmarks_item"},"coordinates":{"type":"object","description":"Optional object that is returned if a bookmark was made on a planet or a random location in space.","required":["x","y","z"],"properties":{"x":{"type":"number","format":"double","title":"get_corporations_corporation_id_bookmarks_x","description":"x number"},"y":{"type":"number","format":"double","title":"get_corporations_corporation_id_bookmarks_y","description":"y number"},"z":{"type":"number","format":"double","title":"get_corporations_corporation_id_bookmarks_z","description":"z number"}},"title":"get_corporations_corporation_id_bookmarks_coordinates"}},"title":"get_corporations_corporation_id_bookmarks_200_ok","description":"200 ok object"},"title":"get_corporations_corporation_id_bookmarks_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-bookmarks.read_corporation_bookmarks.v1"]}],"operationId":"get_corporations_corporation_id_bookmarks","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/bookmarks/folders/":{"get":{"description":"A list of your corporation's bookmark folders\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/bookmarks/folders/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/bookmarks/folders/`\n\nAlternate route: `/v1/corporations/{corporation_id}/bookmarks/folders/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"List corporation bookmark folders","tags":["Bookmarks"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"List of corporation owned bookmark folders","examples":{"application/json":[{"folder_id":5,"name":"Important Locations"}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["folder_id","name"],"properties":{"folder_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_bookmarks_folders_folder_id","description":"folder_id integer"},"name":{"type":"string","title":"get_corporations_corporation_id_bookmarks_folders_name","description":"name string"},"creator_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_bookmarks_folders_creator_id","description":"creator_id integer"}},"title":"get_corporations_corporation_id_bookmarks_folders_200_ok","description":"200 ok object"},"title":"get_corporations_corporation_id_bookmarks_folders_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-bookmarks.read_corporation_bookmarks.v1"]}],"operationId":"get_corporations_corporation_id_bookmarks_folders","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/calendar/":{"get":{"description":"Get 50 event summaries from the calendar. If no from_event ID is given, the resource will return the next 50 chronological event summaries from now. If a from_event ID is specified, it will return the next 50 chronological event summaries from after that event.\n\n---\nAlternate route: `/dev/characters/{character_id}/calendar/`\n\nAlternate route: `/legacy/characters/{character_id}/calendar/`\n\nAlternate route: `/v1/characters/{character_id}/calendar/`\n\n---\nThis route is cached for up to 5 seconds","summary":"List calendar event summaries","tags":["Calendar"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"name":"from_event","in":"query","description":"The event ID to retrieve events from","required":false,"type":"integer","format":"int32"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A collection of event summaries","examples":{"application/json":[{"event_id":1386435,"event_date":"2016-06-26T20:00:00Z","title":"o7 The EVE Online Show","importance":0,"event_response":"accepted"}]},"schema":{"type":"array","description":"Up to 50 events from now or the event you requested","maxItems":50,"items":{"description":"event","type":"object","properties":{"event_id":{"type":"integer","format":"int32","title":"get_characters_character_id_calendar_event_id","description":"event_id integer"},"event_date":{"type":"string","format":"date-time","title":"get_characters_character_id_calendar_event_date","description":"event_date string"},"title":{"type":"string","title":"get_characters_character_id_calendar_title","description":"title string"},"importance":{"type":"integer","format":"int32","title":"get_characters_character_id_calendar_importance","description":"importance integer"},"event_response":{"type":"string","enum":["declined","not_responded","accepted","tentative"],"title":"get_characters_character_id_calendar_event_response","description":"event_response string"}},"title":"get_characters_character_id_calendar_200_ok"},"title":"get_characters_character_id_calendar_ok"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-calendar.read_calendar_events.v1"]}],"operationId":"get_characters_character_id_calendar","x-cached-seconds":5,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/calendar/{event_id}/":{"get":{"description":"Get all the information for a specific event\n\n---\nAlternate route: `/dev/characters/{character_id}/calendar/{event_id}/`\n\nAlternate route: `/v3/characters/{character_id}/calendar/{event_id}/`\n\n---\nThis route is cached for up to 5 seconds","summary":"Get an event","tags":["Calendar"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"name":"event_id","in":"path","description":"The id of the event requested","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Full details of a specific event","examples":{"application/json":{"event_id":1386435,"owner_id":1,"owner_name":"EVE System","date":"2016-06-26T21:00:00Z","title":"o7 The EVE Online Show","duration":60,"importance":1,"response":"Undecided","text":"o7: The EVE Online Show features latest developer news, fast paced action, community overviews and a lot more with CCP Guard and CCP Mimic. Join the thrilling o7 live broadcast at 20:00 EVE time (=UTC) on EVE TV. Don't miss it!","owner_type":"eve_server"}},"schema":{"type":"object","description":"Full details of a specific event","required":["event_id","owner_id","owner_name","date","title","duration","importance","response","text","owner_type"],"properties":{"event_id":{"type":"integer","format":"int32","title":"get_characters_character_id_calendar_event_id_event_id","description":"event_id integer"},"owner_id":{"type":"integer","format":"int32","title":"get_characters_character_id_calendar_event_id_owner_id","description":"owner_id integer"},"owner_name":{"type":"string","title":"get_characters_character_id_calendar_event_id_owner_name","description":"owner_name string"},"date":{"type":"string","format":"date-time","title":"get_characters_character_id_calendar_event_id_date","description":"date string"},"title":{"type":"string","title":"get_characters_character_id_calendar_event_id_title","description":"title string"},"duration":{"type":"integer","format":"int32","description":"Length in minutes","title":"get_characters_character_id_calendar_event_id_duration"},"importance":{"type":"integer","format":"int32","title":"get_characters_character_id_calendar_event_id_importance","description":"importance integer"},"response":{"type":"string","title":"get_characters_character_id_calendar_event_id_response","description":"response string"},"text":{"type":"string","title":"get_characters_character_id_calendar_event_id_text","description":"text string"},"owner_type":{"type":"string","enum":["eve_server","corporation","faction","character","alliance"],"title":"get_characters_character_id_calendar_event_id_owner_type","description":"owner_type string"}},"title":"get_characters_character_id_calendar_event_id_ok"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-calendar.read_calendar_events.v1"]}],"operationId":"get_characters_character_id_calendar_event_id","x-cached-seconds":5,"x-alternate-versions":["dev","v3"]},"put":{"description":"Set your response status to an event\n\n---\nAlternate route: `/dev/characters/{character_id}/calendar/{event_id}/`\n\nAlternate route: `/v3/characters/{character_id}/calendar/{event_id}/`\n","summary":"Respond to an event","tags":["Calendar"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"name":"event_id","in":"path","description":"The ID of the event requested","required":true,"type":"integer","format":"int32"},{"name":"response","in":"body","description":"The response value to set, overriding current value.","required":true,"schema":{"required":["response"],"properties":{"response":{"type":"string","enum":["accepted","declined","tentative"],"title":"put_characters_character_id_calendar_event_id_response","description":"response string"}},"title":"put_characters_character_id_calendar_event_id_response","description":"response schema"}},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"204":{"description":"Event updated"},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-calendar.respond_calendar_events.v1"]}],"operationId":"put_characters_character_id_calendar_event_id","x-alternate-versions":["dev","v3"]}},"/characters/{character_id}/calendar/{event_id}/attendees/":{"get":{"description":"Get all invited attendees for a given event\n\n---\nAlternate route: `/dev/characters/{character_id}/calendar/{event_id}/attendees/`\n\nAlternate route: `/legacy/characters/{character_id}/calendar/{event_id}/attendees/`\n\nAlternate route: `/v1/characters/{character_id}/calendar/{event_id}/attendees/`\n\n---\nThis route is cached for up to 600 seconds","summary":"Get attendees","tags":["Calendar"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"name":"event_id","in":"path","description":"The id of the event requested","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"List of attendees","examples":{"application/json":[{"character_id":2112625428,"event_response":"accepted"},{"character_id":95465499,"event_response":"tentative"}]},"schema":{"type":"array","description":"List of attendees for a given event","maxItems":100,"items":{"type":"object","description":"character_id and response of an attendee","properties":{"character_id":{"type":"integer","format":"int32","title":"get_characters_character_id_calendar_event_id_attendees_character_id","description":"character_id integer"},"event_response":{"type":"string","enum":["declined","not_responded","accepted","tentative"],"title":"get_characters_character_id_calendar_event_id_attendees_event_response","description":"event_response string"}},"title":"get_characters_character_id_calendar_event_id_attendees_200_ok"},"title":"get_characters_character_id_calendar_event_id_attendees_ok"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-calendar.read_calendar_events.v1"]}],"operationId":"get_characters_character_id_calendar_event_id_attendees","x-cached-seconds":600,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/stats/":{"get":{"description":"Returns aggregate yearly stats for a character\n\n---\nAlternate route: `/legacy/characters/{character_id}/stats/`\n\nAlternate route: `/v1/characters/{character_id}/stats/`\n\n---\nThis route is cached for up to 86400 seconds\n\n---\n[This route has an available update](https://esi.tech.ccp.is/diff/latest/dev/#GET-/characters/{character_id}/stats/)","summary":"Yearly aggregate stats","tags":["Character"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Character stats","examples":{"application/json":[{"year":2014,"combat_kills_low_sec":42},{"year":2015,"combat_kills_null_sec":1337}]},"schema":{"type":"array","description":"List of character aggregate stats","maxItems":1000,"items":{"type":"object","description":"Aggregate stats for a year","required":["year"],"properties":{"year":{"type":"integer","format":"int32","description":"Gregorian year for this set of aggregates","title":"get_characters_character_id_stats_year"},"character_minutes":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_character_minutes","description":"character_minutes integer"},"character_sessions_started":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_character_sessions_started","description":"character_sessions_started integer"},"combat_cap_drainedby_npc":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_cap_drainedby_npc","description":"combat_cap_drainedby_npc integer"},"combat_cap_drainedby_pc":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_cap_drainedby_pc","description":"combat_cap_drainedby_pc integer"},"combat_cap_draining_pc":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_cap_draining_pc","description":"combat_cap_draining_pc integer"},"combat_criminal_flag_set":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_criminal_flag_set","description":"combat_criminal_flag_set integer"},"combat_damage_from_np_cs_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_from_np_cs_amount","description":"combat_damage_from_np_cs_amount integer"},"combat_damage_from_np_cs_num_shots":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_from_np_cs_num_shots","description":"combat_damage_from_np_cs_num_shots integer"},"combat_damage_from_players_bomb_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_from_players_bomb_amount","description":"combat_damage_from_players_bomb_amount integer"},"combat_damage_from_players_bomb_num_shots":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_from_players_bomb_num_shots","description":"combat_damage_from_players_bomb_num_shots integer"},"combat_damage_from_players_combat_drone_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_from_players_combat_drone_amount","description":"combat_damage_from_players_combat_drone_amount integer"},"combat_damage_from_players_combat_drone_num_shots":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_from_players_combat_drone_num_shots","description":"combat_damage_from_players_combat_drone_num_shots integer"},"combat_damage_from_players_energy_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_from_players_energy_amount","description":"combat_damage_from_players_energy_amount integer"},"combat_damage_from_players_energy_num_shots":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_from_players_energy_num_shots","description":"combat_damage_from_players_energy_num_shots integer"},"combat_damage_from_players_fighter_bomber_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_from_players_fighter_bomber_amount","description":"combat_damage_from_players_fighter_bomber_amount integer"},"combat_damage_from_players_fighter_bomber_num_shots":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_from_players_fighter_bomber_num_shots","description":"combat_damage_from_players_fighter_bomber_num_shots integer"},"combat_damage_from_players_fighter_drone_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_from_players_fighter_drone_amount","description":"combat_damage_from_players_fighter_drone_amount integer"},"combat_damage_from_players_fighter_drone_num_shots":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_from_players_fighter_drone_num_shots","description":"combat_damage_from_players_fighter_drone_num_shots integer"},"combat_damage_from_players_hybrid_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_from_players_hybrid_amount","description":"combat_damage_from_players_hybrid_amount integer"},"combat_damage_from_players_hybrid_num_shots":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_from_players_hybrid_num_shots","description":"combat_damage_from_players_hybrid_num_shots integer"},"combat_damage_from_players_missile_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_from_players_missile_amount","description":"combat_damage_from_players_missile_amount integer"},"combat_damage_from_players_missile_num_shots":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_from_players_missile_num_shots","description":"combat_damage_from_players_missile_num_shots integer"},"combat_damage_from_players_projectile_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_from_players_projectile_amount","description":"combat_damage_from_players_projectile_amount integer"},"combat_damage_from_players_projectile_num_shots":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_from_players_projectile_num_shots","description":"combat_damage_from_players_projectile_num_shots integer"},"combat_damage_from_players_smart_bomb_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_from_players_smart_bomb_amount","description":"combat_damage_from_players_smart_bomb_amount integer"},"combat_damage_from_players_smart_bomb_num_shots":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_from_players_smart_bomb_num_shots","description":"combat_damage_from_players_smart_bomb_num_shots integer"},"combat_damage_from_players_super_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_from_players_super_amount","description":"combat_damage_from_players_super_amount integer"},"combat_damage_from_players_super_num_shots":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_from_players_super_num_shots","description":"combat_damage_from_players_super_num_shots integer"},"combat_damage_from_structures_total_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_from_structures_total_amount","description":"combat_damage_from_structures_total_amount integer"},"combat_damage_from_structures_total_num_shots":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_from_structures_total_num_shots","description":"combat_damage_from_structures_total_num_shots integer"},"combat_damage_to_players_bomb_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_to_players_bomb_amount","description":"combat_damage_to_players_bomb_amount integer"},"combat_damage_to_players_bomb_num_shots":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_to_players_bomb_num_shots","description":"combat_damage_to_players_bomb_num_shots integer"},"combat_damage_to_players_combat_drone_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_to_players_combat_drone_amount","description":"combat_damage_to_players_combat_drone_amount integer"},"combat_damage_to_players_combat_drone_num_shots":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_to_players_combat_drone_num_shots","description":"combat_damage_to_players_combat_drone_num_shots integer"},"combat_damage_to_players_energy_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_to_players_energy_amount","description":"combat_damage_to_players_energy_amount integer"},"combat_damage_to_players_energy_num_shots":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_to_players_energy_num_shots","description":"combat_damage_to_players_energy_num_shots integer"},"combat_damage_to_players_fighter_bomber_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_to_players_fighter_bomber_amount","description":"combat_damage_to_players_fighter_bomber_amount integer"},"combat_damage_to_players_fighter_bomber_num_shots":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_to_players_fighter_bomber_num_shots","description":"combat_damage_to_players_fighter_bomber_num_shots integer"},"combat_damage_to_players_fighter_drone_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_to_players_fighter_drone_amount","description":"combat_damage_to_players_fighter_drone_amount integer"},"combat_damage_to_players_fighter_drone_num_shots":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_to_players_fighter_drone_num_shots","description":"combat_damage_to_players_fighter_drone_num_shots integer"},"combat_damage_to_players_hybrid_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_to_players_hybrid_amount","description":"combat_damage_to_players_hybrid_amount integer"},"combat_damage_to_players_hybrid_num_shots":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_to_players_hybrid_num_shots","description":"combat_damage_to_players_hybrid_num_shots integer"},"combat_damage_to_players_missile_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_to_players_missile_amount","description":"combat_damage_to_players_missile_amount integer"},"combat_damage_to_players_missile_num_shots":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_to_players_missile_num_shots","description":"combat_damage_to_players_missile_num_shots integer"},"combat_damage_to_players_projectile_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_to_players_projectile_amount","description":"combat_damage_to_players_projectile_amount integer"},"combat_damage_to_players_projectile_num_shots":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_to_players_projectile_num_shots","description":"combat_damage_to_players_projectile_num_shots integer"},"combat_damage_to_players_smart_bomb_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_to_players_smart_bomb_amount","description":"combat_damage_to_players_smart_bomb_amount integer"},"combat_damage_to_players_smart_bomb_num_shots":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_to_players_smart_bomb_num_shots","description":"combat_damage_to_players_smart_bomb_num_shots integer"},"combat_damage_to_players_super_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_to_players_super_amount","description":"combat_damage_to_players_super_amount integer"},"combat_damage_to_players_super_num_shots":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_to_players_super_num_shots","description":"combat_damage_to_players_super_num_shots integer"},"combat_damage_to_structures_total_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_to_structures_total_amount","description":"combat_damage_to_structures_total_amount integer"},"combat_damage_to_structures_total_num_shots":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_damage_to_structures_total_num_shots","description":"combat_damage_to_structures_total_num_shots integer"},"combat_deaths_high_sec":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_deaths_high_sec","description":"combat_deaths_high_sec integer"},"combat_deaths_low_sec":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_deaths_low_sec","description":"combat_deaths_low_sec integer"},"combat_deaths_null_sec":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_deaths_null_sec","description":"combat_deaths_null_sec integer"},"combat_deaths_pod_high_sec":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_deaths_pod_high_sec","description":"combat_deaths_pod_high_sec integer"},"combat_deaths_pod_low_sec":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_deaths_pod_low_sec","description":"combat_deaths_pod_low_sec integer"},"combat_deaths_pod_null_sec":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_deaths_pod_null_sec","description":"combat_deaths_pod_null_sec integer"},"combat_deaths_pod_wormhole":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_deaths_pod_wormhole","description":"combat_deaths_pod_wormhole integer"},"combat_deaths_wormhole":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_deaths_wormhole","description":"combat_deaths_wormhole integer"},"combat_drone_engage":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_drone_engage","description":"combat_drone_engage integer"},"combat_duel_requested":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_duel_requested","description":"combat_duel_requested integer"},"combat_engagement_register":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_engagement_register","description":"combat_engagement_register integer"},"combat_kills_assists":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_kills_assists","description":"combat_kills_assists integer"},"combat_kills_high_sec":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_kills_high_sec","description":"combat_kills_high_sec integer"},"combat_kills_low_sec":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_kills_low_sec","description":"combat_kills_low_sec integer"},"combat_kills_null_sec":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_kills_null_sec","description":"combat_kills_null_sec integer"},"combat_kills_pod_high_sec":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_kills_pod_high_sec","description":"combat_kills_pod_high_sec integer"},"combat_kills_pod_low_sec":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_kills_pod_low_sec","description":"combat_kills_pod_low_sec integer"},"combat_kills_pod_null_sec":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_kills_pod_null_sec","description":"combat_kills_pod_null_sec integer"},"combat_kills_pod_wormhole":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_kills_pod_wormhole","description":"combat_kills_pod_wormhole integer"},"combat_kills_wormhole":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_kills_wormhole","description":"combat_kills_wormhole integer"},"combat_npc_flag_set":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_npc_flag_set","description":"combat_npc_flag_set integer"},"combat_pvp_flag_set":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_pvp_flag_set","description":"combat_pvp_flag_set integer"},"combat_repair_armor_by_remote_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_repair_armor_by_remote_amount","description":"combat_repair_armor_by_remote_amount integer"},"combat_repair_armor_remote_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_repair_armor_remote_amount","description":"combat_repair_armor_remote_amount integer"},"combat_repair_armor_self_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_repair_armor_self_amount","description":"combat_repair_armor_self_amount integer"},"combat_repair_capacitor_by_remote_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_repair_capacitor_by_remote_amount","description":"combat_repair_capacitor_by_remote_amount integer"},"combat_repair_capacitor_remote_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_repair_capacitor_remote_amount","description":"combat_repair_capacitor_remote_amount integer"},"combat_repair_capacitor_self_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_repair_capacitor_self_amount","description":"combat_repair_capacitor_self_amount integer"},"combat_repair_hull_by_remote_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_repair_hull_by_remote_amount","description":"combat_repair_hull_by_remote_amount integer"},"combat_repair_hull_remote_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_repair_hull_remote_amount","description":"combat_repair_hull_remote_amount integer"},"combat_repair_hull_self_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_repair_hull_self_amount","description":"combat_repair_hull_self_amount integer"},"combat_repair_shield_by_remote_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_repair_shield_by_remote_amount","description":"combat_repair_shield_by_remote_amount integer"},"combat_repair_shield_remote_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_repair_shield_remote_amount","description":"combat_repair_shield_remote_amount integer"},"combat_repair_shield_self_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_repair_shield_self_amount","description":"combat_repair_shield_self_amount integer"},"combat_self_destructs":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_self_destructs","description":"combat_self_destructs integer"},"combat_warp_scramble_pc":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_warp_scramble_pc","description":"combat_warp_scramble_pc integer"},"combat_warp_scrambledby_npc":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_warp_scrambledby_npc","description":"combat_warp_scrambledby_npc integer"},"combat_warp_scrambledby_pc":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_warp_scrambledby_pc","description":"combat_warp_scrambledby_pc integer"},"combat_weapon_flag_set":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_weapon_flag_set","description":"combat_weapon_flag_set integer"},"combat_webifiedby_npc":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_webifiedby_npc","description":"combat_webifiedby_npc integer"},"combat_webifiedby_pc":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_webifiedby_pc","description":"combat_webifiedby_pc integer"},"combat_webifying_pc":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_combat_webifying_pc","description":"combat_webifying_pc integer"},"days_of_activity":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_days_of_activity","description":"days_of_activity integer"},"generic_cone_scans":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_generic_cone_scans","description":"generic_cone_scans integer"},"generic_request_scans":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_generic_request_scans","description":"generic_request_scans integer"},"industry_hacking_successes":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_hacking_successes","description":"industry_hacking_successes integer"},"industry_jobs_cancelled":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_cancelled","description":"industry_jobs_cancelled integer"},"industry_jobs_completed_copy_blueprint":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_completed_copy_blueprint","description":"industry_jobs_completed_copy_blueprint integer"},"industry_jobs_completed_invention":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_completed_invention","description":"industry_jobs_completed_invention integer"},"industry_jobs_completed_manufacture":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_completed_manufacture","description":"industry_jobs_completed_manufacture integer"},"industry_jobs_completed_manufacture_asteroid":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_completed_manufacture_asteroid","description":"industry_jobs_completed_manufacture_asteroid integer"},"industry_jobs_completed_manufacture_asteroid_quantity":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_completed_manufacture_asteroid_quantity","description":"industry_jobs_completed_manufacture_asteroid_quantity integer"},"industry_jobs_completed_manufacture_charge":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_completed_manufacture_charge","description":"industry_jobs_completed_manufacture_charge integer"},"industry_jobs_completed_manufacture_charge_quantity":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_completed_manufacture_charge_quantity","description":"industry_jobs_completed_manufacture_charge_quantity integer"},"industry_jobs_completed_manufacture_commodity":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_completed_manufacture_commodity","description":"industry_jobs_completed_manufacture_commodity integer"},"industry_jobs_completed_manufacture_commodity_quantity":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_completed_manufacture_commodity_quantity","description":"industry_jobs_completed_manufacture_commodity_quantity integer"},"industry_jobs_completed_manufacture_deployable":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_completed_manufacture_deployable","description":"industry_jobs_completed_manufacture_deployable integer"},"industry_jobs_completed_manufacture_deployable_quantity":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_completed_manufacture_deployable_quantity","description":"industry_jobs_completed_manufacture_deployable_quantity integer"},"industry_jobs_completed_manufacture_drone":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_completed_manufacture_drone","description":"industry_jobs_completed_manufacture_drone integer"},"industry_jobs_completed_manufacture_drone_quantity":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_completed_manufacture_drone_quantity","description":"industry_jobs_completed_manufacture_drone_quantity integer"},"industry_jobs_completed_manufacture_implant":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_completed_manufacture_implant","description":"industry_jobs_completed_manufacture_implant integer"},"industry_jobs_completed_manufacture_implant_quantity":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_completed_manufacture_implant_quantity","description":"industry_jobs_completed_manufacture_implant_quantity integer"},"industry_jobs_completed_manufacture_module":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_completed_manufacture_module","description":"industry_jobs_completed_manufacture_module integer"},"industry_jobs_completed_manufacture_module_quantity":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_completed_manufacture_module_quantity","description":"industry_jobs_completed_manufacture_module_quantity integer"},"industry_jobs_completed_manufacture_other":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_completed_manufacture_other","description":"industry_jobs_completed_manufacture_other integer"},"industry_jobs_completed_manufacture_other_quantity":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_completed_manufacture_other_quantity","description":"industry_jobs_completed_manufacture_other_quantity integer"},"industry_jobs_completed_manufacture_ship":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_completed_manufacture_ship","description":"industry_jobs_completed_manufacture_ship integer"},"industry_jobs_completed_manufacture_ship_quantity":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_completed_manufacture_ship_quantity","description":"industry_jobs_completed_manufacture_ship_quantity integer"},"industry_jobs_completed_manufacture_structure":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_completed_manufacture_structure","description":"industry_jobs_completed_manufacture_structure integer"},"industry_jobs_completed_manufacture_structure_quantity":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_completed_manufacture_structure_quantity","description":"industry_jobs_completed_manufacture_structure_quantity integer"},"industry_jobs_completed_manufacture_subsystem":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_completed_manufacture_subsystem","description":"industry_jobs_completed_manufacture_subsystem integer"},"industry_jobs_completed_manufacture_subsystem_quantity":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_completed_manufacture_subsystem_quantity","description":"industry_jobs_completed_manufacture_subsystem_quantity integer"},"industry_jobs_completed_material_productivity":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_completed_material_productivity","description":"industry_jobs_completed_material_productivity integer"},"industry_jobs_completed_time_productivity":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_completed_time_productivity","description":"industry_jobs_completed_time_productivity integer"},"industry_jobs_started_copy_blueprint":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_started_copy_blueprint","description":"industry_jobs_started_copy_blueprint integer"},"industry_jobs_started_invention":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_started_invention","description":"industry_jobs_started_invention integer"},"industry_jobs_started_manufacture":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_started_manufacture","description":"industry_jobs_started_manufacture integer"},"industry_jobs_started_material_productivity":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_started_material_productivity","description":"industry_jobs_started_material_productivity integer"},"industry_jobs_started_time_productivity":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_jobs_started_time_productivity","description":"industry_jobs_started_time_productivity integer"},"industry_reprocess_item":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_reprocess_item","description":"industry_reprocess_item integer"},"industry_reprocess_item_quantity":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_industry_reprocess_item_quantity","description":"industry_reprocess_item_quantity integer"},"inventory_abandon_loot_quantity":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_inventory_abandon_loot_quantity","description":"inventory_abandon_loot_quantity integer"},"inventory_trash_item_quantity":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_inventory_trash_item_quantity","description":"inventory_trash_item_quantity integer"},"isk_in":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_isk_in","description":"isk_in integer"},"isk_out":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_isk_out","description":"isk_out integer"},"market_accept_contracts_courier":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_market_accept_contracts_courier","description":"market_accept_contracts_courier integer"},"market_accept_contracts_item_exchange":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_market_accept_contracts_item_exchange","description":"market_accept_contracts_item_exchange integer"},"market_buy_orders_placed":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_market_buy_orders_placed","description":"market_buy_orders_placed integer"},"market_cancel_market_order":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_market_cancel_market_order","description":"market_cancel_market_order integer"},"market_create_contracts_auction":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_market_create_contracts_auction","description":"market_create_contracts_auction integer"},"market_create_contracts_courier":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_market_create_contracts_courier","description":"market_create_contracts_courier integer"},"market_create_contracts_item_exchange":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_market_create_contracts_item_exchange","description":"market_create_contracts_item_exchange integer"},"market_deliver_courier_contract":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_market_deliver_courier_contract","description":"market_deliver_courier_contract integer"},"market_isk_gained":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_market_isk_gained","description":"market_isk_gained integer"},"market_isk_spent":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_market_isk_spent","description":"market_isk_spent integer"},"market_modify_market_order":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_market_modify_market_order","description":"market_modify_market_order integer"},"market_search_contracts":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_market_search_contracts","description":"market_search_contracts integer"},"market_sell_orders_placed":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_market_sell_orders_placed","description":"market_sell_orders_placed integer"},"mining_drone_mine":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_mining_drone_mine","description":"mining_drone_mine integer"},"mining_ore_arkonor":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_mining_ore_arkonor","description":"mining_ore_arkonor integer"},"mining_ore_bistot":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_mining_ore_bistot","description":"mining_ore_bistot integer"},"mining_ore_crokite":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_mining_ore_crokite","description":"mining_ore_crokite integer"},"mining_ore_dark_ochre":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_mining_ore_dark_ochre","description":"mining_ore_dark_ochre integer"},"mining_ore_gneiss":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_mining_ore_gneiss","description":"mining_ore_gneiss integer"},"mining_ore_harvestable_cloud":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_mining_ore_harvestable_cloud","description":"mining_ore_harvestable_cloud integer"},"mining_ore_hedbergite":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_mining_ore_hedbergite","description":"mining_ore_hedbergite integer"},"mining_ore_hemorphite":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_mining_ore_hemorphite","description":"mining_ore_hemorphite integer"},"mining_ore_ice":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_mining_ore_ice","description":"mining_ore_ice integer"},"mining_ore_jaspet":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_mining_ore_jaspet","description":"mining_ore_jaspet integer"},"mining_ore_kernite":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_mining_ore_kernite","description":"mining_ore_kernite integer"},"mining_ore_mercoxit":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_mining_ore_mercoxit","description":"mining_ore_mercoxit integer"},"mining_ore_omber":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_mining_ore_omber","description":"mining_ore_omber integer"},"mining_ore_plagioclase":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_mining_ore_plagioclase","description":"mining_ore_plagioclase integer"},"mining_ore_pyroxeres":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_mining_ore_pyroxeres","description":"mining_ore_pyroxeres integer"},"mining_ore_scordite":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_mining_ore_scordite","description":"mining_ore_scordite integer"},"mining_ore_spodumain":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_mining_ore_spodumain","description":"mining_ore_spodumain integer"},"mining_ore_veldspar":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_mining_ore_veldspar","description":"mining_ore_veldspar integer"},"module_activations_armor_hardener":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_armor_hardener","description":"module_activations_armor_hardener integer"},"module_activations_armor_repair_unit":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_armor_repair_unit","description":"module_activations_armor_repair_unit integer"},"module_activations_armor_resistance_shift_hardener":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_armor_resistance_shift_hardener","description":"module_activations_armor_resistance_shift_hardener integer"},"module_activations_automated_targeting_system":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_automated_targeting_system","description":"module_activations_automated_targeting_system integer"},"module_activations_bastion":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_bastion","description":"module_activations_bastion integer"},"module_activations_bomb_launcher":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_bomb_launcher","description":"module_activations_bomb_launcher integer"},"module_activations_capacitor_booster":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_capacitor_booster","description":"module_activations_capacitor_booster integer"},"module_activations_cargo_scanner":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_cargo_scanner","description":"module_activations_cargo_scanner integer"},"module_activations_cloaking_device":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_cloaking_device","description":"module_activations_cloaking_device integer"},"module_activations_clone_vat_bay":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_clone_vat_bay","description":"module_activations_clone_vat_bay integer"},"module_activations_cynosural_field":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_cynosural_field","description":"module_activations_cynosural_field integer"},"module_activations_damage_control":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_damage_control","description":"module_activations_damage_control integer"},"module_activations_data_miners":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_data_miners","description":"module_activations_data_miners integer"},"module_activations_drone_control_unit":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_drone_control_unit","description":"module_activations_drone_control_unit integer"},"module_activations_drone_tracking_modules":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_drone_tracking_modules","description":"module_activations_drone_tracking_modules integer"},"module_activations_eccm":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_eccm","description":"module_activations_eccm integer"},"module_activations_ecm":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_ecm","description":"module_activations_ecm integer"},"module_activations_ecm_burst":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_ecm_burst","description":"module_activations_ecm_burst integer"},"module_activations_energy_destabilizer":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_energy_destabilizer","description":"module_activations_energy_destabilizer integer"},"module_activations_energy_vampire":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_energy_vampire","description":"module_activations_energy_vampire integer"},"module_activations_energy_weapon":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_energy_weapon","description":"module_activations_energy_weapon integer"},"module_activations_festival_launcher":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_festival_launcher","description":"module_activations_festival_launcher integer"},"module_activations_frequency_mining_laser":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_frequency_mining_laser","description":"module_activations_frequency_mining_laser integer"},"module_activations_fueled_armor_repairer":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_fueled_armor_repairer","description":"module_activations_fueled_armor_repairer integer"},"module_activations_fueled_shield_booster":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_fueled_shield_booster","description":"module_activations_fueled_shield_booster integer"},"module_activations_gang_coordinator":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_gang_coordinator","description":"module_activations_gang_coordinator integer"},"module_activations_gas_cloud_harvester":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_gas_cloud_harvester","description":"module_activations_gas_cloud_harvester integer"},"module_activations_hull_repair_unit":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_hull_repair_unit","description":"module_activations_hull_repair_unit integer"},"module_activations_hybrid_weapon":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_hybrid_weapon","description":"module_activations_hybrid_weapon integer"},"module_activations_industrial_core":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_industrial_core","description":"module_activations_industrial_core integer"},"module_activations_interdiction_sphere_launcher":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_interdiction_sphere_launcher","description":"module_activations_interdiction_sphere_launcher integer"},"module_activations_micro_jump_drive":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_micro_jump_drive","description":"module_activations_micro_jump_drive integer"},"module_activations_mining_laser":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_mining_laser","description":"module_activations_mining_laser integer"},"module_activations_missile_launcher":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_missile_launcher","description":"module_activations_missile_launcher integer"},"module_activations_passive_targeting_system":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_passive_targeting_system","description":"module_activations_passive_targeting_system integer"},"module_activations_probe_launcher":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_probe_launcher","description":"module_activations_probe_launcher integer"},"module_activations_projected_eccm":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_projected_eccm","description":"module_activations_projected_eccm integer"},"module_activations_projectile_weapon":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_projectile_weapon","description":"module_activations_projectile_weapon integer"},"module_activations_propulsion_module":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_propulsion_module","description":"module_activations_propulsion_module integer"},"module_activations_remote_armor_repairer":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_remote_armor_repairer","description":"module_activations_remote_armor_repairer integer"},"module_activations_remote_capacitor_transmitter":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_remote_capacitor_transmitter","description":"module_activations_remote_capacitor_transmitter integer"},"module_activations_remote_ecm_burst":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_remote_ecm_burst","description":"module_activations_remote_ecm_burst integer"},"module_activations_remote_hull_repairer":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_remote_hull_repairer","description":"module_activations_remote_hull_repairer integer"},"module_activations_remote_sensor_booster":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_remote_sensor_booster","description":"module_activations_remote_sensor_booster integer"},"module_activations_remote_sensor_damper":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_remote_sensor_damper","description":"module_activations_remote_sensor_damper integer"},"module_activations_remote_shield_booster":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_remote_shield_booster","description":"module_activations_remote_shield_booster integer"},"module_activations_remote_tracking_computer":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_remote_tracking_computer","description":"module_activations_remote_tracking_computer integer"},"module_activations_salvager":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_salvager","description":"module_activations_salvager integer"},"module_activations_sensor_booster":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_sensor_booster","description":"module_activations_sensor_booster integer"},"module_activations_shield_booster":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_shield_booster","description":"module_activations_shield_booster integer"},"module_activations_shield_hardener":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_shield_hardener","description":"module_activations_shield_hardener integer"},"module_activations_ship_scanner":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_ship_scanner","description":"module_activations_ship_scanner integer"},"module_activations_siege":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_siege","description":"module_activations_siege integer"},"module_activations_smart_bomb":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_smart_bomb","description":"module_activations_smart_bomb integer"},"module_activations_stasis_web":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_stasis_web","description":"module_activations_stasis_web integer"},"module_activations_strip_miner":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_strip_miner","description":"module_activations_strip_miner integer"},"module_activations_super_weapon":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_super_weapon","description":"module_activations_super_weapon integer"},"module_activations_survey_scanner":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_survey_scanner","description":"module_activations_survey_scanner integer"},"module_activations_target_breaker":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_target_breaker","description":"module_activations_target_breaker integer"},"module_activations_target_painter":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_target_painter","description":"module_activations_target_painter integer"},"module_activations_tracking_computer":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_tracking_computer","description":"module_activations_tracking_computer integer"},"module_activations_tracking_disruptor":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_tracking_disruptor","description":"module_activations_tracking_disruptor integer"},"module_activations_tractor_beam":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_tractor_beam","description":"module_activations_tractor_beam integer"},"module_activations_triage":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_triage","description":"module_activations_triage integer"},"module_activations_warp_disrupt_field_generator":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_warp_disrupt_field_generator","description":"module_activations_warp_disrupt_field_generator integer"},"module_activations_warp_scrambler":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_activations_warp_scrambler","description":"module_activations_warp_scrambler integer"},"module_link_weapons":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_link_weapons","description":"module_link_weapons integer"},"module_overload":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_overload","description":"module_overload integer"},"module_repairs":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_module_repairs","description":"module_repairs integer"},"orbital_strike_characters_killed":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_orbital_strike_characters_killed","description":"orbital_strike_characters_killed integer"},"orbital_strike_damage_to_players_armor_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_orbital_strike_damage_to_players_armor_amount","description":"orbital_strike_damage_to_players_armor_amount integer"},"orbital_strike_damage_to_players_shield_amount":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_orbital_strike_damage_to_players_shield_amount","description":"orbital_strike_damage_to_players_shield_amount integer"},"pve_dungeons_completed_agent":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_pve_dungeons_completed_agent","description":"pve_dungeons_completed_agent integer"},"pve_dungeons_completed_distribution":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_pve_dungeons_completed_distribution","description":"pve_dungeons_completed_distribution integer"},"pve_missions_succeeded":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_pve_missions_succeeded","description":"pve_missions_succeeded integer"},"pve_missions_succeeded_epic_arc":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_pve_missions_succeeded_epic_arc","description":"pve_missions_succeeded_epic_arc integer"},"social_add_contact_bad":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_social_add_contact_bad","description":"social_add_contact_bad integer"},"social_add_contact_good":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_social_add_contact_good","description":"social_add_contact_good integer"},"social_add_contact_high":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_social_add_contact_high","description":"social_add_contact_high integer"},"social_add_contact_horrible":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_social_add_contact_horrible","description":"social_add_contact_horrible integer"},"social_add_contact_neutral":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_social_add_contact_neutral","description":"social_add_contact_neutral integer"},"social_add_note":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_social_add_note","description":"social_add_note integer"},"social_added_as_contact_bad":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_social_added_as_contact_bad","description":"social_added_as_contact_bad integer"},"social_added_as_contact_good":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_social_added_as_contact_good","description":"social_added_as_contact_good integer"},"social_added_as_contact_high":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_social_added_as_contact_high","description":"social_added_as_contact_high integer"},"social_added_as_contact_horrible":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_social_added_as_contact_horrible","description":"social_added_as_contact_horrible integer"},"social_added_as_contact_neutral":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_social_added_as_contact_neutral","description":"social_added_as_contact_neutral integer"},"social_calendar_event_created":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_social_calendar_event_created","description":"social_calendar_event_created integer"},"social_chat_messages_alliance":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_social_chat_messages_alliance","description":"social_chat_messages_alliance integer"},"social_chat_messages_constellation":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_social_chat_messages_constellation","description":"social_chat_messages_constellation integer"},"social_chat_messages_corporation":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_social_chat_messages_corporation","description":"social_chat_messages_corporation integer"},"social_chat_messages_fleet":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_social_chat_messages_fleet","description":"social_chat_messages_fleet integer"},"social_chat_messages_region":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_social_chat_messages_region","description":"social_chat_messages_region integer"},"social_chat_messages_solarsystem":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_social_chat_messages_solarsystem","description":"social_chat_messages_solarsystem integer"},"social_chat_messages_warfaction":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_social_chat_messages_warfaction","description":"social_chat_messages_warfaction integer"},"social_chat_total_message_length":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_social_chat_total_message_length","description":"social_chat_total_message_length integer"},"social_direct_trades":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_social_direct_trades","description":"social_direct_trades integer"},"social_fleet_broadcasts":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_social_fleet_broadcasts","description":"social_fleet_broadcasts integer"},"social_fleet_joins":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_social_fleet_joins","description":"social_fleet_joins integer"},"social_mails_received":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_social_mails_received","description":"social_mails_received integer"},"social_mails_sent":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_social_mails_sent","description":"social_mails_sent integer"},"travel_acceleration_gate_activations":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_travel_acceleration_gate_activations","description":"travel_acceleration_gate_activations integer"},"travel_align_to":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_travel_align_to","description":"travel_align_to integer"},"travel_distance_warped_high_sec":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_travel_distance_warped_high_sec","description":"travel_distance_warped_high_sec integer"},"travel_distance_warped_low_sec":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_travel_distance_warped_low_sec","description":"travel_distance_warped_low_sec integer"},"travel_distance_warped_null_sec":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_travel_distance_warped_null_sec","description":"travel_distance_warped_null_sec integer"},"travel_distance_warped_wormhole":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_travel_distance_warped_wormhole","description":"travel_distance_warped_wormhole integer"},"travel_docks_high_sec":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_travel_docks_high_sec","description":"travel_docks_high_sec integer"},"travel_docks_low_sec":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_travel_docks_low_sec","description":"travel_docks_low_sec integer"},"travel_docks_null_sec":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_travel_docks_null_sec","description":"travel_docks_null_sec integer"},"travel_jumps_stargate_high_sec":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_travel_jumps_stargate_high_sec","description":"travel_jumps_stargate_high_sec integer"},"travel_jumps_stargate_low_sec":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_travel_jumps_stargate_low_sec","description":"travel_jumps_stargate_low_sec integer"},"travel_jumps_stargate_null_sec":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_travel_jumps_stargate_null_sec","description":"travel_jumps_stargate_null_sec integer"},"travel_jumps_wormhole":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_travel_jumps_wormhole","description":"travel_jumps_wormhole integer"},"travel_warps_high_sec":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_travel_warps_high_sec","description":"travel_warps_high_sec integer"},"travel_warps_low_sec":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_travel_warps_low_sec","description":"travel_warps_low_sec integer"},"travel_warps_null_sec":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_travel_warps_null_sec","description":"travel_warps_null_sec integer"},"travel_warps_to_bookmark":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_travel_warps_to_bookmark","description":"travel_warps_to_bookmark integer"},"travel_warps_to_celestial":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_travel_warps_to_celestial","description":"travel_warps_to_celestial integer"},"travel_warps_to_fleet_member":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_travel_warps_to_fleet_member","description":"travel_warps_to_fleet_member integer"},"travel_warps_to_scan_result":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_travel_warps_to_scan_result","description":"travel_warps_to_scan_result integer"},"travel_warps_wormhole":{"type":"integer","format":"int64","title":"get_characters_character_id_stats_travel_warps_wormhole","description":"travel_warps_wormhole integer"}},"title":"get_characters_character_id_stats_200_ok"},"title":"get_characters_character_id_stats_ok"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-characterstats.read.v1"]}],"operationId":"get_characters_character_id_stats","x-cached-seconds":86400,"x-alternate-versions":["legacy","v1"]}},"/characters/{character_id}/":{"get":{"description":"Public information about a character\n\n---\nAlternate route: `/dev/characters/{character_id}/`\n\nAlternate route: `/v4/characters/{character_id}/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get character's public information","tags":["Character"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Public data for the given character","examples":{"application/json":{"corporation_id":109299958,"birthday":"2015-03-24T11:37:00Z","name":"CCP Bartender","gender":"male","race_id":2,"description":"","bloodline_id":3,"ancestry_id":19}},"schema":{"type":"object","required":["corporation_id","birthday","name","gender","race_id","bloodline_id"],"properties":{"name":{"type":"string","title":"get_characters_character_id_name","description":"name string"},"description":{"type":"string","title":"get_characters_character_id_description","description":"description string"},"corporation_id":{"type":"integer","format":"int32","description":"The character's corporation ID","title":"get_characters_character_id_corporation_id"},"alliance_id":{"type":"integer","format":"int32","description":"The character's alliance ID","title":"get_characters_character_id_alliance_id"},"birthday":{"type":"string","format":"date-time","description":"Creation date of the character","title":"get_characters_character_id_birthday"},"gender":{"type":"string","enum":["female","male"],"title":"get_characters_character_id_gender","description":"gender string"},"race_id":{"type":"integer","format":"int32","title":"get_characters_character_id_race_id","description":"race_id integer"},"bloodline_id":{"type":"integer","format":"int32","title":"get_characters_character_id_bloodline_id","description":"bloodline_id integer"},"ancestry_id":{"type":"integer","format":"int32","title":"get_characters_character_id_ancestry_id","description":"ancestry_id integer"},"security_status":{"type":"number","format":"float","minimum":-10,"maximum":10,"title":"get_characters_character_id_security_status","description":"security_status number"},"faction_id":{"type":"integer","format":"int32","description":"ID of the faction the character is fighting for, if the character is enlisted in Factional Warfare","title":"get_characters_character_id_faction_id"}},"title":"get_characters_character_id_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"Character not found","schema":{"type":"object","title":"get_characters_character_id_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"get_characters_character_id_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_characters_character_id","x-cached-seconds":3600,"x-alternate-versions":["dev","v4"]}},"/characters/affiliation/":{"post":{"description":"Bulk lookup of character IDs to corporation, alliance and faction\n\n---\nAlternate route: `/dev/characters/affiliation/`\n\nAlternate route: `/legacy/characters/affiliation/`\n\nAlternate route: `/v1/characters/affiliation/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Character affiliation","tags":["Character"],"parameters":[{"name":"characters","in":"body","description":"The character IDs to fetch affiliations for. All characters must exist, or none will be returned.","required":true,"schema":{"type":"array","minItems":1,"maxItems":1000,"uniqueItems":true,"items":{"type":"integer","format":"int32","title":"post_characters_affiliation_character","description":"character integer"},"title":"post_characters_affiliation_characters","description":"characters array"}},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Character corporation, alliance and faction IDs","examples":{"application/json":[{"character_id":95538921,"corporation_id":109299958,"alliance_id":434243723}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["character_id","corporation_id"],"properties":{"character_id":{"type":"integer","format":"int32","description":"The character's ID","title":"post_characters_affiliation_character_id"},"corporation_id":{"type":"integer","format":"int32","description":"The character's corporation ID","title":"post_characters_affiliation_corporation_id"},"alliance_id":{"type":"integer","format":"int32","description":"The character's alliance ID, if their corporation is in an alliance","title":"post_characters_affiliation_alliance_id"},"faction_id":{"type":"integer","format":"int32","description":"The character's faction ID, if their corporation is in a faction","title":"post_characters_affiliation_faction_id"}},"title":"post_characters_affiliation_200_ok","description":"200 ok object"},"title":"post_characters_affiliation_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"No characters found!","schema":{"type":"object","title":"post_characters_affiliation_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"post_characters_affiliation_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"post_characters_affiliation","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/cspa/":{"post":{"description":"Takes a source character ID in the url and a set of target character ID's in the body, returns a CSPA charge cost\n\n---\nAlternate route: `/legacy/characters/{character_id}/cspa/`\n\nAlternate route: `/v3/characters/{character_id}/cspa/`\n\n\n---\n[This route has an available update](https://esi.tech.ccp.is/diff/latest/dev/#POST-/characters/{character_id}/cspa/)","summary":"Calculate a CSPA charge cost","tags":["Character"],"parameters":[{"$ref":"#/parameters/character_id"},{"name":"characters","in":"body","description":"The target characters to calculate the charge for","required":true,"schema":{"type":"object","required":["characters"],"properties":{"characters":{"type":"array","minItems":1,"uniqueItems":true,"maxItems":100,"items":{"type":"integer","format":"int32","title":"post_characters_character_id_cspa_character","description":"character integer"},"title":"post_characters_character_id_cspa_characters","description":"characters array"}},"title":"post_characters_character_id_cspa_characters","description":"characters object"}},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"201":{"description":"Aggregate cost of sending a mail from the source character to the target characters, in ISK hundredths","examples":{"application/json":{"cost":295000}},"schema":{"type":"object","properties":{"cost":{"type":"integer","format":"int64","title":"post_characters_character_id_cspa_cost","description":"cost integer"}},"title":"post_characters_character_id_cspa_created","description":"201 created object"}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-characters.read_contacts.v1"]}],"operationId":"post_characters_character_id_cspa","x-alternate-versions":["legacy","v3"]}},"/characters/names/":{"get":{"description":"Resolve a set of character IDs to character names\n\n---\nAlternate route: `/dev/characters/names/`\n\nAlternate route: `/legacy/characters/names/`\n\nAlternate route: `/v1/characters/names/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get character names","tags":["Character"],"parameters":[{"name":"character_ids","in":"query","description":"A comma separated list of character IDs","required":true,"type":"array","maxItems":1000,"minItems":1,"items":{"type":"integer","format":"int64"}},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"List of id/name associations","examples":{"application/json":[{"character_id":95465499,"character_name":"CCP Bartender"}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["character_id","character_name"],"properties":{"character_id":{"type":"integer","format":"int64","title":"get_characters_names_character_id","description":"character_id integer"},"character_name":{"type":"string","title":"get_characters_names_character_name","description":"character_name string"}},"title":"get_characters_names_200_ok","description":"200 ok object"},"title":"get_characters_names_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_characters_names","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/portrait/":{"get":{"description":"Get portrait urls for a character\n\n---\nAlternate route: `/dev/characters/{character_id}/portrait/`\n\nAlternate route: `/v2/characters/{character_id}/portrait/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get character portraits","tags":["Character"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Public data for the given character","examples":{"application/json":{"px64x64":"https://imageserver.eveonline.com/Character/95465499_64.jpg","px128x128":"https://imageserver.eveonline.com/Character/95465499_128.jpg","px256x256":"https://imageserver.eveonline.com/Character/95465499_256.jpg","px512x512":"https://imageserver.eveonline.com/Character/95465499_512.jpg"}},"schema":{"type":"object","properties":{"px64x64":{"type":"string","title":"get_characters_character_id_portrait_px64x64","description":"px64x64 string"},"px128x128":{"type":"string","title":"get_characters_character_id_portrait_px128x128","description":"px128x128 string"},"px256x256":{"type":"string","title":"get_characters_character_id_portrait_px256x256","description":"px256x256 string"},"px512x512":{"type":"string","title":"get_characters_character_id_portrait_px512x512","description":"px512x512 string"}},"title":"get_characters_character_id_portrait_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"No image server for this datasource","examples":{"application/json":{"error":"No image server for this datasource"}},"schema":{"type":"object","description":"No image server for this datasource","properties":{"error":{"type":"string","description":"error message","title":"get_characters_character_id_portrait_error"}},"title":"get_characters_character_id_portrait_not_found"}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_characters_character_id_portrait","x-cached-seconds":3600,"x-alternate-versions":["dev","v2"]}},"/characters/{character_id}/corporationhistory/":{"get":{"description":"Get a list of all the corporations a character has been a member of\n\n---\nAlternate route: `/dev/characters/{character_id}/corporationhistory/`\n\nAlternate route: `/legacy/characters/{character_id}/corporationhistory/`\n\nAlternate route: `/v1/characters/{character_id}/corporationhistory/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get corporation history","tags":["Character"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Corporation history for the given character","examples":{"application/json":[{"start_date":"2016-06-26T20:00:00Z","corporation_id":90000001,"is_deleted":true,"record_id":500},{"start_date":"2016-07-26T20:00:00Z","corporation_id":90000002,"record_id":501}]},"schema":{"type":"array","maxItems":10000,"items":{"type":"object","required":["start_date","corporation_id","record_id"],"properties":{"start_date":{"type":"string","format":"date-time","title":"get_characters_character_id_corporationhistory_start_date","description":"start_date string"},"corporation_id":{"type":"integer","format":"int32","title":"get_characters_character_id_corporationhistory_corporation_id","description":"corporation_id integer"},"is_deleted":{"type":"boolean","description":"True if the corporation has been deleted","title":"get_characters_character_id_corporationhistory_is_deleted"},"record_id":{"type":"integer","format":"int32","description":"An incrementing ID that can be used to canonically establish order of records in cases where dates may be ambiguous","title":"get_characters_character_id_corporationhistory_record_id"}},"title":"get_characters_character_id_corporationhistory_200_ok","description":"200 ok object"},"title":"get_characters_character_id_corporationhistory_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_characters_character_id_corporationhistory","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/chat_channels/":{"get":{"description":"Return chat channels that a character is the owner or operator of\n\n---\nAlternate route: `/dev/characters/{character_id}/chat_channels/`\n\nAlternate route: `/legacy/characters/{character_id}/chat_channels/`\n\nAlternate route: `/v1/characters/{character_id}/chat_channels/`\n\n---\nThis route is cached for up to 300 seconds","summary":"Get chat channels","tags":["Character"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of chat channels","examples":{"application/json":[{"channel_id":-69329950,"name":"Players' Haven","owner_id":95578451,"comparison_key":"players'haven","has_password":false,"motd":"Feed pineapples to the cats!","allowed":[],"operators":[],"blocked":[],"muted":[]}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["channel_id","name","owner_id","comparison_key","has_password","motd","allowed","operators","blocked","muted"],"properties":{"channel_id":{"type":"integer","format":"int32","description":"Unique channel ID. Always negative for player-created channels. Permanent (CCP created) channels have a positive ID, but don't appear in the API","title":"get_characters_character_id_chat_channels_channel_id"},"name":{"type":"string","description":"Displayed name of channel","title":"get_characters_character_id_chat_channels_name"},"owner_id":{"type":"integer","format":"int32","title":"get_characters_character_id_chat_channels_owner_id","description":"owner_id integer"},"comparison_key":{"type":"string","description":"Normalized, unique string used to compare channel names","title":"get_characters_character_id_chat_channels_comparison_key"},"has_password":{"type":"boolean","description":"If this is a password protected channel","title":"get_characters_character_id_chat_channels_has_password"},"motd":{"type":"string","description":"Message of the day for this channel","title":"get_characters_character_id_chat_channels_motd"},"allowed":{"type":"array","maxItems":1000,"items":{"type":"object","required":["accessor_id","accessor_type"],"properties":{"accessor_id":{"type":"integer","format":"int32","description":"ID of an allowed channel member","title":"get_characters_character_id_chat_channels_accessor_id"},"accessor_type":{"type":"string","enum":["character","corporation","alliance"],"title":"get_characters_character_id_chat_channels_accessor_type","description":"accessor_type string"}},"title":"get_characters_character_id_chat_channels_allowed","description":"allowed object"},"title":"get_characters_character_id_chat_channels_allowed","description":"allowed array"},"operators":{"type":"array","maxItems":1000,"items":{"type":"object","required":["accessor_id","accessor_type"],"properties":{"accessor_id":{"type":"integer","format":"int32","description":"ID of a channel operator","title":"get_characters_character_id_chat_channels_accessor_id"},"accessor_type":{"type":"string","enum":["character","corporation","alliance"],"title":"get_characters_character_id_chat_channels_accessor_type","description":"accessor_type string"}},"title":"get_characters_character_id_chat_channels_operator","description":"operator object"},"title":"get_characters_character_id_chat_channels_operators","description":"operators array"},"blocked":{"type":"array","maxItems":1000,"items":{"type":"object","required":["accessor_id","accessor_type"],"properties":{"accessor_id":{"type":"integer","format":"int32","description":"ID of a blocked channel member","title":"get_characters_character_id_chat_channels_accessor_id"},"accessor_type":{"type":"string","enum":["character","corporation","alliance"],"title":"get_characters_character_id_chat_channels_accessor_type","description":"accessor_type string"},"reason":{"type":"string","description":"Reason this accessor is blocked","title":"get_characters_character_id_chat_channels_reason"},"end_at":{"type":"string","format":"date-time","description":"Time at which this accessor will no longer be blocked","title":"get_characters_character_id_chat_channels_end_at"}},"title":"get_characters_character_id_chat_channels_blocked","description":"blocked object"},"title":"get_characters_character_id_chat_channels_blocked","description":"blocked array"},"muted":{"type":"array","maxItems":1000,"items":{"type":"object","required":["accessor_id","accessor_type"],"properties":{"accessor_id":{"type":"integer","format":"int32","description":"ID of a muted channel member","title":"get_characters_character_id_chat_channels_accessor_id"},"accessor_type":{"type":"string","enum":["character","corporation","alliance"],"title":"get_characters_character_id_chat_channels_accessor_type","description":"accessor_type string"},"reason":{"type":"string","description":"Reason this accessor is muted","title":"get_characters_character_id_chat_channels_reason"},"end_at":{"type":"string","format":"date-time","description":"Time at which this accessor will no longer be muted","title":"get_characters_character_id_chat_channels_end_at"}},"title":"get_characters_character_id_chat_channels_muted","description":"muted object"},"title":"get_characters_character_id_chat_channels_muted","description":"muted array"}},"title":"get_characters_character_id_chat_channels_200_ok","description":"200 ok object"},"title":"get_characters_character_id_chat_channels_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-characters.read_chat_channels.v1"]}],"operationId":"get_characters_character_id_chat_channels","x-cached-seconds":300,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/medals/":{"get":{"description":"Return a list of medals the character has\n\n---\nAlternate route: `/dev/characters/{character_id}/medals/`\n\nAlternate route: `/legacy/characters/{character_id}/medals/`\n\nAlternate route: `/v1/characters/{character_id}/medals/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get medals","tags":["Character"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of medals","examples":{"application/json":[{"medal_id":3,"title":"33 tester medal","description":"For 33 corp!","corporation_id":98000001,"issuer_id":2112000002,"date":"2017-03-16T15:01:45Z","reason":"Thanks!","status":"private","graphics":[{"part":1,"layer":0,"graphic":"gallente.1_1","color":-1}]}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["medal_id","title","description","corporation_id","issuer_id","date","reason","status","graphics"],"properties":{"medal_id":{"type":"integer","format":"int32","title":"get_characters_character_id_medals_medal_id","description":"medal_id integer"},"title":{"type":"string","title":"get_characters_character_id_medals_title","description":"title string"},"description":{"type":"string","title":"get_characters_character_id_medals_description","description":"description string"},"corporation_id":{"type":"integer","format":"int32","title":"get_characters_character_id_medals_corporation_id","description":"corporation_id integer"},"issuer_id":{"type":"integer","format":"int32","title":"get_characters_character_id_medals_issuer_id","description":"issuer_id integer"},"date":{"type":"string","format":"date-time","title":"get_characters_character_id_medals_date","description":"date string"},"reason":{"type":"string","title":"get_characters_character_id_medals_reason","description":"reason string"},"status":{"type":"string","enum":["public","private"],"title":"get_characters_character_id_medals_status","description":"status string"},"graphics":{"type":"array","maxItems":10,"items":{"type":"object","required":["part","layer","graphic"],"properties":{"part":{"type":"integer","format":"int32","title":"get_characters_character_id_medals_part","description":"part integer"},"layer":{"type":"integer","format":"int32","title":"get_characters_character_id_medals_layer","description":"layer integer"},"graphic":{"type":"string","title":"get_characters_character_id_medals_graphic","description":"graphic string"},"color":{"type":"integer","format":"int32","title":"get_characters_character_id_medals_color","description":"color integer"}},"title":"get_characters_character_id_medals_graphic","description":"graphic object"},"title":"get_characters_character_id_medals_graphics","description":"graphics array"}},"title":"get_characters_character_id_medals_200_ok","description":"200 ok object"},"title":"get_characters_character_id_medals_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-characters.read_medals.v1"]}],"operationId":"get_characters_character_id_medals","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/standings/":{"get":{"description":"Return character standings from agents, NPC corporations, and factions\n\n---\nAlternate route: `/dev/characters/{character_id}/standings/`\n\nAlternate route: `/legacy/characters/{character_id}/standings/`\n\nAlternate route: `/v1/characters/{character_id}/standings/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get standings","tags":["Character"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of standings","examples":{"application/json":[{"from_id":3009841,"from_type":"agent","standing":0.1},{"from_id":1000061,"from_type":"npc_corp","standing":0},{"from_id":500003,"from_type":"faction","standing":-1}]},"schema":{"type":"array","maxItems":10000,"items":{"type":"object","required":["from_id","from_type","standing"],"properties":{"from_id":{"type":"integer","format":"int32","title":"get_characters_character_id_standings_from_id","description":"from_id integer"},"from_type":{"type":"string","enum":["agent","npc_corp","faction"],"title":"get_characters_character_id_standings_from_type","description":"from_type string"},"standing":{"type":"number","format":"float","minimum":-10,"maximum":10,"title":"get_characters_character_id_standings_standing","description":"standing number"}},"title":"get_characters_character_id_standings_200_ok","description":"200 ok object"},"title":"get_characters_character_id_standings_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-characters.read_standings.v1"]}],"operationId":"get_characters_character_id_standings","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/agents_research/":{"get":{"description":"Return a list of agents research information for a character. The formula for finding the current research points with an agent is: currentPoints = remainderPoints + pointsPerDay * days(currentTime - researchStartDate)\n\n---\nAlternate route: `/dev/characters/{character_id}/agents_research/`\n\nAlternate route: `/legacy/characters/{character_id}/agents_research/`\n\nAlternate route: `/v1/characters/{character_id}/agents_research/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get agents research","tags":["Character"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of agents research information","examples":{"application/json":[{"agent_id":3009358,"skill_type_id":11450,"started_at":"2017-03-23T14:47:00Z","points_per_day":53.5346162146776,"remainder_points":53604.0634303189}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["agent_id","skill_type_id","started_at","points_per_day","remainder_points"],"properties":{"agent_id":{"type":"integer","format":"int32","title":"get_characters_character_id_agents_research_agent_id","description":"agent_id integer"},"skill_type_id":{"type":"integer","format":"int32","title":"get_characters_character_id_agents_research_skill_type_id","description":"skill_type_id integer"},"started_at":{"type":"string","format":"date-time","title":"get_characters_character_id_agents_research_started_at","description":"started_at string"},"points_per_day":{"type":"number","format":"float","title":"get_characters_character_id_agents_research_points_per_day","description":"points_per_day number"},"remainder_points":{"type":"number","format":"float","title":"get_characters_character_id_agents_research_remainder_points","description":"remainder_points number"}},"title":"get_characters_character_id_agents_research_200_ok","description":"200 ok object"},"title":"get_characters_character_id_agents_research_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-characters.read_agents_research.v1"]}],"operationId":"get_characters_character_id_agents_research","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/blueprints/":{"get":{"description":"Return a list of blueprints the character owns\n\n---\nAlternate route: `/dev/characters/{character_id}/blueprints/`\n\nAlternate route: `/v2/characters/{character_id}/blueprints/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get blueprints","tags":["Character"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of blueprints","examples":{"application/json":[{"item_id":1000000010495,"type_id":691,"location_id":60014719,"location_flag":"Hangar","quantity":1,"time_efficiency":0,"material_efficiency":0,"runs":-1}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["item_id","type_id","location_id","location_flag","quantity","time_efficiency","material_efficiency","runs"],"properties":{"item_id":{"type":"integer","format":"int64","description":"Unique ID for this item.","title":"get_characters_character_id_blueprints_item_id"},"type_id":{"type":"integer","format":"int32","title":"get_characters_character_id_blueprints_type_id","description":"type_id integer"},"location_id":{"type":"integer","format":"int64","description":"References a solar system, station or item_id if this blueprint is located within a container. If the return value is an item_id, then the Character AssetList API must be queried to find the container using the given item_id to determine the correct location of the Blueprint.","title":"get_characters_character_id_blueprints_location_id"},"location_flag":{"type":"string","description":"Type of the location_id","enum":["AutoFit","Cargo","CorpseBay","DroneBay","FleetHangar","Deliveries","HiddenModifiers","Hangar","HangarAll","LoSlot0","LoSlot1","LoSlot2","LoSlot3","LoSlot4","LoSlot5","LoSlot6","LoSlot7","MedSlot0","MedSlot1","MedSlot2","MedSlot3","MedSlot4","MedSlot5","MedSlot6","MedSlot7","HiSlot0","HiSlot1","HiSlot2","HiSlot3","HiSlot4","HiSlot5","HiSlot6","HiSlot7","AssetSafety","Locked","Unlocked","Implant","QuafeBay","RigSlot0","RigSlot1","RigSlot2","RigSlot3","RigSlot4","RigSlot5","RigSlot6","RigSlot7","ShipHangar","SpecializedFuelBay","SpecializedOreHold","SpecializedGasHold","SpecializedMineralHold","SpecializedSalvageHold","SpecializedShipHold","SpecializedSmallShipHold","SpecializedMediumShipHold","SpecializedLargeShipHold","SpecializedIndustrialShipHold","SpecializedAmmoHold","SpecializedCommandCenterHold","SpecializedPlanetaryCommoditiesHold","SpecializedMaterialBay","SubSystemSlot0","SubSystemSlot1","SubSystemSlot2","SubSystemSlot3","SubSystemSlot4","SubSystemSlot5","SubSystemSlot6","SubSystemSlot7","FighterBay","FighterTube0","FighterTube1","FighterTube2","FighterTube3","FighterTube4","Module"],"title":"get_characters_character_id_blueprints_location_flag"},"quantity":{"type":"integer","format":"int32","description":"A range of numbers with a minimum of -2 and no maximum value where -1 is an original and -2 is a copy. It can be a positive integer if it is a stack of blueprint originals fresh from the market (e.g. no activities performed on them yet).","minimum":-2,"title":"get_characters_character_id_blueprints_quantity"},"time_efficiency":{"type":"integer","format":"int32","description":"Time Efficiency Level of the blueprint.","minimum":0,"maximum":20,"title":"get_characters_character_id_blueprints_time_efficiency"},"material_efficiency":{"type":"integer","format":"int32","description":"Material Efficiency Level of the blueprint.","minimum":0,"maximum":25,"title":"get_characters_character_id_blueprints_material_efficiency"},"runs":{"type":"integer","format":"int32","description":"Number of runs remaining if the blueprint is a copy, -1 if it is an original.","minimum":-1,"title":"get_characters_character_id_blueprints_runs"}},"title":"get_characters_character_id_blueprints_200_ok","description":"200 ok object"},"title":"get_characters_character_id_blueprints_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-characters.read_blueprints.v1"]}],"operationId":"get_characters_character_id_blueprints","x-cached-seconds":3600,"x-alternate-versions":["dev","v2"]}},"/characters/{character_id}/fatigue/":{"get":{"description":"Return a character's jump activation and fatigue information\n\n---\nAlternate route: `/dev/characters/{character_id}/fatigue/`\n\nAlternate route: `/legacy/characters/{character_id}/fatigue/`\n\nAlternate route: `/v1/characters/{character_id}/fatigue/`\n\n---\nThis route is cached for up to 300 seconds","summary":"Get jump fatigue","tags":["Character"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Jump activation and fatigue information","examples":{"application/json":{"last_jump_date":"2017-07-05T15:47:00Z","jump_fatigue_expire_date":"2017-07-06T15:47:00Z","last_update_date":"2017-07-05T15:42:00Z"}},"schema":{"type":"object","properties":{"last_jump_date":{"type":"string","format":"date-time","description":"Character's last jump activation","title":"get_characters_character_id_fatigue_last_jump_date"},"jump_fatigue_expire_date":{"type":"string","format":"date-time","description":"Character's jump fatigue expiry","title":"get_characters_character_id_fatigue_jump_fatigue_expire_date"},"last_update_date":{"type":"string","format":"date-time","description":"Character's last jump update","title":"get_characters_character_id_fatigue_last_update_date"}},"title":"get_characters_character_id_fatigue_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-characters.read_fatigue.v1"]}],"operationId":"get_characters_character_id_fatigue","x-cached-seconds":300,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/notifications/contacts/":{"get":{"description":"Return notifications about having been added to someone's contact list\n\n---\nAlternate route: `/dev/characters/{character_id}/notifications/contacts/`\n\nAlternate route: `/legacy/characters/{character_id}/notifications/contacts/`\n\nAlternate route: `/v1/characters/{character_id}/notifications/contacts/`\n\n---\nThis route is cached for up to 600 seconds","summary":"Get new contact notifications","tags":["Character"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of contact notifications","examples":{"application/json":[{"notification_id":1,"sender_character_id":95465499,"send_date":"2017-08-16T10:08:00Z","standing_level":1.5,"message":"hello friend :3"}]},"schema":{"type":"array","maxItems":50,"items":{"type":"object","required":["notification_id","send_date","standing_level","message","sender_character_id"],"properties":{"notification_id":{"type":"integer","format":"int32","title":"get_characters_character_id_notifications_contacts_notification_id","description":"notification_id integer"},"send_date":{"type":"string","format":"date-time","title":"get_characters_character_id_notifications_contacts_send_date","description":"send_date string"},"standing_level":{"description":"A number representing the standing level the receiver has been added at by the sender. The standing levels are as follows: -10 -> Terrible | -5 -> Bad | 0 -> Neutral | 5 -> Good | 10 -> Excellent","type":"number","format":"float","title":"get_characters_character_id_notifications_contacts_standing_level"},"message":{"type":"string","title":"get_characters_character_id_notifications_contacts_message","description":"message string"},"sender_character_id":{"type":"integer","format":"int32","title":"get_characters_character_id_notifications_contacts_sender_character_id","description":"sender_character_id integer"}},"title":"get_characters_character_id_notifications_contacts_200_ok","description":"200 ok object"},"title":"get_characters_character_id_notifications_contacts_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-characters.read_notifications.v1"]}],"operationId":"get_characters_character_id_notifications_contacts","x-cached-seconds":600,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/notifications/":{"get":{"description":"Return character notifications\n\n---\nAlternate route: `/dev/characters/{character_id}/notifications/`\n\nAlternate route: `/legacy/characters/{character_id}/notifications/`\n\nAlternate route: `/v1/characters/{character_id}/notifications/`\n\n---\nThis route is cached for up to 600 seconds","summary":"Get character notifications","tags":["Character"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Returns your recent notifications","examples":{"application/json":[{"notification_id":1,"type":"InsurancePayoutMsg","sender_id":1000132,"sender_type":"corporation","timestamp":"2017-08-16T10:08:00Z","is_read":true,"text":"amount: 3731016.4000000004\\nitemID: 1024881021663\\npayout: 1\\n"}]},"schema":{"type":"array","maxItems":500,"items":{"type":"object","required":["notification_id","type","sender_id","sender_type","timestamp"],"properties":{"notification_id":{"type":"integer","format":"int64","title":"get_characters_character_id_notifications_notification_id","description":"notification_id integer"},"type":{"type":"string","enum":["AcceptedAlly","AcceptedSurrender","AllAnchoringMsg","AllMaintenanceBillMsg","AllStrucInvulnerableMsg","AllStructVulnerableMsg","AllWarCorpJoinedAllianceMsg","AllWarDeclaredMsg","AllWarInvalidatedMsg","AllWarRetractedMsg","AllWarSurrenderMsg","AllianceCapitalChanged","AllyContractCancelled","AllyJoinedWarAggressorMsg","AllyJoinedWarAllyMsg","AllyJoinedWarDefenderMsg","BattlePunishFriendlyFire","BillOutOfMoneyMsg","BillPaidCorpAllMsg","BountyClaimMsg","BountyESSShared","BountyESSTaken","BountyPlacedAlliance","BountyPlacedChar","BountyPlacedCorp","BountyYourBountyClaimed","BuddyConnectContactAdd","CharAppAcceptMsg","CharAppRejectMsg","CharAppWithdrawMsg","CharLeftCorpMsg","CharMedalMsg","CharTerminationMsg","CloneActivationMsg","CloneActivationMsg2","CloneMovedMsg","CloneRevokedMsg1","CloneRevokedMsg2","ContactAdd","ContactEdit","ContainerPasswordMsg","CorpAllBillMsg","CorpAppAcceptMsg","CorpAppInvitedMsg","CorpAppNewMsg","CorpAppRejectCustomMsg","CorpAppRejectMsg","CorpDividendMsg","CorpFriendlyFireDisableTimerCompleted","CorpFriendlyFireDisableTimerStarted","CorpFriendlyFireEnableTimerCompleted","CorpFriendlyFireEnableTimerStarted","CorpKicked","CorpLiquidationMsg","CorpNewCEOMsg","CorpNewsMsg","CorpOfficeExpirationMsg","CorpStructLostMsg","CorpTaxChangeMsg","CorpVoteCEORevokedMsg","CorpVoteMsg","CorpWarDeclaredMsg","CorpWarFightingLegalMsg","CorpWarInvalidatedMsg","CorpWarRetractedMsg","CorpWarSurrenderMsg","CustomsMsg","DeclareWar","DistrictAttacked","DustAppAcceptedMsg","EntosisCaptureStarted","FWAllianceKickMsg","FWAllianceWarningMsg","FWCharKickMsg","FWCharRankGainMsg","FWCharRankLossMsg","FWCharWarningMsg","FWCorpJoinMsg","FWCorpKickMsg","FWCorpLeaveMsg","FWCorpWarningMsg","FacWarCorpJoinRequestMsg","FacWarCorpJoinWithdrawMsg","FacWarCorpLeaveRequestMsg","FacWarCorpLeaveWithdrawMsg","FacWarLPDisqualifiedEvent","FacWarLPDisqualifiedKill","FacWarLPPayoutEvent","FacWarLPPayoutKill","GameTimeAdded","GameTimeReceived","GameTimeSent","GiftReceived","IHubDestroyedByBillFailure","IncursionCompletedMsg","IndustryTeamAuctionLost","IndustryTeamAuctionWon","InfrastructureHubBillAboutToExpire","InsuranceExpirationMsg","InsuranceFirstShipMsg","InsuranceInvalidatedMsg","InsuranceIssuedMsg","InsurancePayoutMsg","JumpCloneDeletedMsg1","JumpCloneDeletedMsg2","KillReportFinalBlow","KillReportVictim","KillRightAvailable","KillRightAvailableOpen","KillRightEarned","KillRightUnavailable","KillRightUnavailableOpen","KillRightUsed","LocateCharMsg","MadeWarMutual","MercOfferedNegotiationMsg","MissionOfferExpirationMsg","MissionTimeoutMsg","MoonminingAutomaticFracture","MoonminingExtractionCancelled","MoonminingExtractionFinished","MoonminingLaserFired","NPCStandingsGained","NPCStandingsLost","OfferedSurrender","OfferedToAlly","OldLscMessages","OperationFinished","OrbitalAttacked","OrbitalReinforced","OwnershipTransferred","ReimbursementMsg","ResearchMissionAvailableMsg","RetractsWar","SeasonalChallengeCompleted","SovAllClaimAquiredMsg","SovAllClaimLostMsg","SovCommandNodeEventStarted","SovCorpBillLateMsg","SovCorpClaimFailMsg","SovDisruptorMsg","SovStationEnteredFreeport","SovStructureDestroyed","SovStructureReinforced","SovStructureSelfDestructCancel","SovStructureSelfDestructFinished","SovStructureSelfDestructRequested","SovereigntyIHDamageMsg","SovereigntySBUDamageMsg","SovereigntyTCUDamageMsg","StationAggressionMsg1","StationAggressionMsg2","StationConquerMsg","StationServiceDisabled","StationServiceEnabled","StationStateChangeMsg","StoryLineMissionAvailableMsg","StructureAnchoring","StructureCourierContractChanged","StructureDestroyed","StructureFuelAlert","StructureItemsDelivered","StructureLostArmor","StructureLostShields","StructureOnline","StructureServicesOffline","StructureUnanchoring","StructureUnderAttack","TowerAlertMsg","TowerResourceAlertMsg","TransactionReversalMsg","TutorialMsg","WarAllyOfferDeclinedMsg","WarSurrenderDeclinedMsg","WarSurrenderOfferMsg","notificationTypeMoonminingExtractionStarted"],"title":"get_characters_character_id_notifications_type","description":"type string"},"sender_id":{"type":"integer","format":"int32","title":"get_characters_character_id_notifications_sender_id","description":"sender_id integer"},"sender_type":{"type":"string","enum":["character","corporation","alliance","faction","other"],"title":"get_characters_character_id_notifications_sender_type","description":"sender_type string"},"timestamp":{"type":"string","format":"date-time","title":"get_characters_character_id_notifications_timestamp","description":"timestamp string"},"is_read":{"type":"boolean","title":"get_characters_character_id_notifications_is_read","description":"is_read boolean"},"text":{"type":"string","title":"get_characters_character_id_notifications_text","description":"text string"}},"title":"get_characters_character_id_notifications_200_ok","description":"200 ok object"},"title":"get_characters_character_id_notifications_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-characters.read_notifications.v1"]}],"operationId":"get_characters_character_id_notifications","x-cached-seconds":600,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/roles/":{"get":{"description":"Returns a character's corporation roles\n\n---\nAlternate route: `/legacy/characters/{character_id}/roles/`\n\nAlternate route: `/v1/characters/{character_id}/roles/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\n[This route has an available update](https://esi.tech.ccp.is/diff/latest/dev/#GET-/characters/{character_id}/roles/)","summary":"Get character corporation roles","tags":["Character"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"The character's roles in thier corporation","examples":{"application/json":["Director","Station_Manager"]},"schema":{"type":"array","maxItems":50,"uniqueItems":true,"items":{"type":"string","enum":["Account_Take_1","Account_Take_2","Account_Take_3","Account_Take_4","Account_Take_5","Account_Take_6","Account_Take_7","Accountant","Auditor","Communications_Officer","Config_Equipment","Config_Starbase_Equipment","Container_Take_1","Container_Take_2","Container_Take_3","Container_Take_4","Container_Take_5","Container_Take_6","Container_Take_7","Contract_Manager","Diplomat","Director","Factory_Manager","Fitting_Manager","Hangar_Query_1","Hangar_Query_2","Hangar_Query_3","Hangar_Query_4","Hangar_Query_5","Hangar_Query_6","Hangar_Query_7","Hangar_Take_1","Hangar_Take_2","Hangar_Take_3","Hangar_Take_4","Hangar_Take_5","Hangar_Take_6","Hangar_Take_7","Junior_Accountant","Personnel_Manager","Rent_Factory_Facility","Rent_Office","Rent_Research_Facility","Security_Officer","Starbase_Defense_Operator","Starbase_Fuel_Technician","Station_Manager","Terrestrial_Combat_Officer","Terrestrial_Logistics_Officer","Trader"],"title":"get_characters_character_id_roles_200_ok","description":"200 ok string"},"title":"get_characters_character_id_roles_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-characters.read_corporation_roles.v1"]}],"operationId":"get_characters_character_id_roles","x-cached-seconds":3600,"x-alternate-versions":["legacy","v1"]}},"/characters/{character_id}/titles/":{"get":{"description":"Returns a character's titles\n\n---\nAlternate route: `/dev/characters/{character_id}/titles/`\n\nAlternate route: `/legacy/characters/{character_id}/titles/`\n\nAlternate route: `/v1/characters/{character_id}/titles/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get character corporation titles","tags":["Character"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of titles","examples":{"application/json":[{"title_id":1,"name":"Awesome Title"}]},"schema":{"type":"array","maxItems":16,"items":{"type":"object","properties":{"title_id":{"type":"integer","format":"int32","title":"get_characters_character_id_titles_title_id","description":"title_id integer"},"name":{"type":"string","title":"get_characters_character_id_titles_name","description":"name string"}},"title":"get_characters_character_id_titles_200_ok","description":"200 ok object"},"title":"get_characters_character_id_titles_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-characters.read_titles.v1"]}],"operationId":"get_characters_character_id_titles","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/clones/":{"get":{"description":"A list of the character's clones\n\n---\nAlternate route: `/legacy/characters/{character_id}/clones/`\n\nAlternate route: `/v2/characters/{character_id}/clones/`\n\n---\nThis route is cached for up to 120 seconds\n\n---\n[This route has an available update](https://esi.tech.ccp.is/diff/latest/dev/#GET-/characters/{character_id}/clones/)","summary":"Get clones","tags":["Clones"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Clone information for the given character","examples":{"application/json":{"home_location":{"location_id":1021348135816,"location_type":"structure"},"jump_clones":[{"location_id":60003463,"location_type":"station","implants":[22118]},{"location_id":1021348135816,"location_type":"structure","implants":[]}]}},"schema":{"type":"object","required":["jump_clones"],"properties":{"last_jump_date":{"type":"string","format":"date-time","title":"get_characters_character_id_clones_last_jump_date","description":"last_jump_date string"},"home_location":{"type":"object","properties":{"location_id":{"type":"integer","format":"int64","title":"get_characters_character_id_clones_location_id","description":"location_id integer"},"location_type":{"type":"string","enum":["station","structure"],"title":"get_characters_character_id_clones_location_type","description":"location_type string"}},"title":"get_characters_character_id_clones_home_location","description":"home_location object"},"jump_clones":{"type":"array","maxItems":10,"items":{"type":"object","properties":{"location_id":{"type":"integer","format":"int64","title":"get_characters_character_id_clones_location_id","description":"location_id integer"},"location_type":{"type":"string","enum":["station","structure"],"title":"get_characters_character_id_clones_location_type","description":"location_type string"},"implants":{"type":"array","maxItems":11,"items":{"type":"integer","format":"int32","title":"get_characters_character_id_clones_implant","description":"implant integer"},"title":"get_characters_character_id_clones_implants","description":"implants array"}},"title":"get_characters_character_id_clones_jump_clone","description":"jump_clone object"},"title":"get_characters_character_id_clones_jump_clones","description":"jump_clones array"}},"title":"get_characters_character_id_clones_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-clones.read_clones.v1"]}],"operationId":"get_characters_character_id_clones","x-cached-seconds":120,"x-alternate-versions":["legacy","v2"]}},"/characters/{character_id}/implants/":{"get":{"description":"Return implants on the active clone of a character\n\n---\nAlternate route: `/dev/characters/{character_id}/implants/`\n\nAlternate route: `/legacy/characters/{character_id}/implants/`\n\nAlternate route: `/v1/characters/{character_id}/implants/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get active implants","tags":["Clones"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of implant type ids","examples":{"application/json":[1,2,3]},"schema":{"type":"array","maxItems":11,"items":{"type":"integer","format":"int32","title":"get_characters_character_id_implants_200_ok","description":"200 ok integer"},"title":"get_characters_character_id_implants_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-clones.read_implants.v1"]}],"operationId":"get_characters_character_id_implants","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/contacts/":{"delete":{"description":"Bulk delete contacts\n\n---\nAlternate route: `/legacy/characters/{character_id}/contacts/`\n\nAlternate route: `/v1/characters/{character_id}/contacts/`\n\n\n---\n[This route has an available update](https://esi.tech.ccp.is/diff/latest/dev/#DELETE-/characters/{character_id}/contacts/)","summary":"Delete contacts","tags":["Contacts"],"parameters":[{"$ref":"#/parameters/character_id"},{"name":"contact_ids","in":"body","description":"A list of contacts to delete","required":true,"schema":{"type":"array","minItems":1,"maxItems":100,"items":{"type":"integer","format":"int32","description":"ID of the contact to delete","title":"delete_characters_character_id_contacts_contact_id"},"title":"delete_characters_character_id_contacts_contact_ids","description":"contact_ids array"}},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"204":{"description":"Contacts deleted"},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-characters.write_contacts.v1"]}],"operationId":"delete_characters_character_id_contacts","x-alternate-versions":["legacy","v1"]},"get":{"description":"Return contacts of a character\n\n---\nAlternate route: `/dev/characters/{character_id}/contacts/`\n\nAlternate route: `/legacy/characters/{character_id}/contacts/`\n\nAlternate route: `/v1/characters/{character_id}/contacts/`\n\n---\nThis route is cached for up to 300 seconds","summary":"Get contacts","tags":["Contacts"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of contacts","examples":{"application/json":[{"standing":9.9,"contact_type":"character","contact_id":123,"is_watched":true,"is_blocked":true}]},"schema":{"type":"array","maxItems":1024,"items":{"type":"object","required":["standing","contact_type","contact_id"],"properties":{"standing":{"type":"number","format":"float","description":"Standing of the contact","title":"get_characters_character_id_contacts_standing"},"contact_type":{"type":"string","enum":["character","corporation","alliance","faction"],"title":"get_characters_character_id_contacts_contact_type","description":"contact_type string"},"contact_id":{"type":"integer","format":"int32","title":"get_characters_character_id_contacts_contact_id","description":"contact_id integer"},"is_watched":{"type":"boolean","description":"Whether this contact is being watched","title":"get_characters_character_id_contacts_is_watched"},"is_blocked":{"type":"boolean","description":"Whether this contact is in the blocked list. Note a missing value denotes unknown, not true or false","title":"get_characters_character_id_contacts_is_blocked"},"label_id":{"type":"integer","format":"int64","description":"Custom label of the contact","title":"get_characters_character_id_contacts_label_id"}},"title":"get_characters_character_id_contacts_200_ok","description":"200 ok object"},"title":"get_characters_character_id_contacts_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-characters.read_contacts.v1"]}],"operationId":"get_characters_character_id_contacts","x-cached-seconds":300,"x-alternate-versions":["dev","legacy","v1"]},"post":{"description":"Bulk add contacts with same settings\n\n---\nAlternate route: `/dev/characters/{character_id}/contacts/`\n\nAlternate route: `/legacy/characters/{character_id}/contacts/`\n\nAlternate route: `/v1/characters/{character_id}/contacts/`\n","summary":"Add contacts","tags":["Contacts"],"parameters":[{"$ref":"#/parameters/character_id"},{"name":"contact_ids","in":"body","description":"A list of contacts to add","required":true,"schema":{"type":"array","minItems":1,"maxItems":100,"items":{"type":"integer","format":"int32","description":"ID of the contact to add","title":"post_characters_character_id_contacts_contact_id"},"title":"post_characters_character_id_contacts_contact_ids","description":"contact_ids array"}},{"$ref":"#/parameters/datasource"},{"name":"label_id","in":"query","description":"Add a custom label to the new contact","required":false,"type":"integer","format":"int64","default":0},{"name":"standing","in":"query","description":"Standing for the new contact","required":true,"type":"number","format":"float","minimum":-10,"maximum":10},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"name":"watched","in":"query","description":"Whether the new contact should be watched, note this is only effective on characters","required":false,"type":"boolean","default":false},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"201":{"description":"A list of contact ids that successfully created","examples":{"application/json":[123,456]},"schema":{"type":"array","maxItems":100,"items":{"type":"integer","format":"int32","title":"post_characters_character_id_contacts_201_created","description":"201 created integer"},"title":"post_characters_character_id_contacts_created","description":"201 created array"}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-characters.write_contacts.v1"]}],"operationId":"post_characters_character_id_contacts","x-alternate-versions":["dev","legacy","v1"]},"put":{"description":"Bulk edit contacts with same settings\n\n---\nAlternate route: `/dev/characters/{character_id}/contacts/`\n\nAlternate route: `/legacy/characters/{character_id}/contacts/`\n\nAlternate route: `/v1/characters/{character_id}/contacts/`\n","summary":"Edit contacts","tags":["Contacts"],"parameters":[{"$ref":"#/parameters/character_id"},{"name":"contact_ids","in":"body","description":"A list of contacts to edit","required":true,"schema":{"type":"array","minItems":1,"maxItems":100,"items":{"type":"integer","format":"int32","description":"ID of the contact to edit","title":"put_characters_character_id_contacts_contact_id"},"title":"put_characters_character_id_contacts_contact_ids","description":"contact_ids array"}},{"$ref":"#/parameters/datasource"},{"name":"label_id","in":"query","description":"Add a custom label to the contact, use 0 for clearing label","required":false,"type":"integer","format":"int64","default":0},{"name":"standing","in":"query","description":"Standing for the contact","required":true,"type":"number","format":"float","minimum":-10,"maximum":10},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"name":"watched","in":"query","description":"Whether the contact should be watched, note this is only effective on characters","required":false,"type":"boolean","default":false},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"204":{"description":"Contacts updated"},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-characters.write_contacts.v1"]}],"operationId":"put_characters_character_id_contacts","x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/contacts/":{"get":{"description":"Return contacts of a corporation\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/contacts/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/contacts/`\n\nAlternate route: `/v1/corporations/{corporation_id}/contacts/`\n\n---\nThis route is cached for up to 300 seconds","summary":"Get corporation contacts","tags":["Contacts"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of contacts","examples":{"application/json":[{"standing":9.9,"contact_type":"character","contact_id":123,"is_watched":true}]},"schema":{"type":"array","maxItems":200,"items":{"type":"object","required":["standing","contact_type","contact_id"],"properties":{"standing":{"type":"number","format":"float","description":"Standing of the contact","title":"get_corporations_corporation_id_contacts_standing"},"contact_type":{"type":"string","enum":["character","corporation","alliance","faction"],"title":"get_corporations_corporation_id_contacts_contact_type","description":"contact_type string"},"contact_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_contacts_contact_id","description":"contact_id integer"},"is_watched":{"type":"boolean","description":"Whether this contact is being watched","title":"get_corporations_corporation_id_contacts_is_watched"},"label_id":{"type":"integer","format":"int64","description":"Custom label of the contact","title":"get_corporations_corporation_id_contacts_label_id"}},"title":"get_corporations_corporation_id_contacts_200_ok","description":"200 ok object"},"title":"get_corporations_corporation_id_contacts_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":[],"security":[{"evesso":["esi-corporations.read_contacts.v1"]}],"operationId":"get_corporations_corporation_id_contacts","x-cached-seconds":300,"x-alternate-versions":["dev","legacy","v1"]}},"/alliances/{alliance_id}/contacts/":{"get":{"description":"Return contacts of an alliance\n\n---\nAlternate route: `/dev/alliances/{alliance_id}/contacts/`\n\nAlternate route: `/legacy/alliances/{alliance_id}/contacts/`\n\nAlternate route: `/v1/alliances/{alliance_id}/contacts/`\n\n---\nThis route is cached for up to 300 seconds","summary":"Get alliance contacts","tags":["Contacts"],"parameters":[{"$ref":"#/parameters/alliance_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of contacts","examples":{"application/json":[{"standing":9.9,"contact_type":"character","contact_id":2112625428}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["standing","contact_type","contact_id"],"properties":{"standing":{"type":"number","format":"float","description":"Standing of the contact","title":"get_alliances_alliance_id_contacts_standing"},"contact_type":{"type":"string","enum":["character","corporation","alliance","faction"],"title":"get_alliances_alliance_id_contacts_contact_type","description":"contact_type string"},"contact_id":{"type":"integer","format":"int32","title":"get_alliances_alliance_id_contacts_contact_id","description":"contact_id integer"},"label_id":{"type":"integer","format":"int64","description":"Custom label of the contact","title":"get_alliances_alliance_id_contacts_label_id"}},"title":"get_alliances_alliance_id_contacts_200_ok","description":"200 ok object"},"title":"get_alliances_alliance_id_contacts_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-alliances.read_contacts.v1"]}],"operationId":"get_alliances_alliance_id_contacts","x-cached-seconds":300,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/contacts/labels/":{"get":{"description":"Return custom labels for contacts the character defined\n\n---\nAlternate route: `/dev/characters/{character_id}/contacts/labels/`\n\nAlternate route: `/legacy/characters/{character_id}/contacts/labels/`\n\nAlternate route: `/v1/characters/{character_id}/contacts/labels/`\n\n---\nThis route is cached for up to 300 seconds","summary":"Get contact labels","tags":["Contacts"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of contact labels","examples":{"application/json":[{"label_id":123,"label_name":"Friends"}]},"schema":{"type":"array","maxItems":64,"items":{"type":"object","required":["label_id","label_name"],"properties":{"label_id":{"type":"integer","format":"int64","title":"get_characters_character_id_contacts_labels_label_id","description":"label_id integer"},"label_name":{"type":"string","title":"get_characters_character_id_contacts_labels_label_name","description":"label_name string"}},"title":"get_characters_character_id_contacts_labels_200_ok","description":"200 ok object"},"title":"get_characters_character_id_contacts_labels_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-characters.read_contacts.v1"]}],"operationId":"get_characters_character_id_contacts_labels","x-cached-seconds":300,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/contracts/":{"get":{"description":"Returns contracts available to a character, only if the character is issuer, acceptor or assignee. Only returns contracts no older than 30 days, or if the status is \"in_progress\".\n\n---\nAlternate route: `/dev/characters/{character_id}/contracts/`\n\nAlternate route: `/legacy/characters/{character_id}/contracts/`\n\nAlternate route: `/v1/characters/{character_id}/contracts/`\n\n---\nThis route is cached for up to 300 seconds","summary":"Get contracts","tags":["Contracts"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of contracts","examples":{"application/json":[{"contract_id":1,"issuer_id":123,"issuer_corporation_id":456,"assignee_id":0,"acceptor_id":0,"type":"auction","status":"outstanding","for_corporation":true,"availability":"public","date_issued":"2017-06-06T13:12:32Z","date_expired":"2017-06-13T13:12:32Z","start_location_id":60014719,"end_location_id":60014719,"date_accepted":"2017-06-06T13:12:32Z","days_to_complete":0,"date_completed":"2017-06-06T13:12:32Z","price":1000000.01,"reward":0.01,"buyout":10000000000.01,"volume":0.01}]},"schema":{"type":"array","maxItems":10000,"items":{"type":"object","required":["contract_id","issuer_id","issuer_corporation_id","assignee_id","acceptor_id","type","status","for_corporation","availability","date_issued","date_expired"],"properties":{"contract_id":{"type":"integer","format":"int32","title":"get_characters_character_id_contracts_contract_id","description":"contract_id integer"},"issuer_id":{"type":"integer","format":"int32","description":"Character ID for the issuer","title":"get_characters_character_id_contracts_issuer_id"},"issuer_corporation_id":{"type":"integer","format":"int32","description":"Character's corporation ID for the issuer","title":"get_characters_character_id_contracts_issuer_corporation_id"},"assignee_id":{"type":"integer","format":"int32","description":"ID to whom the contract is assigned, can be corporation or character ID","title":"get_characters_character_id_contracts_assignee_id"},"acceptor_id":{"type":"integer","format":"int32","description":"Who will accept the contract","title":"get_characters_character_id_contracts_acceptor_id"},"start_location_id":{"type":"integer","format":"int64","description":"Start location ID (for Couriers contract)","title":"get_characters_character_id_contracts_start_location_id"},"end_location_id":{"type":"integer","format":"int64","description":"End location ID (for Couriers contract)","title":"get_characters_character_id_contracts_end_location_id"},"type":{"type":"string","description":"Type of the contract","enum":["unknown","item_exchange","auction","courier","loan"],"title":"get_characters_character_id_contracts_type"},"status":{"type":"string","description":"Status of the the contract","enum":["outstanding","in_progress","finished_issuer","finished_contractor","finished","cancelled","rejected","failed","deleted","reversed"],"title":"get_characters_character_id_contracts_status"},"title":{"type":"string","description":"Title of the contract","title":"get_characters_character_id_contracts_title"},"for_corporation":{"type":"boolean","description":"true if the contract was issued on behalf of the issuer's corporation","title":"get_characters_character_id_contracts_for_corporation"},"availability":{"type":"string","description":"To whom the contract is available","enum":["public","personal","corporation","alliance"],"title":"get_characters_character_id_contracts_availability"},"date_issued":{"type":"string","format":"date-time","description":"\u0421reation date of the contract","title":"get_characters_character_id_contracts_date_issued"},"date_expired":{"type":"string","format":"date-time","description":"Expiration date of the contract","title":"get_characters_character_id_contracts_date_expired"},"date_accepted":{"type":"string","format":"date-time","description":"Date of confirmation of contract","title":"get_characters_character_id_contracts_date_accepted"},"days_to_complete":{"type":"integer","format":"int32","description":"Number of days to perform the contract","title":"get_characters_character_id_contracts_days_to_complete"},"date_completed":{"type":"string","format":"date-time","description":"Date of completed of contract","title":"get_characters_character_id_contracts_date_completed"},"price":{"type":"number","format":"double","description":"Price of contract (for ItemsExchange and Auctions)","title":"get_characters_character_id_contracts_price"},"reward":{"type":"number","format":"double","description":"Remuneration for contract (for Couriers only)","title":"get_characters_character_id_contracts_reward"},"collateral":{"type":"number","format":"double","description":"Collateral price (for Couriers only)","title":"get_characters_character_id_contracts_collateral"},"buyout":{"type":"number","format":"double","description":"Buyout price (for Auctions only)","title":"get_characters_character_id_contracts_buyout"},"volume":{"type":"number","format":"float","description":"Volume of items in the contract","title":"get_characters_character_id_contracts_volume"}},"title":"get_characters_character_id_contracts_200_ok","description":"200 ok object"},"title":"get_characters_character_id_contracts_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-contracts.read_character_contracts.v1"]}],"operationId":"get_characters_character_id_contracts","x-cached-seconds":300,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/contracts/{contract_id}/items/":{"get":{"description":"Lists items of a particular contract\n\n---\nAlternate route: `/dev/characters/{character_id}/contracts/{contract_id}/items/`\n\nAlternate route: `/legacy/characters/{character_id}/contracts/{contract_id}/items/`\n\nAlternate route: `/v1/characters/{character_id}/contracts/{contract_id}/items/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get contract items","tags":["Contracts"],"parameters":[{"$ref":"#/parameters/character_id"},{"name":"contract_id","in":"path","description":"ID of a contract","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of items in this contract","examples":{"application/json":[{"record_id":123456,"type_id":587,"quantity":1,"is_singleton":false,"is_included":true}]},"schema":{"type":"array","maxItems":2000,"items":{"type":"object","required":["record_id","type_id","quantity","is_singleton","is_included"],"properties":{"record_id":{"type":"integer","format":"int64","description":"Unique ID for the item","title":"get_characters_character_id_contracts_contract_id_items_record_id"},"type_id":{"type":"integer","format":"int32","description":"Type ID for item","title":"get_characters_character_id_contracts_contract_id_items_type_id"},"quantity":{"type":"integer","format":"int32","description":"Number of items in the stack","title":"get_characters_character_id_contracts_contract_id_items_quantity"},"raw_quantity":{"type":"integer","format":"int32","description":"-1 indicates that the item is a singleton (non-stackable). If the item happens to be a Blueprint, -1 is an Original and -2 is a Blueprint Copy","title":"get_characters_character_id_contracts_contract_id_items_raw_quantity"},"is_singleton":{"type":"boolean","title":"get_characters_character_id_contracts_contract_id_items_is_singleton","description":"is_singleton boolean"},"is_included":{"type":"boolean","description":"true if the contract issuer has submitted this item with the contract, false if the isser is asking for this item in the contract.","title":"get_characters_character_id_contracts_contract_id_items_is_included"}},"title":"get_characters_character_id_contracts_contract_id_items_200_ok","description":"200 ok object"},"title":"get_characters_character_id_contracts_contract_id_items_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-contracts.read_character_contracts.v1"]}],"operationId":"get_characters_character_id_contracts_contract_id_items","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/contracts/{contract_id}/bids/":{"get":{"description":"Lists bids on a particular auction contract\n\n---\nAlternate route: `/dev/characters/{character_id}/contracts/{contract_id}/bids/`\n\nAlternate route: `/legacy/characters/{character_id}/contracts/{contract_id}/bids/`\n\nAlternate route: `/v1/characters/{character_id}/contracts/{contract_id}/bids/`\n\n---\nThis route is cached for up to 300 seconds","summary":"Get contract bids","tags":["Contracts"],"parameters":[{"$ref":"#/parameters/character_id"},{"name":"contract_id","in":"path","description":"ID of a contract","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of bids","examples":{"application/json":[{"bid_id":1,"bidder_id":123,"date_bid":"2017-01-01T10:10:10Z","amount":1.23}]},"schema":{"type":"array","maxItems":2000,"items":{"type":"object","required":["bid_id","bidder_id","date_bid","amount"],"properties":{"bid_id":{"type":"integer","format":"int32","description":"Unique ID for the bid","title":"get_characters_character_id_contracts_contract_id_bids_bid_id"},"bidder_id":{"type":"integer","format":"int32","description":"Character ID of the bidder","title":"get_characters_character_id_contracts_contract_id_bids_bidder_id"},"date_bid":{"type":"string","format":"date-time","description":"Datetime when the bid was placed","title":"get_characters_character_id_contracts_contract_id_bids_date_bid"},"amount":{"type":"number","format":"float","description":"The amount bid, in ISK","title":"get_characters_character_id_contracts_contract_id_bids_amount"}},"title":"get_characters_character_id_contracts_contract_id_bids_200_ok","description":"200 ok object"},"title":"get_characters_character_id_contracts_contract_id_bids_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-contracts.read_character_contracts.v1"]}],"operationId":"get_characters_character_id_contracts_contract_id_bids","x-cached-seconds":300,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/contracts/":{"get":{"description":"Returns contracts available to a coporation, only if the corporation is issuer, acceptor or assignee. Only returns contracts no older than 30 days, or if the status is \"in_progress\".\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/contracts/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/contracts/`\n\nAlternate route: `/v1/corporations/{corporation_id}/contracts/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get coporation contracts","tags":["Contracts"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of contracts","examples":{"application/json":[{"contract_id":1,"issuer_id":123,"issuer_corporation_id":456,"assignee_id":0,"acceptor_id":0,"type":"auction","status":"outstanding","for_corporation":true,"availability":"public","date_issued":"2017-06-06T13:12:32Z","date_expired":"2017-06-13T13:12:32Z","start_location_id":60014719,"end_location_id":60014719,"date_accepted":"2017-06-06T13:12:32Z","days_to_complete":0,"date_completed":"2017-06-06T13:12:32Z","price":1000000.01,"reward":0.01,"buyout":10000000000.01,"volume":0.01}]},"schema":{"type":"array","maxItems":10000,"items":{"type":"object","required":["contract_id","issuer_id","issuer_corporation_id","assignee_id","acceptor_id","type","status","for_corporation","availability","date_issued","date_expired"],"properties":{"contract_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_contracts_contract_id","description":"contract_id integer"},"issuer_id":{"type":"integer","format":"int32","description":"Character ID for the issuer","title":"get_corporations_corporation_id_contracts_issuer_id"},"issuer_corporation_id":{"type":"integer","format":"int32","description":"Character's corporation ID for the issuer","title":"get_corporations_corporation_id_contracts_issuer_corporation_id"},"assignee_id":{"type":"integer","format":"int32","description":"ID to whom the contract is assigned, can be corporation or character ID","title":"get_corporations_corporation_id_contracts_assignee_id"},"acceptor_id":{"type":"integer","format":"int32","description":"Who will accept the contract","title":"get_corporations_corporation_id_contracts_acceptor_id"},"start_location_id":{"type":"integer","format":"int64","description":"Start location ID (for Couriers contract)","title":"get_corporations_corporation_id_contracts_start_location_id"},"end_location_id":{"type":"integer","format":"int64","description":"End location ID (for Couriers contract)","title":"get_corporations_corporation_id_contracts_end_location_id"},"type":{"type":"string","description":"Type of the contract","enum":["unknown","item_exchange","auction","courier","loan"],"title":"get_corporations_corporation_id_contracts_type"},"status":{"type":"string","description":"Status of the the contract","enum":["outstanding","in_progress","finished_issuer","finished_contractor","finished","cancelled","rejected","failed","deleted","reversed"],"title":"get_corporations_corporation_id_contracts_status"},"title":{"type":"string","description":"Title of the contract","title":"get_corporations_corporation_id_contracts_title"},"for_corporation":{"type":"boolean","description":"true if the contract was issued on behalf of the issuer's corporation","title":"get_corporations_corporation_id_contracts_for_corporation"},"availability":{"type":"string","description":"To whom the contract is available","enum":["public","personal","corporation","alliance"],"title":"get_corporations_corporation_id_contracts_availability"},"date_issued":{"type":"string","format":"date-time","description":"\u0421reation date of the contract","title":"get_corporations_corporation_id_contracts_date_issued"},"date_expired":{"type":"string","format":"date-time","description":"Expiration date of the contract","title":"get_corporations_corporation_id_contracts_date_expired"},"date_accepted":{"type":"string","format":"date-time","description":"Date of confirmation of contract","title":"get_corporations_corporation_id_contracts_date_accepted"},"days_to_complete":{"type":"integer","format":"int32","description":"Number of days to perform the contract","title":"get_corporations_corporation_id_contracts_days_to_complete"},"date_completed":{"type":"string","format":"date-time","description":"Date of completed of contract","title":"get_corporations_corporation_id_contracts_date_completed"},"price":{"type":"number","format":"double","description":"Price of contract (for ItemsExchange and Auctions)","title":"get_corporations_corporation_id_contracts_price"},"reward":{"type":"number","format":"double","description":"Remuneration for contract (for Couriers only)","title":"get_corporations_corporation_id_contracts_reward"},"collateral":{"type":"number","format":"double","description":"Collateral price (for Couriers only)","title":"get_corporations_corporation_id_contracts_collateral"},"buyout":{"type":"number","format":"double","description":"Buyout price (for Auctions only)","title":"get_corporations_corporation_id_contracts_buyout"},"volume":{"type":"number","format":"float","description":"Volume of items in the contract","title":"get_corporations_corporation_id_contracts_volume"}},"title":"get_corporations_corporation_id_contracts_200_ok","description":"200 ok object"},"title":"get_corporations_corporation_id_contracts_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":[],"security":[{"evesso":["esi-contracts.read_corporation_contracts.v1"]}],"operationId":"get_corporations_corporation_id_contracts","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/contracts/{contract_id}/items/":{"get":{"description":"Lists items of a particular contract\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/contracts/{contract_id}/items/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/contracts/{contract_id}/items/`\n\nAlternate route: `/v1/corporations/{corporation_id}/contracts/{contract_id}/items/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get corporation contract items","tags":["Contracts"],"parameters":[{"name":"contract_id","in":"path","description":"ID of a contract","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of items in this contract","examples":{"application/json":[{"record_id":123456,"type_id":587,"quantity":1,"is_singleton":false,"is_included":true}]},"schema":{"type":"array","maxItems":2000,"items":{"type":"object","required":["record_id","type_id","quantity","is_singleton","is_included"],"properties":{"record_id":{"type":"integer","format":"int64","description":"Unique ID for the item","title":"get_corporations_corporation_id_contracts_contract_id_items_record_id"},"type_id":{"type":"integer","format":"int32","description":"Type ID for item","title":"get_corporations_corporation_id_contracts_contract_id_items_type_id"},"quantity":{"type":"integer","format":"int32","description":"Number of items in the stack","title":"get_corporations_corporation_id_contracts_contract_id_items_quantity"},"raw_quantity":{"type":"integer","format":"int32","description":"-1 indicates that the item is a singleton (non-stackable). If the item happens to be a Blueprint, -1 is an Original and -2 is a Blueprint Copy","title":"get_corporations_corporation_id_contracts_contract_id_items_raw_quantity"},"is_singleton":{"type":"boolean","title":"get_corporations_corporation_id_contracts_contract_id_items_is_singleton","description":"is_singleton boolean"},"is_included":{"type":"boolean","description":"true if the contract issuer has submitted this item with the contract, false if the isser is asking for this item in the contract.","title":"get_corporations_corporation_id_contracts_contract_id_items_is_included"}},"title":"get_corporations_corporation_id_contracts_contract_id_items_200_ok","description":"200 ok object"},"title":"get_corporations_corporation_id_contracts_contract_id_items_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":[],"security":[{"evesso":["esi-contracts.read_corporation_contracts.v1"]}],"operationId":"get_corporations_corporation_id_contracts_contract_id_items","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/contracts/{contract_id}/bids/":{"get":{"description":"Lists bids on a particular auction contract\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/contracts/{contract_id}/bids/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/contracts/{contract_id}/bids/`\n\nAlternate route: `/v1/corporations/{corporation_id}/contracts/{contract_id}/bids/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get corporation contract bids","tags":["Contracts"],"parameters":[{"name":"contract_id","in":"path","description":"ID of a contract","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of bids","examples":{"application/json":[{"bid_id":1,"bidder_id":123,"date_bid":"2017-01-01T10:10:10Z","amount":1.23}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["bid_id","bidder_id","date_bid","amount"],"properties":{"bid_id":{"type":"integer","format":"int32","description":"Unique ID for the bid","title":"get_corporations_corporation_id_contracts_contract_id_bids_bid_id"},"bidder_id":{"type":"integer","format":"int32","description":"Character ID of the bidder","title":"get_corporations_corporation_id_contracts_contract_id_bids_bidder_id"},"date_bid":{"type":"string","format":"date-time","description":"Datetime when the bid was placed","title":"get_corporations_corporation_id_contracts_contract_id_bids_date_bid"},"amount":{"type":"number","format":"float","description":"The amount bid, in ISK","title":"get_corporations_corporation_id_contracts_contract_id_bids_amount"}},"title":"get_corporations_corporation_id_contracts_contract_id_bids_200_ok","description":"200 ok object"},"title":"get_corporations_corporation_id_contracts_contract_id_bids_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":[],"security":[{"evesso":["esi-contracts.read_corporation_contracts.v1"]}],"operationId":"get_corporations_corporation_id_contracts_contract_id_bids","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/shareholders/":{"get":{"description":"Return the current member list of a corporation, the token's character need to be a member of the corporation.\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/shareholders/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/shareholders/`\n\nAlternate route: `/v1/corporations/{corporation_id}/shareholders/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get corporation members","tags":["Corporation"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"List of member character IDs","examples":{"application/json":[{"shareholder_id":98000001,"shareholder_type":"corporation","share_count":580},{"shareholder_id":2112000003,"shareholder_type":"character","share_count":20},{"shareholder_id":2112000004,"shareholder_type":"character","share_count":300},{"shareholder_id":2112000001,"shareholder_type":"character","share_count":100}]},"schema":{"type":"array","maxItems":1000,"description":"A list of character IDs","items":{"type":"object","required":["shareholder_id","shareholder_type","share_count"],"properties":{"shareholder_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_shareholders_shareholder_id","description":"shareholder_id integer"},"shareholder_type":{"type":"string","enum":["character","corporation"],"title":"get_corporations_corporation_id_shareholders_shareholder_type","description":"shareholder_type string"},"share_count":{"type":"integer","format":"int64","title":"get_corporations_corporation_id_shareholders_share_count","description":"share_count integer"}},"title":"get_corporations_corporation_id_shareholders_200_ok","description":"200 ok object"},"title":"get_corporations_corporation_id_shareholders_ok"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-wallet.read_corporation_wallets.v1"]}],"operationId":"get_corporations_corporation_id_shareholders","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/":{"get":{"description":"Public information about a corporation\n\n---\nAlternate route: `/v3/corporations/{corporation_id}/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\n[This route has an available update](https://esi.tech.ccp.is/diff/latest/dev/#GET-/corporations/{corporation_id}/)","summary":"Get corporation information","tags":["Corporation"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Public data about a corporation","examples":{"application/json":{"corporation_name":"C C P","ticker":"-CCP-","member_count":656,"ceo_id":180548812,"alliance_id":434243723,"corporation_description":"This is a corporation description, it's basically just a string","tax_rate":0.256,"creation_date":"2004-11-28T16:42:51Z","creator_id":180548812,"url":"http://www.eveonline.com"}},"schema":{"type":"object","required":["corporation_name","ticker","member_count","ceo_id","corporation_description","tax_rate","creator_id","url"],"properties":{"corporation_name":{"type":"string","description":"the full name of the corporation","title":"get_corporations_corporation_id_corporation_name"},"ticker":{"type":"string","description":"the short name of the corporation","title":"get_corporations_corporation_id_ticker"},"member_count":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_member_count","description":"member_count integer"},"ceo_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_ceo_id","description":"ceo_id integer"},"alliance_id":{"type":"integer","format":"int32","description":"id of alliance that corporation is a member of, if any","title":"get_corporations_corporation_id_alliance_id"},"corporation_description":{"type":"string","title":"get_corporations_corporation_id_corporation_description","description":"corporation_description string"},"tax_rate":{"type":"number","format":"float","minimum":0,"maximum":1,"title":"get_corporations_corporation_id_tax_rate","description":"tax_rate number"},"creation_date":{"type":"string","format":"date-time","title":"get_corporations_corporation_id_creation_date","description":"creation_date string"},"creator_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_creator_id","description":"creator_id integer"},"url":{"type":"string","title":"get_corporations_corporation_id_url","description":"url string"},"faction":{"type":"string","enum":["Minmatar","Gallente","Caldari","Amarr"],"title":"get_corporations_corporation_id_faction","description":"faction string"}},"title":"get_corporations_corporation_id_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"Corporation not found","schema":{"type":"object","title":"get_corporations_corporation_id_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"get_corporations_corporation_id_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_corporations_corporation_id","x-cached-seconds":3600,"x-alternate-versions":["v3"]}},"/corporations/{corporation_id}/alliancehistory/":{"get":{"description":"Get a list of all the alliances a corporation has been a member of\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/alliancehistory/`\n\nAlternate route: `/v2/corporations/{corporation_id}/alliancehistory/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get alliance history","tags":["Corporation"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Alliance history for the given corporation","examples":{"application/json":[{"record_id":23,"start_date":"2016-10-25T14:46:00Z","alliance_id":99000006,"is_deleted":true},{"record_id":1,"start_date":"2015-07-06T20:56:00Z"}]},"schema":{"type":"array","maxItems":10000,"items":{"type":"object","required":["start_date","record_id"],"properties":{"start_date":{"type":"string","format":"date-time","title":"get_corporations_corporation_id_alliancehistory_start_date","description":"start_date string"},"alliance_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_alliancehistory_alliance_id","description":"alliance_id integer"},"is_deleted":{"type":"boolean","description":"True if the alliance has been closed","title":"get_corporations_corporation_id_alliancehistory_is_deleted"},"record_id":{"type":"integer","format":"int32","description":"An incrementing ID that can be used to canonically establish order of records in cases where dates may be ambiguous","title":"get_corporations_corporation_id_alliancehistory_record_id"}},"title":"get_corporations_corporation_id_alliancehistory_200_ok","description":"200 ok object"},"title":"get_corporations_corporation_id_alliancehistory_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_corporations_corporation_id_alliancehistory","x-cached-seconds":3600,"x-alternate-versions":["dev","v2"]}},"/corporations/names/":{"get":{"description":"Resolve a set of corporation IDs to corporation names\n\n---\nAlternate route: `/legacy/corporations/names/`\n\nAlternate route: `/v1/corporations/names/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\n[This route has an available update](https://esi.tech.ccp.is/diff/latest/dev/#GET-/corporations/names/)","summary":"Get corporation names","tags":["Corporation"],"parameters":[{"name":"corporation_ids","in":"query","description":"A comma separated list of corporation IDs","required":true,"type":"array","minItems":1,"maxItems":100,"items":{"type":"integer","format":"int64"}},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"List of id/name associations","examples":{"application/json":[{"corporation_id":1000171,"corporation_name":"Republic University"}]},"schema":{"type":"array","maxItems":100,"items":{"type":"object","required":["corporation_id","corporation_name"],"properties":{"corporation_id":{"type":"integer","format":"int32","title":"get_corporations_names_corporation_id","description":"corporation_id integer"},"corporation_name":{"type":"string","title":"get_corporations_names_corporation_name","description":"corporation_name string"}},"title":"get_corporations_names_200_ok","description":"200 ok object"},"title":"get_corporations_names_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_corporations_names","x-cached-seconds":3600,"x-alternate-versions":["legacy","v1"]}},"/corporations/{corporation_id}/members/":{"get":{"description":"Read the current list of members if the calling character is a member.\n\n---\nAlternate route: `/legacy/corporations/{corporation_id}/members/`\n\nAlternate route: `/v2/corporations/{corporation_id}/members/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\n[This route has an available update](https://esi.tech.ccp.is/diff/latest/dev/#GET-/corporations/{corporation_id}/members/)","summary":"Get corporation members","tags":["Corporation"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"List of member character IDs","examples":{"application/json":[{"character_id":90000001},{"character_id":90000002}]},"schema":{"type":"array","maxItems":12601,"items":{"type":"object","required":["character_id"],"properties":{"character_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_members_character_id","description":"character_id integer"}},"title":"get_corporations_corporation_id_members_200_ok","description":"200 ok object"},"title":"get_corporations_corporation_id_members_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-corporations.read_corporation_membership.v1"]}],"operationId":"get_corporations_corporation_id_members","x-cached-seconds":3600,"x-alternate-versions":["legacy","v2"]}},"/corporations/{corporation_id}/roles/":{"get":{"description":"Return the roles of all members if the character has the personnel manager role or any grantable role.\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/roles/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/roles/`\n\nAlternate route: `/v1/corporations/{corporation_id}/roles/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get corporation member roles","tags":["Corporation"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"List of member character ID's and roles","examples":{"application/json":[{"character_id":1000171,"roles":["Director","Station_Manager"]}]},"schema":{"type":"array","maxItems":12601,"items":{"type":"object","required":["character_id"],"properties":{"character_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_roles_character_id","description":"character_id integer"},"roles":{"type":"array","maxItems":50,"items":{"type":"string","uniqueItems":true,"enum":["Account_Take_1","Account_Take_2","Account_Take_3","Account_Take_4","Account_Take_5","Account_Take_6","Account_Take_7","Accountant","Auditor","Communications_Officer","Config_Equipment","Config_Starbase_Equipment","Container_Take_1","Container_Take_2","Container_Take_3","Container_Take_4","Container_Take_5","Container_Take_6","Container_Take_7","Contract_Manager","Diplomat","Director","Factory_Manager","Fitting_Manager","Hangar_Query_1","Hangar_Query_2","Hangar_Query_3","Hangar_Query_4","Hangar_Query_5","Hangar_Query_6","Hangar_Query_7","Hangar_Take_1","Hangar_Take_2","Hangar_Take_3","Hangar_Take_4","Hangar_Take_5","Hangar_Take_6","Hangar_Take_7","Junior_Accountant","Personnel_Manager","Rent_Factory_Facility","Rent_Office","Rent_Research_Facility","Security_Officer","Starbase_Defense_Operator","Starbase_Fuel_Technician","Station_Manager","Terrestrial_Combat_Officer","Terrestrial_Logistics_Officer","Trader"],"title":"get_corporations_corporation_id_roles_role","description":"role string"},"title":"get_corporations_corporation_id_roles_roles","description":"roles array"},"grantable_roles":{"type":"array","maxItems":50,"items":{"type":"string","uniqueItems":true,"enum":["Account_Take_1","Account_Take_2","Account_Take_3","Account_Take_4","Account_Take_5","Account_Take_6","Account_Take_7","Accountant","Auditor","Communications_Officer","Config_Equipment","Config_Starbase_Equipment","Container_Take_1","Container_Take_2","Container_Take_3","Container_Take_4","Container_Take_5","Container_Take_6","Container_Take_7","Contract_Manager","Diplomat","Director","Factory_Manager","Fitting_Manager","Hangar_Query_1","Hangar_Query_2","Hangar_Query_3","Hangar_Query_4","Hangar_Query_5","Hangar_Query_6","Hangar_Query_7","Hangar_Take_1","Hangar_Take_2","Hangar_Take_3","Hangar_Take_4","Hangar_Take_5","Hangar_Take_6","Hangar_Take_7","Junior_Accountant","Personnel_Manager","Rent_Factory_Facility","Rent_Office","Rent_Research_Facility","Security_Officer","Starbase_Defense_Operator","Starbase_Fuel_Technician","Station_Manager","Terrestrial_Combat_Officer","Terrestrial_Logistics_Officer","Trader"],"title":"get_corporations_corporation_id_roles_grantable_role","description":"grantable_role string"},"title":"get_corporations_corporation_id_roles_grantable_roles","description":"grantable_roles array"},"roles_at_hq":{"type":"array","maxItems":50,"items":{"type":"string","uniqueItems":true,"enum":["Account_Take_1","Account_Take_2","Account_Take_3","Account_Take_4","Account_Take_5","Account_Take_6","Account_Take_7","Accountant","Auditor","Communications_Officer","Config_Equipment","Config_Starbase_Equipment","Container_Take_1","Container_Take_2","Container_Take_3","Container_Take_4","Container_Take_5","Container_Take_6","Container_Take_7","Contract_Manager","Diplomat","Director","Factory_Manager","Fitting_Manager","Hangar_Query_1","Hangar_Query_2","Hangar_Query_3","Hangar_Query_4","Hangar_Query_5","Hangar_Query_6","Hangar_Query_7","Hangar_Take_1","Hangar_Take_2","Hangar_Take_3","Hangar_Take_4","Hangar_Take_5","Hangar_Take_6","Hangar_Take_7","Junior_Accountant","Personnel_Manager","Rent_Factory_Facility","Rent_Office","Rent_Research_Facility","Security_Officer","Starbase_Defense_Operator","Starbase_Fuel_Technician","Station_Manager","Terrestrial_Combat_Officer","Terrestrial_Logistics_Officer","Trader"],"title":"get_corporations_corporation_id_roles_roles_at_hq","description":"roles_at_hq string"},"title":"get_corporations_corporation_id_roles_roles_at_hq","description":"roles_at_hq array"},"grantable_roles_at_hq":{"type":"array","maxItems":50,"items":{"type":"string","uniqueItems":true,"enum":["Account_Take_1","Account_Take_2","Account_Take_3","Account_Take_4","Account_Take_5","Account_Take_6","Account_Take_7","Accountant","Auditor","Communications_Officer","Config_Equipment","Config_Starbase_Equipment","Container_Take_1","Container_Take_2","Container_Take_3","Container_Take_4","Container_Take_5","Container_Take_6","Container_Take_7","Contract_Manager","Diplomat","Director","Factory_Manager","Fitting_Manager","Hangar_Query_1","Hangar_Query_2","Hangar_Query_3","Hangar_Query_4","Hangar_Query_5","Hangar_Query_6","Hangar_Query_7","Hangar_Take_1","Hangar_Take_2","Hangar_Take_3","Hangar_Take_4","Hangar_Take_5","Hangar_Take_6","Hangar_Take_7","Junior_Accountant","Personnel_Manager","Rent_Factory_Facility","Rent_Office","Rent_Research_Facility","Security_Officer","Starbase_Defense_Operator","Starbase_Fuel_Technician","Station_Manager","Terrestrial_Combat_Officer","Terrestrial_Logistics_Officer","Trader"],"title":"get_corporations_corporation_id_roles_grantable_roles_at_hq","description":"grantable_roles_at_hq string"},"title":"get_corporations_corporation_id_roles_grantable_roles_at_hq","description":"grantable_roles_at_hq array"},"roles_at_base":{"type":"array","maxItems":50,"items":{"type":"string","uniqueItems":true,"enum":["Account_Take_1","Account_Take_2","Account_Take_3","Account_Take_4","Account_Take_5","Account_Take_6","Account_Take_7","Accountant","Auditor","Communications_Officer","Config_Equipment","Config_Starbase_Equipment","Container_Take_1","Container_Take_2","Container_Take_3","Container_Take_4","Container_Take_5","Container_Take_6","Container_Take_7","Contract_Manager","Diplomat","Director","Factory_Manager","Fitting_Manager","Hangar_Query_1","Hangar_Query_2","Hangar_Query_3","Hangar_Query_4","Hangar_Query_5","Hangar_Query_6","Hangar_Query_7","Hangar_Take_1","Hangar_Take_2","Hangar_Take_3","Hangar_Take_4","Hangar_Take_5","Hangar_Take_6","Hangar_Take_7","Junior_Accountant","Personnel_Manager","Rent_Factory_Facility","Rent_Office","Rent_Research_Facility","Security_Officer","Starbase_Defense_Operator","Starbase_Fuel_Technician","Station_Manager","Terrestrial_Combat_Officer","Terrestrial_Logistics_Officer","Trader"],"title":"get_corporations_corporation_id_roles_roles_at_base","description":"roles_at_base string"},"title":"get_corporations_corporation_id_roles_roles_at_base","description":"roles_at_base array"},"grantable_roles_at_base":{"type":"array","maxItems":50,"items":{"type":"string","uniqueItems":true,"enum":["Account_Take_1","Account_Take_2","Account_Take_3","Account_Take_4","Account_Take_5","Account_Take_6","Account_Take_7","Accountant","Auditor","Communications_Officer","Config_Equipment","Config_Starbase_Equipment","Container_Take_1","Container_Take_2","Container_Take_3","Container_Take_4","Container_Take_5","Container_Take_6","Container_Take_7","Contract_Manager","Diplomat","Director","Factory_Manager","Fitting_Manager","Hangar_Query_1","Hangar_Query_2","Hangar_Query_3","Hangar_Query_4","Hangar_Query_5","Hangar_Query_6","Hangar_Query_7","Hangar_Take_1","Hangar_Take_2","Hangar_Take_3","Hangar_Take_4","Hangar_Take_5","Hangar_Take_6","Hangar_Take_7","Junior_Accountant","Personnel_Manager","Rent_Factory_Facility","Rent_Office","Rent_Research_Facility","Security_Officer","Starbase_Defense_Operator","Starbase_Fuel_Technician","Station_Manager","Terrestrial_Combat_Officer","Terrestrial_Logistics_Officer","Trader"],"title":"get_corporations_corporation_id_roles_grantable_roles_at_base","description":"grantable_roles_at_base string"},"title":"get_corporations_corporation_id_roles_grantable_roles_at_base","description":"grantable_roles_at_base array"},"roles_at_other":{"type":"array","maxItems":50,"items":{"type":"string","uniqueItems":true,"enum":["Account_Take_1","Account_Take_2","Account_Take_3","Account_Take_4","Account_Take_5","Account_Take_6","Account_Take_7","Accountant","Auditor","Communications_Officer","Config_Equipment","Config_Starbase_Equipment","Container_Take_1","Container_Take_2","Container_Take_3","Container_Take_4","Container_Take_5","Container_Take_6","Container_Take_7","Contract_Manager","Diplomat","Director","Factory_Manager","Fitting_Manager","Hangar_Query_1","Hangar_Query_2","Hangar_Query_3","Hangar_Query_4","Hangar_Query_5","Hangar_Query_6","Hangar_Query_7","Hangar_Take_1","Hangar_Take_2","Hangar_Take_3","Hangar_Take_4","Hangar_Take_5","Hangar_Take_6","Hangar_Take_7","Junior_Accountant","Personnel_Manager","Rent_Factory_Facility","Rent_Office","Rent_Research_Facility","Security_Officer","Starbase_Defense_Operator","Starbase_Fuel_Technician","Station_Manager","Terrestrial_Combat_Officer","Terrestrial_Logistics_Officer","Trader"],"title":"get_corporations_corporation_id_roles_roles_at_other","description":"roles_at_other string"},"title":"get_corporations_corporation_id_roles_roles_at_other","description":"roles_at_other array"},"grantable_roles_at_other":{"type":"array","maxItems":50,"items":{"type":"string","uniqueItems":true,"enum":["Account_Take_1","Account_Take_2","Account_Take_3","Account_Take_4","Account_Take_5","Account_Take_6","Account_Take_7","Accountant","Auditor","Communications_Officer","Config_Equipment","Config_Starbase_Equipment","Container_Take_1","Container_Take_2","Container_Take_3","Container_Take_4","Container_Take_5","Container_Take_6","Container_Take_7","Contract_Manager","Diplomat","Director","Factory_Manager","Fitting_Manager","Hangar_Query_1","Hangar_Query_2","Hangar_Query_3","Hangar_Query_4","Hangar_Query_5","Hangar_Query_6","Hangar_Query_7","Hangar_Take_1","Hangar_Take_2","Hangar_Take_3","Hangar_Take_4","Hangar_Take_5","Hangar_Take_6","Hangar_Take_7","Junior_Accountant","Personnel_Manager","Rent_Factory_Facility","Rent_Office","Rent_Research_Facility","Security_Officer","Starbase_Defense_Operator","Starbase_Fuel_Technician","Station_Manager","Terrestrial_Combat_Officer","Terrestrial_Logistics_Officer","Trader"],"title":"get_corporations_corporation_id_roles_grantable_roles_at_other","description":"grantable_roles_at_other string"},"title":"get_corporations_corporation_id_roles_grantable_roles_at_other","description":"grantable_roles_at_other array"}},"title":"get_corporations_corporation_id_roles_200_ok","description":"200 ok object"},"title":"get_corporations_corporation_id_roles_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-corporations.read_corporation_membership.v1"]}],"operationId":"get_corporations_corporation_id_roles","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/roles/history/":{"get":{"description":"Return how roles have changed for a coporation's members, up to a month\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/roles/history/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/roles/history/`\n\nAlternate route: `/v1/corporations/{corporation_id}/roles/history/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\nRequires one of the following EVE corporation role(s): Director\n","summary":"Get corporation member roles history","tags":["Corporation"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"List of role changes","examples":{"application/json":[{"character_id":12345,"changed_at":"2016-10-25T14:46:00Z","issuer_id":45678,"role_type":"roles","old_roles":["Diplomat"],"new_roles":["Station_Manager"]}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["character_id","changed_at","issuer_id","role_type","old_roles","new_roles"],"properties":{"character_id":{"type":"integer","format":"int32","description":"The character whose roles are changed","title":"get_corporations_corporation_id_roles_history_character_id"},"changed_at":{"type":"string","format":"date-time","title":"get_corporations_corporation_id_roles_history_changed_at","description":"changed_at string"},"issuer_id":{"type":"integer","format":"int32","description":"ID of the character who issued this change","title":"get_corporations_corporation_id_roles_history_issuer_id"},"role_type":{"type":"string","enum":["grantable_roles","grantable_roles_at_base","grantable_roles_at_hq","grantable_roles_at_other","roles","roles_at_base","roles_at_hq","roles_at_other"],"title":"get_corporations_corporation_id_roles_history_role_type","description":"role_type string"},"old_roles":{"type":"array","maxItems":50,"items":{"type":"string","uniqueItems":true,"enum":["Account_Take_1","Account_Take_2","Account_Take_3","Account_Take_4","Account_Take_5","Account_Take_6","Account_Take_7","Accountant","Auditor","Communications_Officer","Config_Equipment","Config_Starbase_Equipment","Container_Take_1","Container_Take_2","Container_Take_3","Container_Take_4","Container_Take_5","Container_Take_6","Container_Take_7","Contract_Manager","Diplomat","Director","Factory_Manager","Fitting_Manager","Hangar_Query_1","Hangar_Query_2","Hangar_Query_3","Hangar_Query_4","Hangar_Query_5","Hangar_Query_6","Hangar_Query_7","Hangar_Take_1","Hangar_Take_2","Hangar_Take_3","Hangar_Take_4","Hangar_Take_5","Hangar_Take_6","Hangar_Take_7","Junior_Accountant","Personnel_Manager","Rent_Factory_Facility","Rent_Office","Rent_Research_Facility","Security_Officer","Starbase_Defense_Operator","Starbase_Fuel_Technician","Station_Manager","Terrestrial_Combat_Officer","Terrestrial_Logistics_Officer","Trader"],"title":"get_corporations_corporation_id_roles_history_old_role","description":"old_role string"},"title":"get_corporations_corporation_id_roles_history_old_roles","description":"old_roles array"},"new_roles":{"type":"array","maxItems":50,"items":{"type":"string","uniqueItems":true,"enum":["Account_Take_1","Account_Take_2","Account_Take_3","Account_Take_4","Account_Take_5","Account_Take_6","Account_Take_7","Accountant","Auditor","Communications_Officer","Config_Equipment","Config_Starbase_Equipment","Container_Take_1","Container_Take_2","Container_Take_3","Container_Take_4","Container_Take_5","Container_Take_6","Container_Take_7","Contract_Manager","Diplomat","Director","Factory_Manager","Fitting_Manager","Hangar_Query_1","Hangar_Query_2","Hangar_Query_3","Hangar_Query_4","Hangar_Query_5","Hangar_Query_6","Hangar_Query_7","Hangar_Take_1","Hangar_Take_2","Hangar_Take_3","Hangar_Take_4","Hangar_Take_5","Hangar_Take_6","Hangar_Take_7","Junior_Accountant","Personnel_Manager","Rent_Factory_Facility","Rent_Office","Rent_Research_Facility","Security_Officer","Starbase_Defense_Operator","Starbase_Fuel_Technician","Station_Manager","Terrestrial_Combat_Officer","Terrestrial_Logistics_Officer","Trader"],"title":"get_corporations_corporation_id_roles_history_new_role","description":"new_role string"},"title":"get_corporations_corporation_id_roles_history_new_roles","description":"new_roles array"}},"title":"get_corporations_corporation_id_roles_history_200_ok","description":"200 ok object"},"title":"get_corporations_corporation_id_roles_history_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":["Director"],"security":[{"evesso":["esi-corporations.read_corporation_membership.v1"]}],"operationId":"get_corporations_corporation_id_roles_history","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/icons/":{"get":{"description":"Get the icon urls for a corporation\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/icons/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/icons/`\n\nAlternate route: `/v1/corporations/{corporation_id}/icons/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get corporation icon","tags":["Corporation"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Urls for icons for the given corporation id and server","examples":{"application/json":{"px64x64":"https://imageserver.eveonline.com/Corporation/1000010_64.png","px128x128":"https://imageserver.eveonline.com/Corporation/1000010_128.png","px256x256":"https://imageserver.eveonline.com/Corporation/1000010_256.png"}},"schema":{"type":"object","properties":{"px64x64":{"type":"string","title":"get_corporations_corporation_id_icons_px64x64","description":"px64x64 string"},"px128x128":{"type":"string","title":"get_corporations_corporation_id_icons_px128x128","description":"px128x128 string"},"px256x256":{"type":"string","title":"get_corporations_corporation_id_icons_px256x256","description":"px256x256 string"}},"title":"get_corporations_corporation_id_icons_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"No image server for this datasource","examples":{"application/json":{"error":"No image server for this datasource"}},"schema":{"type":"object","description":"No image server for this datasource","properties":{"error":{"type":"string","description":"error message","title":"get_corporations_corporation_id_icons_error"}},"title":"get_corporations_corporation_id_icons_not_found"}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_corporations_corporation_id_icons","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/npccorps/":{"get":{"description":"Get a list of npc corporations\n\n---\nAlternate route: `/dev/corporations/npccorps/`\n\nAlternate route: `/legacy/corporations/npccorps/`\n\nAlternate route: `/v1/corporations/npccorps/`\n\n---\nThis route expires daily at 11:05","summary":"Get npc corporations","tags":["Corporation"],"responses":{"200":{"description":"A list of npc corporation ids","examples":{"application/json":[1000001,1000002,1000003]},"schema":{"type":"array","maxItems":500,"items":{"type":"integer","format":"int32","title":"get_corporations_npccorps_200_ok","description":"200 ok integer"},"title":"get_corporations_npccorps_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_corporations_npccorps","x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/structures/":{"get":{"description":"Get a list of corporation structures\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/structures/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/structures/`\n\nAlternate route: `/v1/corporations/{corporation_id}/structures/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\nRequires one of the following EVE corporation role(s): StationManager\n","summary":"Get corporation structures","tags":["Corporation"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/language"},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"List of corporation structures' information","examples":{"application/json":[{"structure_id":1021975535893,"type_id":35833,"corporation_id":667531913,"system_id":30004763,"profile_id":11237,"current_vul":[{"day":1,"hour":2}],"next_vul":[{"day":3,"hour":4}]}]},"schema":{"type":"array","maxItems":250,"items":{"type":"object","required":["structure_id","type_id","corporation_id","system_id","profile_id","current_vul","next_vul"],"properties":{"structure_id":{"type":"integer","format":"int64","description":"The Item ID of the structure","title":"get_corporations_corporation_id_structures_structure_id"},"type_id":{"type":"integer","format":"int32","description":"The type id of the structure","title":"get_corporations_corporation_id_structures_type_id"},"corporation_id":{"type":"integer","format":"int32","description":"ID of the corporation that owns the structure","title":"get_corporations_corporation_id_structures_corporation_id"},"system_id":{"type":"integer","format":"int32","description":"The solar system the structure is in","title":"get_corporations_corporation_id_structures_system_id"},"profile_id":{"type":"integer","format":"int32","description":"The id of the ACL profile for this citadel","title":"get_corporations_corporation_id_structures_profile_id"},"current_vul":{"type":"array","description":"This week's vulnerability windows, Monday is day 0","maxItems":168,"items":{"type":"object","required":["day","hour"],"properties":{"day":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_structures_day","description":"day integer"},"hour":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_structures_hour","description":"hour integer"}},"title":"get_corporations_corporation_id_structures_current_vul","description":"current_vul object"},"title":"get_corporations_corporation_id_structures_current_vul"},"next_vul":{"type":"array","description":"Next week's vulnerability windows, Monday is day 0","maxItems":168,"items":{"type":"object","required":["day","hour"],"properties":{"day":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_structures_day","description":"day integer"},"hour":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_structures_hour","description":"hour integer"}},"title":"get_corporations_corporation_id_structures_next_vul","description":"next_vul object"},"title":"get_corporations_corporation_id_structures_next_vul"},"fuel_expires":{"type":"string","format":"date-time","description":"Date on which the structure will run out of fuel","title":"get_corporations_corporation_id_structures_fuel_expires"},"services":{"type":"array","description":"Contains a list of service upgrades, and their state","maxItems":10,"items":{"type":"object","required":["name","state"],"properties":{"name":{"type":"string","title":"get_corporations_corporation_id_structures_name","description":"name string"},"state":{"type":"string","enum":["online","offline","cleanup"],"title":"get_corporations_corporation_id_structures_state","description":"state string"}},"title":"get_corporations_corporation_id_structures_service","description":"service object"},"title":"get_corporations_corporation_id_structures_services"},"state_timer_start":{"type":"string","format":"date-time","description":"Date at which the structure entered it's current state","title":"get_corporations_corporation_id_structures_state_timer_start"},"state_timer_end":{"type":"string","format":"date-time","description":"Date at which the structure will move to it's next state","title":"get_corporations_corporation_id_structures_state_timer_end"},"unanchors_at":{"type":"string","format":"date-time","description":"Date at which the structure will unanchor","title":"get_corporations_corporation_id_structures_unanchors_at"}},"title":"get_corporations_corporation_id_structures_200_ok","description":"200 ok object"},"title":"get_corporations_corporation_id_structures_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Content-Language":{"description":"The language used in the response","type":"string","enum":["de","en-us","fr","ja","ru","zh"]},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":["StationManager"],"security":[{"evesso":["esi-corporations.read_structures.v1"]}],"operationId":"get_corporations_corporation_id_structures","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/structures/{structure_id}/":{"put":{"description":"Update the vulnerability window schedule of a corporation structure\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/structures/{structure_id}/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/structures/{structure_id}/`\n\nAlternate route: `/v1/corporations/{corporation_id}/structures/{structure_id}/`\n","summary":"Update structure vulnerability schedule","tags":["Corporation"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"name":"new_schedule","in":"body","description":"New vulnerability window schedule for the structure","required":true,"schema":{"type":"array","minItems":1,"maxItems":168,"items":{"type":"object","required":["day","hour"],"properties":{"day":{"type":"integer","format":"int32","description":"Day of the week, zero-indexed to Monday","minimum":0,"maximum":6,"title":"put_corporations_corporation_id_structures_structure_id_day"},"hour":{"type":"integer","format":"int32","description":"Hour of the day evetime, zero-indexed to midnight","minimum":0,"maximum":23,"title":"put_corporations_corporation_id_structures_structure_id_hour"}},"title":"put_corporations_corporation_id_structures_structure_id_new_schedule","description":"new_schedule object"},"title":"put_corporations_corporation_id_structures_structure_id_new_schedule","description":"new_schedule array"}},{"name":"structure_id","in":"path","description":"A structure ID","required":true,"type":"integer","format":"int64"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"204":{"description":"Structure vulnerability window updated"},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-corporations.write_structures.v1"]}],"operationId":"put_corporations_corporation_id_structures_structure_id","x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/membertracking/":{"get":{"description":"Returns additional information about a corporation's members which helps tracking their activities\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/membertracking/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/membertracking/`\n\nAlternate route: `/v1/corporations/{corporation_id}/membertracking/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\nRequires one of the following EVE corporation role(s): Director\n","summary":"Track corporation members","tags":["Corporation"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"List of member character IDs","examples":{"application/json":[{"character_id":2112000001,"start_date":"2017-07-10T14:46:00Z","logon_date":"2017-08-03T14:22:03Z","logoff_date":"2017-08-03T14:31:16Z","location_id":30003657,"ship_type_id":22464},{"character_id":2112000002,"start_date":"2017-07-10T14:50:00Z","logon_date":"2017-07-25T10:54:00Z","logoff_date":"2017-07-25T11:07:40Z","location_id":30003657,"ship_type_id":670}]},"schema":{"type":"array","maxItems":12601,"items":{"type":"object","required":["character_id"],"properties":{"character_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_membertracking_character_id","description":"character_id integer"},"start_date":{"type":"string","format":"date-time","title":"get_corporations_corporation_id_membertracking_start_date","description":"start_date string"},"base_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_membertracking_base_id","description":"base_id integer"},"logon_date":{"type":"string","format":"date-time","title":"get_corporations_corporation_id_membertracking_logon_date","description":"logon_date string"},"logoff_date":{"type":"string","format":"date-time","title":"get_corporations_corporation_id_membertracking_logoff_date","description":"logoff_date string"},"location_id":{"type":"integer","format":"int64","title":"get_corporations_corporation_id_membertracking_location_id","description":"location_id integer"},"ship_type_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_membertracking_ship_type_id","description":"ship_type_id integer"}},"title":"get_corporations_corporation_id_membertracking_200_ok","description":"200 ok object"},"title":"get_corporations_corporation_id_membertracking_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":["Director"],"security":[{"evesso":["esi-corporations.track_members.v1"]}],"operationId":"get_corporations_corporation_id_membertracking","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/divisions/":{"get":{"description":"Return corporation hangar and wallet division names, only show if a division is not using the default name\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/divisions/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/divisions/`\n\nAlternate route: `/v1/corporations/{corporation_id}/divisions/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\nRequires one of the following EVE corporation role(s): Director\n","summary":"Get corporation divisions","tags":["Corporation"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"List of corporation division names","examples":{"application/json":{"hangar":[{"division":1,"name":"Awesome Hangar 1"}],"wallet":[{"division":1,"name":"Rich Wallet 1"}]}},"schema":{"type":"object","properties":{"hangar":{"type":"array","maxItems":7,"items":{"type":"object","properties":{"division":{"type":"integer","format":"int32","minimum":1,"maximum":7,"title":"get_corporations_corporation_id_divisions_division","description":"division integer"},"name":{"type":"string","maxLength":50,"title":"get_corporations_corporation_id_divisions_name","description":"name string"}},"title":"get_corporations_corporation_id_divisions_hangar","description":"hangar object"},"title":"get_corporations_corporation_id_divisions_hangar","description":"hangar array"},"wallet":{"type":"array","maxItems":7,"items":{"type":"object","properties":{"division":{"type":"integer","format":"int32","minimum":1,"maximum":7,"title":"get_corporations_corporation_id_divisions_division","description":"division integer"},"name":{"type":"string","maxLength":50,"title":"get_corporations_corporation_id_divisions_name","description":"name string"}},"title":"get_corporations_corporation_id_divisions_wallet","description":"wallet object"},"title":"get_corporations_corporation_id_divisions_wallet","description":"wallet array"}},"title":"get_corporations_corporation_id_divisions_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":["Director"],"security":[{"evesso":["esi-corporations.read_divisions.v1"]}],"operationId":"get_corporations_corporation_id_divisions","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/members/limit/":{"get":{"description":"Return a corporation's member limit, not including CEO himself\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/members/limit/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/members/limit/`\n\nAlternate route: `/v1/corporations/{corporation_id}/members/limit/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\nRequires one of the following EVE corporation role(s): Director\n","summary":"Get corporation member limit","tags":["Corporation"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"The corporation's member limit","examples":{"application/json":40},"schema":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_members_limit_ok","description":"200 ok integer"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":["Director"],"security":[{"evesso":["esi-corporations.track_members.v1"]}],"operationId":"get_corporations_corporation_id_members_limit","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/titles/":{"get":{"description":"Returns a corporation's titles\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/titles/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/titles/`\n\nAlternate route: `/v1/corporations/{corporation_id}/titles/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\nRequires one of the following EVE corporation role(s): Director\n","summary":"Get corporation titles","tags":["Corporation"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of titles","examples":{"application/json":[{"title_id":1,"name":"Awesome Title","roles":["Hangar_Take_6","Hangar_Query_2"]}]},"schema":{"type":"array","maxItems":16,"items":{"type":"object","properties":{"title_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_titles_title_id","description":"title_id integer"},"name":{"type":"string","title":"get_corporations_corporation_id_titles_name","description":"name string"},"roles":{"type":"array","maxItems":50,"items":{"type":"string","uniqueItems":true,"enum":["Account_Take_1","Account_Take_2","Account_Take_3","Account_Take_4","Account_Take_5","Account_Take_6","Account_Take_7","Accountant","Auditor","Communications_Officer","Config_Equipment","Config_Starbase_Equipment","Container_Take_1","Container_Take_2","Container_Take_3","Container_Take_4","Container_Take_5","Container_Take_6","Container_Take_7","Contract_Manager","Diplomat","Director","Factory_Manager","Fitting_Manager","Hangar_Query_1","Hangar_Query_2","Hangar_Query_3","Hangar_Query_4","Hangar_Query_5","Hangar_Query_6","Hangar_Query_7","Hangar_Take_1","Hangar_Take_2","Hangar_Take_3","Hangar_Take_4","Hangar_Take_5","Hangar_Take_6","Hangar_Take_7","Junior_Accountant","Personnel_Manager","Rent_Factory_Facility","Rent_Office","Rent_Research_Facility","Security_Officer","Starbase_Defense_Operator","Starbase_Fuel_Technician","Station_Manager","Terrestrial_Combat_Officer","Terrestrial_Logistics_Officer","Trader"],"title":"get_corporations_corporation_id_titles_role","description":"role string"},"title":"get_corporations_corporation_id_titles_roles","description":"roles array"},"grantable_roles":{"type":"array","maxItems":50,"items":{"type":"string","uniqueItems":true,"enum":["Account_Take_1","Account_Take_2","Account_Take_3","Account_Take_4","Account_Take_5","Account_Take_6","Account_Take_7","Accountant","Auditor","Communications_Officer","Config_Equipment","Config_Starbase_Equipment","Container_Take_1","Container_Take_2","Container_Take_3","Container_Take_4","Container_Take_5","Container_Take_6","Container_Take_7","Contract_Manager","Diplomat","Director","Factory_Manager","Fitting_Manager","Hangar_Query_1","Hangar_Query_2","Hangar_Query_3","Hangar_Query_4","Hangar_Query_5","Hangar_Query_6","Hangar_Query_7","Hangar_Take_1","Hangar_Take_2","Hangar_Take_3","Hangar_Take_4","Hangar_Take_5","Hangar_Take_6","Hangar_Take_7","Junior_Accountant","Personnel_Manager","Rent_Factory_Facility","Rent_Office","Rent_Research_Facility","Security_Officer","Starbase_Defense_Operator","Starbase_Fuel_Technician","Station_Manager","Terrestrial_Combat_Officer","Terrestrial_Logistics_Officer","Trader"],"title":"get_corporations_corporation_id_titles_grantable_role","description":"grantable_role string"},"title":"get_corporations_corporation_id_titles_grantable_roles","description":"grantable_roles array"},"roles_at_hq":{"type":"array","maxItems":50,"items":{"type":"string","uniqueItems":true,"enum":["Account_Take_1","Account_Take_2","Account_Take_3","Account_Take_4","Account_Take_5","Account_Take_6","Account_Take_7","Accountant","Auditor","Communications_Officer","Config_Equipment","Config_Starbase_Equipment","Container_Take_1","Container_Take_2","Container_Take_3","Container_Take_4","Container_Take_5","Container_Take_6","Container_Take_7","Contract_Manager","Diplomat","Director","Factory_Manager","Fitting_Manager","Hangar_Query_1","Hangar_Query_2","Hangar_Query_3","Hangar_Query_4","Hangar_Query_5","Hangar_Query_6","Hangar_Query_7","Hangar_Take_1","Hangar_Take_2","Hangar_Take_3","Hangar_Take_4","Hangar_Take_5","Hangar_Take_6","Hangar_Take_7","Junior_Accountant","Personnel_Manager","Rent_Factory_Facility","Rent_Office","Rent_Research_Facility","Security_Officer","Starbase_Defense_Operator","Starbase_Fuel_Technician","Station_Manager","Terrestrial_Combat_Officer","Terrestrial_Logistics_Officer","Trader"],"title":"get_corporations_corporation_id_titles_roles_at_hq","description":"roles_at_hq string"},"title":"get_corporations_corporation_id_titles_roles_at_hq","description":"roles_at_hq array"},"grantable_roles_at_hq":{"type":"array","maxItems":50,"items":{"type":"string","uniqueItems":true,"enum":["Account_Take_1","Account_Take_2","Account_Take_3","Account_Take_4","Account_Take_5","Account_Take_6","Account_Take_7","Accountant","Auditor","Communications_Officer","Config_Equipment","Config_Starbase_Equipment","Container_Take_1","Container_Take_2","Container_Take_3","Container_Take_4","Container_Take_5","Container_Take_6","Container_Take_7","Contract_Manager","Diplomat","Director","Factory_Manager","Fitting_Manager","Hangar_Query_1","Hangar_Query_2","Hangar_Query_3","Hangar_Query_4","Hangar_Query_5","Hangar_Query_6","Hangar_Query_7","Hangar_Take_1","Hangar_Take_2","Hangar_Take_3","Hangar_Take_4","Hangar_Take_5","Hangar_Take_6","Hangar_Take_7","Junior_Accountant","Personnel_Manager","Rent_Factory_Facility","Rent_Office","Rent_Research_Facility","Security_Officer","Starbase_Defense_Operator","Starbase_Fuel_Technician","Station_Manager","Terrestrial_Combat_Officer","Terrestrial_Logistics_Officer","Trader"],"title":"get_corporations_corporation_id_titles_grantable_roles_at_hq","description":"grantable_roles_at_hq string"},"title":"get_corporations_corporation_id_titles_grantable_roles_at_hq","description":"grantable_roles_at_hq array"},"roles_at_base":{"type":"array","maxItems":50,"items":{"type":"string","uniqueItems":true,"enum":["Account_Take_1","Account_Take_2","Account_Take_3","Account_Take_4","Account_Take_5","Account_Take_6","Account_Take_7","Accountant","Auditor","Communications_Officer","Config_Equipment","Config_Starbase_Equipment","Container_Take_1","Container_Take_2","Container_Take_3","Container_Take_4","Container_Take_5","Container_Take_6","Container_Take_7","Contract_Manager","Diplomat","Director","Factory_Manager","Fitting_Manager","Hangar_Query_1","Hangar_Query_2","Hangar_Query_3","Hangar_Query_4","Hangar_Query_5","Hangar_Query_6","Hangar_Query_7","Hangar_Take_1","Hangar_Take_2","Hangar_Take_3","Hangar_Take_4","Hangar_Take_5","Hangar_Take_6","Hangar_Take_7","Junior_Accountant","Personnel_Manager","Rent_Factory_Facility","Rent_Office","Rent_Research_Facility","Security_Officer","Starbase_Defense_Operator","Starbase_Fuel_Technician","Station_Manager","Terrestrial_Combat_Officer","Terrestrial_Logistics_Officer","Trader"],"title":"get_corporations_corporation_id_titles_roles_at_base","description":"roles_at_base string"},"title":"get_corporations_corporation_id_titles_roles_at_base","description":"roles_at_base array"},"grantable_roles_at_base":{"type":"array","maxItems":50,"items":{"type":"string","uniqueItems":true,"enum":["Account_Take_1","Account_Take_2","Account_Take_3","Account_Take_4","Account_Take_5","Account_Take_6","Account_Take_7","Accountant","Auditor","Communications_Officer","Config_Equipment","Config_Starbase_Equipment","Container_Take_1","Container_Take_2","Container_Take_3","Container_Take_4","Container_Take_5","Container_Take_6","Container_Take_7","Contract_Manager","Diplomat","Director","Factory_Manager","Fitting_Manager","Hangar_Query_1","Hangar_Query_2","Hangar_Query_3","Hangar_Query_4","Hangar_Query_5","Hangar_Query_6","Hangar_Query_7","Hangar_Take_1","Hangar_Take_2","Hangar_Take_3","Hangar_Take_4","Hangar_Take_5","Hangar_Take_6","Hangar_Take_7","Junior_Accountant","Personnel_Manager","Rent_Factory_Facility","Rent_Office","Rent_Research_Facility","Security_Officer","Starbase_Defense_Operator","Starbase_Fuel_Technician","Station_Manager","Terrestrial_Combat_Officer","Terrestrial_Logistics_Officer","Trader"],"title":"get_corporations_corporation_id_titles_grantable_roles_at_base","description":"grantable_roles_at_base string"},"title":"get_corporations_corporation_id_titles_grantable_roles_at_base","description":"grantable_roles_at_base array"},"roles_at_other":{"type":"array","maxItems":50,"items":{"type":"string","uniqueItems":true,"enum":["Account_Take_1","Account_Take_2","Account_Take_3","Account_Take_4","Account_Take_5","Account_Take_6","Account_Take_7","Accountant","Auditor","Communications_Officer","Config_Equipment","Config_Starbase_Equipment","Container_Take_1","Container_Take_2","Container_Take_3","Container_Take_4","Container_Take_5","Container_Take_6","Container_Take_7","Contract_Manager","Diplomat","Director","Factory_Manager","Fitting_Manager","Hangar_Query_1","Hangar_Query_2","Hangar_Query_3","Hangar_Query_4","Hangar_Query_5","Hangar_Query_6","Hangar_Query_7","Hangar_Take_1","Hangar_Take_2","Hangar_Take_3","Hangar_Take_4","Hangar_Take_5","Hangar_Take_6","Hangar_Take_7","Junior_Accountant","Personnel_Manager","Rent_Factory_Facility","Rent_Office","Rent_Research_Facility","Security_Officer","Starbase_Defense_Operator","Starbase_Fuel_Technician","Station_Manager","Terrestrial_Combat_Officer","Terrestrial_Logistics_Officer","Trader"],"title":"get_corporations_corporation_id_titles_roles_at_other","description":"roles_at_other string"},"title":"get_corporations_corporation_id_titles_roles_at_other","description":"roles_at_other array"},"grantable_roles_at_other":{"type":"array","maxItems":50,"items":{"type":"string","uniqueItems":true,"enum":["Account_Take_1","Account_Take_2","Account_Take_3","Account_Take_4","Account_Take_5","Account_Take_6","Account_Take_7","Accountant","Auditor","Communications_Officer","Config_Equipment","Config_Starbase_Equipment","Container_Take_1","Container_Take_2","Container_Take_3","Container_Take_4","Container_Take_5","Container_Take_6","Container_Take_7","Contract_Manager","Diplomat","Director","Factory_Manager","Fitting_Manager","Hangar_Query_1","Hangar_Query_2","Hangar_Query_3","Hangar_Query_4","Hangar_Query_5","Hangar_Query_6","Hangar_Query_7","Hangar_Take_1","Hangar_Take_2","Hangar_Take_3","Hangar_Take_4","Hangar_Take_5","Hangar_Take_6","Hangar_Take_7","Junior_Accountant","Personnel_Manager","Rent_Factory_Facility","Rent_Office","Rent_Research_Facility","Security_Officer","Starbase_Defense_Operator","Starbase_Fuel_Technician","Station_Manager","Terrestrial_Combat_Officer","Terrestrial_Logistics_Officer","Trader"],"title":"get_corporations_corporation_id_titles_grantable_roles_at_other","description":"grantable_roles_at_other string"},"title":"get_corporations_corporation_id_titles_grantable_roles_at_other","description":"grantable_roles_at_other array"}},"title":"get_corporations_corporation_id_titles_200_ok","description":"200 ok object"},"title":"get_corporations_corporation_id_titles_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":["Director"],"security":[{"evesso":["esi-corporations.read_titles.v1"]}],"operationId":"get_corporations_corporation_id_titles","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/members/titles/":{"get":{"description":"Returns a corporation's members' titles\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/members/titles/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/members/titles/`\n\nAlternate route: `/v1/corporations/{corporation_id}/members/titles/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\nRequires one of the following EVE corporation role(s): Director\n","summary":"Get corporation's members' titles","tags":["Corporation"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of members and theirs titles","examples":{"application/json":[{"character_id":12345,"titles":[]}]},"schema":{"type":"array","maxItems":12601,"items":{"type":"object","required":["character_id","titles"],"properties":{"character_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_members_titles_character_id","description":"character_id integer"},"titles":{"type":"array","maxItems":16,"description":"A list of title_id","items":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_members_titles_title","description":"title integer"},"title":"get_corporations_corporation_id_members_titles_titles"}},"title":"get_corporations_corporation_id_members_titles_200_ok","description":"200 ok object"},"title":"get_corporations_corporation_id_members_titles_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":["Director"],"security":[{"evesso":["esi-corporations.read_titles.v1"]}],"operationId":"get_corporations_corporation_id_members_titles","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/blueprints/":{"get":{"description":"Returns a list of blueprints the corporation owns\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/blueprints/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/blueprints/`\n\nAlternate route: `/v1/corporations/{corporation_id}/blueprints/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\nRequires one of the following EVE corporation role(s): Director\n","summary":"Get corporation blueprints","tags":["Corporation"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"List of corporation blueprints","examples":{"application/json":[{"item_id":1000000010495,"type_id":691,"location_id":60014719,"location_flag":"CorpSAG1","quantity":1,"time_efficiency":0,"material_efficiency":0,"runs":-1}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["item_id","type_id","location_id","location_flag","quantity","time_efficiency","material_efficiency","runs"],"properties":{"item_id":{"type":"integer","format":"int64","description":"Unique ID for this item.","title":"get_corporations_corporation_id_blueprints_item_id"},"type_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_blueprints_type_id","description":"type_id integer"},"location_id":{"type":"integer","format":"int64","description":"References a solar system, station or item_id if this blueprint is located within a container.","title":"get_corporations_corporation_id_blueprints_location_id"},"location_flag":{"type":"string","description":"Type of the location_id","enum":["AssetSafety","AutoFit","Bonus","Booster","BoosterBay","Capsule","Cargo","CorpDeliveries","CorpSAG1","CorpSAG2","CorpSAG3","CorpSAG4","CorpSAG5","CorpSAG6","CorpSAG7","CrateLoot","Deliveries","DroneBay","DustBattle","DustDatabank","FighterBay","FighterTube0","FighterTube1","FighterTube2","FighterTube3","FighterTube4","FleetHangar","Hangar","HangarAll","HiSlot0","HiSlot1","HiSlot2","HiSlot3","HiSlot4","HiSlot5","HiSlot6","HiSlot7","HiddenModifers","Implant","Impounded","JunkyardReprocessed","JunkyardTrashed","LoSlot0","LoSlot1","LoSlot2","LoSlot3","LoSlot4","LoSlot5","LoSlot6","LoSlot7","Locked","MedSlot0","MedSlot1","MedSlot2","MedSlot3","MedSlot4","MedSlot5","MedSlot6","MedSlot7","OfficeFolder","Pilot","PlanetSurface","QuafeBay","Reward","RigSlot0","RigSlot1","RigSlot2","RigSlot3","RigSlot4","RigSlot5","RigSlot6","RigSlot7","SecondaryStorage","ServiceSlot0","ServiceSlot1","ServiceSlot2","ServiceSlot3","ServiceSlot4","ServiceSlot5","ServiceSlot6","ServiceSlot7","ShipHangar","ShipOffline","Skill","SkillInTraining","SpecializedAmmoHold","SpecializedCommandCenterHold","SpecializedFuelBay","SpecializedGasHold","SpecializedIndustrialShipHold","SpecializedLargeShipHold","SpecializedMaterialBay","SpecializedMediumShipHold","SpecializedMineralHold","SpecializedOreHold","SpecializedPlanetaryCommoditiesHold","SpecializedSalvageHold","SpecializedShipHold","SpecializedSmallShipHold","StructureActive","StructureFuel","StructureInactive","StructureOffline","SubSystemSlot0","SubSystemSlot1","SubSystemSlot2","SubSystemSlot3","SubSystemSlot4","SubSystemSlot5","SubSystemSlot6","SubSystemSlot7","SubsystemBay","Unlocked","Wallet","Wardrobe"],"title":"get_corporations_corporation_id_blueprints_location_flag"},"quantity":{"type":"integer","format":"int32","description":"A range of numbers with a minimum of -2 and no maximum value where -1 is an original and -2 is a copy. It can be a positive integer if it is a stack of blueprint originals fresh from the market (e.g. no activities performed on them yet).","minimum":-2,"title":"get_corporations_corporation_id_blueprints_quantity"},"time_efficiency":{"type":"integer","format":"int32","description":"Time Efficiency Level of the blueprint.","minimum":0,"maximum":20,"title":"get_corporations_corporation_id_blueprints_time_efficiency"},"material_efficiency":{"type":"integer","format":"int32","description":"Material Efficiency Level of the blueprint.","minimum":0,"maximum":25,"title":"get_corporations_corporation_id_blueprints_material_efficiency"},"runs":{"type":"integer","format":"int32","description":"Number of runs remaining if the blueprint is a copy, -1 if it is an original.","minimum":-1,"title":"get_corporations_corporation_id_blueprints_runs"}},"title":"get_corporations_corporation_id_blueprints_200_ok","description":"200 ok object"},"title":"get_corporations_corporation_id_blueprints_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":["Director"],"security":[{"evesso":["esi-corporations.read_blueprints.v1"]}],"operationId":"get_corporations_corporation_id_blueprints","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/standings/":{"get":{"description":"Return corporation standings from agents, NPC corporations, and factions\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/standings/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/standings/`\n\nAlternate route: `/v1/corporations/{corporation_id}/standings/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get corporation standings","tags":["Corporation"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of standings","examples":{"application/json":[{"from_id":3009841,"from_type":"agent","standing":0.1},{"from_id":1000061,"from_type":"npc_corp","standing":0},{"from_id":500003,"from_type":"faction","standing":-1}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["from_id","from_type","standing"],"properties":{"from_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_standings_from_id","description":"from_id integer"},"from_type":{"type":"string","enum":["agent","npc_corp","faction"],"title":"get_corporations_corporation_id_standings_from_type","description":"from_type string"},"standing":{"type":"number","format":"float","minimum":-10,"maximum":10,"title":"get_corporations_corporation_id_standings_standing","description":"standing number"}},"title":"get_corporations_corporation_id_standings_200_ok","description":"200 ok object"},"title":"get_corporations_corporation_id_standings_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":[],"security":[{"evesso":["esi-corporations.read_standings.v1"]}],"operationId":"get_corporations_corporation_id_standings","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/starbases/":{"get":{"description":"Returns list of corporation starbases (POSes)\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/starbases/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/starbases/`\n\nAlternate route: `/v1/corporations/{corporation_id}/starbases/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\nRequires one of the following EVE corporation role(s): Director\n","summary":"Get corporation starbases (POSes)","tags":["Corporation"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"List of starbases (POSes)","examples":{"application/json":[{"starbase_id":12345,"type_id":456,"system_id":123456}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["starbase_id","type_id","system_id"],"properties":{"starbase_id":{"type":"integer","format":"int64","description":"Unique ID for this starbase (POS)","title":"get_corporations_corporation_id_starbases_starbase_id"},"type_id":{"type":"integer","format":"int32","description":"Starbase (POS) type","title":"get_corporations_corporation_id_starbases_type_id"},"system_id":{"type":"integer","format":"int32","description":"The solar system this starbase (POS) is in, unanchored POSes have this information","title":"get_corporations_corporation_id_starbases_system_id"},"moon_id":{"type":"integer","format":"int32","description":"The moon this starbase (POS) is anchored on, unanchored POSes do not have this information","title":"get_corporations_corporation_id_starbases_moon_id"},"state":{"type":"string","enum":["offline","online","onlining","reinforced","unanchoring"],"title":"get_corporations_corporation_id_starbases_state","description":"state string"},"unanchor_at":{"type":"string","format":"date-time","description":"When the POS started unanchoring, for starbases (POSes) in unanchoring state","title":"get_corporations_corporation_id_starbases_unanchor_at"},"reinforced_until":{"type":"string","format":"date-time","description":"When the POS will be out of reinforcement, for starbases (POSes) in reinforced state","title":"get_corporations_corporation_id_starbases_reinforced_until"},"onlined_since":{"type":"string","format":"date-time","description":"When the POS onlined, for starbases (POSes) in online state","title":"get_corporations_corporation_id_starbases_onlined_since"}},"title":"get_corporations_corporation_id_starbases_200_ok","description":"200 ok object"},"title":"get_corporations_corporation_id_starbases_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":["Director"],"security":[{"evesso":["esi-corporations.read_starbases.v1"]}],"operationId":"get_corporations_corporation_id_starbases","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/starbases/{starbase_id}/":{"get":{"description":"Returns various settings and fuels of a starbase (POS)\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/starbases/{starbase_id}/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/starbases/{starbase_id}/`\n\nAlternate route: `/v1/corporations/{corporation_id}/starbases/{starbase_id}/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\nRequires one of the following EVE corporation role(s): Director\n","summary":"Get starbase (POS) detail","tags":["Corporation"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"name":"starbase_id","in":"path","description":"An EVE starbase (POS) ID","required":true,"type":"integer","format":"int64"},{"name":"system_id","in":"query","description":"The solar system this starbase (POS) is located in,","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"List of starbases (POSes)","examples":{"application/json":{"fuel_bay_view":"config_starbase_equipment_role","fuel_bay_take":"config_starbase_equipment_role","anchor":"config_starbase_equipment_role","unanchor":"config_starbase_equipment_role","online":"config_starbase_equipment_role","offline":"config_starbase_equipment_role","allow_corporation_members":true,"allow_alliance_members":false,"use_alliance_standings":false,"attack_if_other_security_status_dropping":false,"attack_if_at_war":true}},"schema":{"type":"object","required":["fuel_bay_view","fuel_bay_take","anchor","unanchor","online","offline","allow_corporation_members","allow_alliance_members","use_alliance_standings","attack_if_other_security_status_dropping","attack_if_at_war"],"properties":{"fuel_bay_view":{"type":"string","description":"Who can view the starbase (POS)'s fule bay. Characters either need to have required role or belong to the starbase (POS) owner's corporation or alliance, as described by the enum, all other access settings follows the same scheme","enum":["alliance_member","config_starbase_equipment_role","corporation_member","starbase_fuel_technician_role"],"title":"get_corporations_corporation_id_starbases_starbase_id_fuel_bay_view"},"fuel_bay_take":{"type":"string","description":"Who can take fuel blocks out of the starbase (POS)'s fuel bay","enum":["alliance_member","config_starbase_equipment_role","corporation_member","starbase_fuel_technician_role"],"title":"get_corporations_corporation_id_starbases_starbase_id_fuel_bay_take"},"anchor":{"type":"string","description":"Who can anchor starbase (POS) and its structures","enum":["alliance_member","config_starbase_equipment_role","corporation_member","starbase_fuel_technician_role"],"title":"get_corporations_corporation_id_starbases_starbase_id_anchor"},"unanchor":{"type":"string","description":"Who can unanchor starbase (POS) and its structures","enum":["alliance_member","config_starbase_equipment_role","corporation_member","starbase_fuel_technician_role"],"title":"get_corporations_corporation_id_starbases_starbase_id_unanchor"},"online":{"type":"string","description":"Who can online starbase (POS) and its structures","enum":["alliance_member","config_starbase_equipment_role","corporation_member","starbase_fuel_technician_role"],"title":"get_corporations_corporation_id_starbases_starbase_id_online"},"offline":{"type":"string","description":"Who can offline starbase (POS) and its structures","enum":["alliance_member","config_starbase_equipment_role","corporation_member","starbase_fuel_technician_role"],"title":"get_corporations_corporation_id_starbases_starbase_id_offline"},"allow_corporation_members":{"type":"boolean","title":"get_corporations_corporation_id_starbases_starbase_id_allow_corporation_members","description":"allow_corporation_members boolean"},"allow_alliance_members":{"type":"boolean","title":"get_corporations_corporation_id_starbases_starbase_id_allow_alliance_members","description":"allow_alliance_members boolean"},"use_alliance_standings":{"type":"boolean","description":"True if the starbase (POS) is using alliance standings, otherwise using corporation's","title":"get_corporations_corporation_id_starbases_starbase_id_use_alliance_standings"},"attack_standing_threshold":{"type":"number","format":"float","description":"Starbase (POS) will attack if target's standing is lower than this value","title":"get_corporations_corporation_id_starbases_starbase_id_attack_standing_threshold"},"attack_security_status_threshold":{"type":"number","format":"float","description":"Starbase (POS) will attack if target's security standing is lower than this value","title":"get_corporations_corporation_id_starbases_starbase_id_attack_security_status_threshold"},"attack_if_other_security_status_dropping":{"type":"boolean","title":"get_corporations_corporation_id_starbases_starbase_id_attack_if_other_security_status_dropping","description":"attack_if_other_security_status_dropping boolean"},"attack_if_at_war":{"type":"boolean","title":"get_corporations_corporation_id_starbases_starbase_id_attack_if_at_war","description":"attack_if_at_war boolean"},"fuels":{"type":"array","maxItems":20,"description":"Fuel blocks and other things that will be consumed when operating a starbase (POS)","items":{"type":"object","required":["type_id","quantity"],"properties":{"type_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_starbases_starbase_id_type_id","description":"type_id integer"},"quantity":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_starbases_starbase_id_quantity","description":"quantity integer"}},"title":"get_corporations_corporation_id_starbases_starbase_id_fuel","description":"fuel object"},"title":"get_corporations_corporation_id_starbases_starbase_id_fuels"}},"title":"get_corporations_corporation_id_starbases_starbase_id_ok","description":"200 ok object"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":["Director"],"security":[{"evesso":["esi-corporations.read_starbases.v1"]}],"operationId":"get_corporations_corporation_id_starbases_starbase_id","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/containers/logs/":{"get":{"description":"Returns logs recorded in the past seven days from all audit log secure containers (ALSC) owned by a given corporation\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/containers/logs/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/containers/logs/`\n\nAlternate route: `/v1/corporations/{corporation_id}/containers/logs/`\n\n---\nThis route is cached for up to 600 seconds\n\n---\nRequires one of the following EVE corporation role(s): Director\n","summary":"Get all corporation ALSC logs","tags":["Corporation"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"List of corporation ALSC logs","examples":{"application/json":[{"logged_at":"2017-10-10T14:00:00Z","container_id":1000000012279,"container_type_id":17365,"character_id":2112625428,"location_id":1000000012278,"action":"set_password","location_flag":"CorpSAG1","password_type":"general"},{"logged_at":"2017-10-11T12:04:33Z","container_id":1000000012279,"container_type_id":17365,"character_id":2112625428,"location_id":1000000012278,"action":"lock","location_flag":"CorpSAG1","type_id":1230,"quantity":30},{"logged_at":"2017-10-11T12:06:29Z","container_id":1000000012279,"container_type_id":17365,"character_id":2112625428,"location_id":1000000012278,"action":"configure","location_flag":"CorpSAG2","old_config_bitmask":23,"new_config_bitmask":31}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["logged_at","container_id","container_type_id","character_id","location_id","action","location_flag"],"properties":{"logged_at":{"type":"string","format":"date-time","description":"Timestamp when this log was created","title":"get_corporations_corporation_id_containers_logs_logged_at"},"container_id":{"type":"integer","format":"int64","description":"ID of the container","title":"get_corporations_corporation_id_containers_logs_container_id"},"container_type_id":{"type":"integer","format":"int32","description":"Type ID of the container","title":"get_corporations_corporation_id_containers_logs_container_type_id"},"character_id":{"type":"integer","format":"int32","description":"ID of the character who performed the action.","title":"get_corporations_corporation_id_containers_logs_character_id"},"location_id":{"type":"integer","format":"int64","title":"get_corporations_corporation_id_containers_logs_location_id","description":"location_id integer"},"location_flag":{"type":"string","enum":["AssetSafety","AutoFit","Bonus","Booster","BoosterBay","Capsule","Cargo","CorpDeliveries","CorpSAG1","CorpSAG2","CorpSAG3","CorpSAG4","CorpSAG5","CorpSAG6","CorpSAG7","CrateLoot","Deliveries","DroneBay","DustBattle","DustDatabank","FighterBay","FighterTube0","FighterTube1","FighterTube2","FighterTube3","FighterTube4","FleetHangar","Hangar","HangarAll","HiSlot0","HiSlot1","HiSlot2","HiSlot3","HiSlot4","HiSlot5","HiSlot6","HiSlot7","HiddenModifers","Implant","Impounded","JunkyardReprocessed","JunkyardTrashed","LoSlot0","LoSlot1","LoSlot2","LoSlot3","LoSlot4","LoSlot5","LoSlot6","LoSlot7","Locked","MedSlot0","MedSlot1","MedSlot2","MedSlot3","MedSlot4","MedSlot5","MedSlot6","MedSlot7","OfficeFolder","Pilot","PlanetSurface","QuafeBay","Reward","RigSlot0","RigSlot1","RigSlot2","RigSlot3","RigSlot4","RigSlot5","RigSlot6","RigSlot7","SecondaryStorage","ServiceSlot0","ServiceSlot1","ServiceSlot2","ServiceSlot3","ServiceSlot4","ServiceSlot5","ServiceSlot6","ServiceSlot7","ShipHangar","ShipOffline","Skill","SkillInTraining","SpecializedAmmoHold","SpecializedCommandCenterHold","SpecializedFuelBay","SpecializedGasHold","SpecializedIndustrialShipHold","SpecializedLargeShipHold","SpecializedMaterialBay","SpecializedMediumShipHold","SpecializedMineralHold","SpecializedOreHold","SpecializedPlanetaryCommoditiesHold","SpecializedSalvageHold","SpecializedShipHold","SpecializedSmallShipHold","StructureActive","StructureFuel","StructureInactive","StructureOffline","SubSystemSlot0","SubSystemSlot1","SubSystemSlot2","SubSystemSlot3","SubSystemSlot4","SubSystemSlot5","SubSystemSlot6","SubSystemSlot7","SubsystemBay","Unlocked","Wallet","Wardrobe"],"title":"get_corporations_corporation_id_containers_logs_location_flag","description":"location_flag string"},"action":{"type":"string","enum":["add","assemble","configure","enter_password","lock","move","repackage","set_name","set_password","unlock"],"title":"get_corporations_corporation_id_containers_logs_action","description":"action string"},"password_type":{"type":"string","enum":["config","general"],"description":"Type of password set if action is of type SetPassword or EnterPassword","title":"get_corporations_corporation_id_containers_logs_password_type"},"type_id":{"type":"integer","format":"int32","description":"Type ID of the item being acted upon","title":"get_corporations_corporation_id_containers_logs_type_id"},"quantity":{"type":"integer","format":"int32","description":"Quantity of the item being acted upon","title":"get_corporations_corporation_id_containers_logs_quantity"},"old_config_bitmask":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_containers_logs_old_config_bitmask","description":"old_config_bitmask integer"},"new_config_bitmask":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_containers_logs_new_config_bitmask","description":"new_config_bitmask integer"}},"title":"get_corporations_corporation_id_containers_logs_200_ok","description":"200 ok object"},"title":"get_corporations_corporation_id_containers_logs_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":["Director"],"security":[{"evesso":["esi-corporations.read_container_logs.v1"]}],"operationId":"get_corporations_corporation_id_containers_logs","x-cached-seconds":600,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/facilities/":{"get":{"description":"Return a corporation's facilities\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/facilities/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/facilities/`\n\nAlternate route: `/v1/corporations/{corporation_id}/facilities/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\nRequires one of the following EVE corporation role(s): Factory_Manager\n","summary":"Get corporation facilities","tags":["Corporation"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"List of corporation facilities","examples":{"application/json":[{"facility_id":123,"type_id":2502,"system_id":45678}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["facility_id","type_id","system_id"],"properties":{"facility_id":{"type":"integer","format":"int64","title":"get_corporations_corporation_id_facilities_facility_id","description":"facility_id integer"},"type_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_facilities_type_id","description":"type_id integer"},"system_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_facilities_system_id","description":"system_id integer"}},"title":"get_corporations_corporation_id_facilities_200_ok","description":"200 ok object"},"title":"get_corporations_corporation_id_facilities_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":["Factory_Manager"],"security":[{"evesso":["esi-corporations.read_facilities.v1"]}],"operationId":"get_corporations_corporation_id_facilities","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/medals/":{"get":{"description":"Returns a corporation's medals\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/medals/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/medals/`\n\nAlternate route: `/v1/corporations/{corporation_id}/medals/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get corporation medals","tags":["Corporation"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of medals","examples":{"application/json":[{"medal_id":123,"title":"Awesome Medal","description":"An Awesome Medal","creator_id":46578,"created_at":"2017-10-10T14:00:00Z"}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["medal_id","title","description","creator_id","created_at"],"properties":{"medal_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_medals_medal_id","description":"medal_id integer"},"title":{"type":"string","maxLength":100,"title":"get_corporations_corporation_id_medals_title","description":"title string"},"description":{"type":"string","maxLength":1000,"title":"get_corporations_corporation_id_medals_description","description":"description string"},"creator_id":{"type":"integer","format":"int32","description":"ID of the character who created this medal","title":"get_corporations_corporation_id_medals_creator_id"},"created_at":{"type":"string","format":"date-time","title":"get_corporations_corporation_id_medals_created_at","description":"created_at string"}},"title":"get_corporations_corporation_id_medals_200_ok","description":"200 ok object"},"title":"get_corporations_corporation_id_medals_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":[],"security":[{"evesso":["esi-corporations.read_medals.v1"]}],"operationId":"get_corporations_corporation_id_medals","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/medals/issued/":{"get":{"description":"Returns medals issued by a corporation\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/medals/issued/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/medals/issued/`\n\nAlternate route: `/v1/corporations/{corporation_id}/medals/issued/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\nRequires one of the following EVE corporation role(s): Director\n","summary":"Get corporation issued medals","tags":["Corporation"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of issued medals","examples":{"application/json":[{"medal_id":123,"character_id":45678,"reason":"Awesome Reason","status":"private","issuer_id":67890,"issued_at":"2017-10-10T14:00:00Z"}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["medal_id","character_id","reason","status","issuer_id","issued_at"],"properties":{"medal_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_medals_issued_medal_id","description":"medal_id integer"},"character_id":{"type":"integer","format":"int32","description":"ID of the character who was rewarded this medal","title":"get_corporations_corporation_id_medals_issued_character_id"},"reason":{"type":"string","maxLength":1000,"title":"get_corporations_corporation_id_medals_issued_reason","description":"reason string"},"status":{"type":"string","enum":["private","public"],"title":"get_corporations_corporation_id_medals_issued_status","description":"status string"},"issuer_id":{"type":"integer","format":"int32","description":"ID of the character who issued the medal","title":"get_corporations_corporation_id_medals_issued_issuer_id"},"issued_at":{"type":"string","format":"date-time","title":"get_corporations_corporation_id_medals_issued_issued_at","description":"issued_at string"}},"title":"get_corporations_corporation_id_medals_issued_200_ok","description":"200 ok object"},"title":"get_corporations_corporation_id_medals_issued_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":["Director"],"security":[{"evesso":["esi-corporations.read_medals.v1"]}],"operationId":"get_corporations_corporation_id_medals_issued","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/outposts/":{"get":{"description":"Get a list of corporation outpost IDs Note: This endpoint will be removed once outposts are migrated to Citadels as talked about in this blog: https://community.eveonline.com/news/dev-blogs/the-next-steps-in-structure-transition/\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/outposts/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/outposts/`\n\nAlternate route: `/v1/corporations/{corporation_id}/outposts/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\nRequires one of the following EVE corporation role(s): Director\n","summary":"Get corporation outposts","tags":["Corporation"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"List of corporation outpost IDs","examples":{"application/json":[61001146,61001147,61001148]},"schema":{"type":"array","maxItems":1000,"uniqueItems":true,"items":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_outposts_200_ok","description":"200 ok integer"},"title":"get_corporations_corporation_id_outposts_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":["Director"],"security":[{"evesso":["esi-corporations.read_outposts.v1"]}],"operationId":"get_corporations_corporation_id_outposts","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/outposts/{outpost_id}/":{"get":{"description":"Get details about a given outpost. Note: This endpoint will be removed once outposts are migrated to Citadels as talked about in this blog: https://community.eveonline.com/news/dev-blogs/the-next-steps-in-structure-transition/\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/outposts/{outpost_id}/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/outposts/{outpost_id}/`\n\nAlternate route: `/v1/corporations/{corporation_id}/outposts/{outpost_id}/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\nRequires one of the following EVE corporation role(s): Director\n","summary":"Get corporation outpost details","tags":["Corporation"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"name":"outpost_id","in":"path","description":"A station (outpost) ID","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Details about the given outpost","examples":{"application/json":{"owner_id":98000002,"system_id":30000657,"docking_cost_per_ship_volume":0,"office_rental_cost":100,"type_id":21644,"reprocessing_efficiency":0.5,"reprocessing_station_take":0.01,"standing_owner_id":99003581,"coordinates":{"x":-102478848000,"y":-18310963200,"z":-9660456960},"services":[{"service_name":"Reprocessing Plant","owner_id":98000002,"minimum_standing":0,"surcharge_per_bad_standing":0,"discount_per_good_standing":10}]}},"schema":{"type":"object","required":["owner_id","system_id","docking_cost_per_ship_volume","office_rental_cost","type_id","reprocessing_efficiency","reprocessing_station_take","standing_owner_id","coordinates","services"],"properties":{"owner_id":{"description":"The entity that owns the station (e.g. the entity whose logo is on the station services bar)","type":"integer","format":"int32","title":"get_corporations_corporation_id_outposts_outpost_id_owner_id"},"system_id":{"description":"The ID of the solar system the outpost rests in","type":"integer","format":"int32","title":"get_corporations_corporation_id_outposts_outpost_id_system_id"},"docking_cost_per_ship_volume":{"type":"number","format":"float","title":"get_corporations_corporation_id_outposts_outpost_id_docking_cost_per_ship_volume","description":"docking_cost_per_ship_volume number"},"office_rental_cost":{"type":"integer","format":"int64","title":"get_corporations_corporation_id_outposts_outpost_id_office_rental_cost","description":"office_rental_cost integer"},"type_id":{"description":"The type ID of the given outpost","type":"integer","format":"int32","title":"get_corporations_corporation_id_outposts_outpost_id_type_id"},"reprocessing_efficiency":{"type":"number","format":"float","title":"get_corporations_corporation_id_outposts_outpost_id_reprocessing_efficiency","description":"reprocessing_efficiency number"},"reprocessing_station_take":{"type":"number","format":"float","title":"get_corporations_corporation_id_outposts_outpost_id_reprocessing_station_take","description":"reprocessing_station_take number"},"standing_owner_id":{"description":"The owner ID that sets the ability for someone to dock based on standings.","type":"integer","format":"int32","title":"get_corporations_corporation_id_outposts_outpost_id_standing_owner_id"},"coordinates":{"type":"object","required":["x","y","z"],"properties":{"x":{"type":"number","format":"double","title":"get_corporations_corporation_id_outposts_outpost_id_x","description":"x number"},"y":{"type":"number","format":"double","title":"get_corporations_corporation_id_outposts_outpost_id_y","description":"y number"},"z":{"type":"number","format":"double","title":"get_corporations_corporation_id_outposts_outpost_id_z","description":"z number"}},"title":"get_corporations_corporation_id_outposts_outpost_id_coordinates","description":"coordinates object"},"services":{"description":"A list of services the given outpost provides","type":"array","maxItems":27,"items":{"type":"object","required":["service_name","minimum_standing","surcharge_per_bad_standing","discount_per_good_standing"],"properties":{"service_name":{"type":"string","enum":["Bounty Missions","Assassination Missions","Courier Missions","Interbus","Reprocessing Plant","Refinery","Market","Black Market","Stock Exchange","Cloning","Surgery","DNA Therapy","Repair Facilities","Factory","Laboratory","Gambling","Fitting","Paintshop","News","Storage","Insurance","Docking","Office Rental","Jump Clone Facility","Loyalty Point Store","Navy Offices","Security Office"],"title":"get_corporations_corporation_id_outposts_outpost_id_service_name","description":"service_name string"},"minimum_standing":{"type":"number","format":"double","title":"get_corporations_corporation_id_outposts_outpost_id_minimum_standing","description":"minimum_standing number"},"surcharge_per_bad_standing":{"type":"number","format":"double","title":"get_corporations_corporation_id_outposts_outpost_id_surcharge_per_bad_standing","description":"surcharge_per_bad_standing number"},"discount_per_good_standing":{"type":"number","format":"double","title":"get_corporations_corporation_id_outposts_outpost_id_discount_per_good_standing","description":"discount_per_good_standing number"}},"title":"get_corporations_corporation_id_outposts_outpost_id_service","description":"service object"},"title":"get_corporations_corporation_id_outposts_outpost_id_services"}},"title":"get_corporations_corporation_id_outposts_outpost_id_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":["Director"],"security":[{"evesso":["esi-corporations.read_outposts.v1"]}],"operationId":"get_corporations_corporation_id_outposts_outpost_id","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/dogma/attributes/":{"get":{"description":"Get a list of dogma attribute ids\n\n---\nAlternate route: `/dev/dogma/attributes/`\n\nAlternate route: `/legacy/dogma/attributes/`\n\nAlternate route: `/v1/dogma/attributes/`\n\n---\nThis route expires daily at 11:05","summary":"Get attributes","tags":["Dogma"],"responses":{"200":{"description":"A list of dogma attribute ids","examples":{"application/json":[1,2,3]},"schema":{"type":"array","maxItems":10000,"items":{"type":"integer","format":"int32","title":"get_dogma_attributes_200_ok","description":"200 ok integer"},"title":"get_dogma_attributes_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_dogma_attributes","x-alternate-versions":["dev","legacy","v1"]}},"/dogma/attributes/{attribute_id}/":{"get":{"description":"Get information on a dogma attribute\n\n---\nAlternate route: `/dev/dogma/attributes/{attribute_id}/`\n\nAlternate route: `/legacy/dogma/attributes/{attribute_id}/`\n\nAlternate route: `/v1/dogma/attributes/{attribute_id}/`\n\n---\nThis route expires daily at 11:05","summary":"Get attribute information","tags":["Dogma"],"parameters":[{"name":"attribute_id","in":"path","description":"A dogma attribute ID","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Information about a dogma attribute","examples":{"application/json":{"attribute_id":20,"name":"speedFactor","description":"Factor by which topspeed increases.","icon_id":1389,"default_value":1,"published":true,"display_name":"Maximum Velocity Bonus","unit_id":124,"high_is_good":true}},"schema":{"type":"object","required":["attribute_id"],"properties":{"attribute_id":{"type":"integer","format":"int32","title":"get_dogma_attributes_attribute_id_attribute_id","description":"attribute_id integer"},"name":{"type":"string","title":"get_dogma_attributes_attribute_id_name","description":"name string"},"description":{"type":"string","title":"get_dogma_attributes_attribute_id_description","description":"description string"},"icon_id":{"type":"integer","format":"int32","title":"get_dogma_attributes_attribute_id_icon_id","description":"icon_id integer"},"default_value":{"type":"number","format":"float","title":"get_dogma_attributes_attribute_id_default_value","description":"default_value number"},"published":{"type":"boolean","title":"get_dogma_attributes_attribute_id_published","description":"published boolean"},"display_name":{"type":"string","title":"get_dogma_attributes_attribute_id_display_name","description":"display_name string"},"unit_id":{"type":"integer","format":"int32","title":"get_dogma_attributes_attribute_id_unit_id","description":"unit_id integer"},"stackable":{"type":"boolean","title":"get_dogma_attributes_attribute_id_stackable","description":"stackable boolean"},"high_is_good":{"type":"boolean","title":"get_dogma_attributes_attribute_id_high_is_good","description":"high_is_good boolean"}},"title":"get_dogma_attributes_attribute_id_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"Dogma attribute not found","schema":{"type":"object","title":"get_dogma_attributes_attribute_id_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"get_dogma_attributes_attribute_id_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_dogma_attributes_attribute_id","x-alternate-versions":["dev","legacy","v1"]}},"/dogma/effects/":{"get":{"description":"Get a list of dogma effect ids\n\n---\nAlternate route: `/dev/dogma/effects/`\n\nAlternate route: `/legacy/dogma/effects/`\n\nAlternate route: `/v1/dogma/effects/`\n\n---\nThis route expires daily at 11:05","summary":"Get effects","tags":["Dogma"],"responses":{"200":{"description":"A list of dogma effect ids","examples":{"application/json":[1,2,3]},"schema":{"type":"array","maxItems":10000,"items":{"type":"integer","format":"int32","title":"get_dogma_effects_200_ok","description":"200 ok integer"},"title":"get_dogma_effects_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_dogma_effects","x-alternate-versions":["dev","legacy","v1"]}},"/dogma/effects/{effect_id}/":{"get":{"description":"Get information on a dogma effect\n\n---\nAlternate route: `/dev/dogma/effects/{effect_id}/`\n\nAlternate route: `/v2/dogma/effects/{effect_id}/`\n\n---\nThis route expires daily at 11:05","summary":"Get effect information","tags":["Dogma"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"effect_id","in":"path","description":"A dogma effect ID","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Information about a dogma effect","examples":{"application/json":{"effect_id":12,"name":"hiPower","display_name":"High power","description":"Requires a high power slot.","icon_id":293,"effect_category":0,"pre_expression":131,"post_expression":131,"published":true}},"schema":{"type":"object","required":["effect_id"],"properties":{"effect_id":{"type":"integer","format":"int32","title":"get_dogma_effects_effect_id_effect_id","description":"effect_id integer"},"name":{"type":"string","title":"get_dogma_effects_effect_id_name","description":"name string"},"display_name":{"type":"string","title":"get_dogma_effects_effect_id_display_name","description":"display_name string"},"description":{"type":"string","title":"get_dogma_effects_effect_id_description","description":"description string"},"icon_id":{"type":"integer","format":"int32","title":"get_dogma_effects_effect_id_icon_id","description":"icon_id integer"},"effect_category":{"type":"integer","format":"int32","title":"get_dogma_effects_effect_id_effect_category","description":"effect_category integer"},"pre_expression":{"type":"integer","format":"int32","title":"get_dogma_effects_effect_id_pre_expression","description":"pre_expression integer"},"post_expression":{"type":"integer","format":"int32","title":"get_dogma_effects_effect_id_post_expression","description":"post_expression integer"},"is_offensive":{"type":"boolean","title":"get_dogma_effects_effect_id_is_offensive","description":"is_offensive boolean"},"is_assistance":{"type":"boolean","title":"get_dogma_effects_effect_id_is_assistance","description":"is_assistance boolean"},"disallow_auto_repeat":{"type":"boolean","title":"get_dogma_effects_effect_id_disallow_auto_repeat","description":"disallow_auto_repeat boolean"},"published":{"type":"boolean","title":"get_dogma_effects_effect_id_published","description":"published boolean"},"is_warp_safe":{"type":"boolean","title":"get_dogma_effects_effect_id_is_warp_safe","description":"is_warp_safe boolean"},"range_chance":{"type":"boolean","title":"get_dogma_effects_effect_id_range_chance","description":"range_chance boolean"},"electronic_chance":{"type":"boolean","title":"get_dogma_effects_effect_id_electronic_chance","description":"electronic_chance boolean"},"duration_attribute_id":{"type":"integer","format":"int32","title":"get_dogma_effects_effect_id_duration_attribute_id","description":"duration_attribute_id integer"},"tracking_speed_attribute_id":{"type":"integer","format":"int32","title":"get_dogma_effects_effect_id_tracking_speed_attribute_id","description":"tracking_speed_attribute_id integer"},"discharge_attribute_id":{"type":"integer","format":"int32","title":"get_dogma_effects_effect_id_discharge_attribute_id","description":"discharge_attribute_id integer"},"range_attribute_id":{"type":"integer","format":"int32","title":"get_dogma_effects_effect_id_range_attribute_id","description":"range_attribute_id integer"},"falloff_attribute_id":{"type":"integer","format":"int32","title":"get_dogma_effects_effect_id_falloff_attribute_id","description":"falloff_attribute_id integer"},"modifiers":{"type":"array","maxItems":100,"items":{"type":"object","required":["func"],"properties":{"func":{"type":"string","title":"get_dogma_effects_effect_id_func","description":"func string"},"domain":{"type":"string","title":"get_dogma_effects_effect_id_domain","description":"domain string"},"modified_attribute_id":{"type":"integer","format":"int32","title":"get_dogma_effects_effect_id_modified_attribute_id","description":"modified_attribute_id integer"},"modifying_attribute_id":{"type":"integer","format":"int32","title":"get_dogma_effects_effect_id_modifying_attribute_id","description":"modifying_attribute_id integer"},"effect_id":{"type":"integer","format":"int32","title":"get_dogma_effects_effect_id_effect_id","description":"effect_id integer"},"operator":{"type":"integer","format":"int32","title":"get_dogma_effects_effect_id_operator","description":"operator integer"}},"title":"get_dogma_effects_effect_id_modifier","description":"modifier object"},"title":"get_dogma_effects_effect_id_modifiers","description":"modifiers array"}},"title":"get_dogma_effects_effect_id_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"Dogma effect not found","schema":{"type":"object","title":"get_dogma_effects_effect_id_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"get_dogma_effects_effect_id_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_dogma_effects_effect_id","x-alternate-versions":["dev","v2"]}},"/fw/wars/":{"get":{"description":"Data about which NPC factions are at war\n\n---\nAlternate route: `/dev/fw/wars/`\n\nAlternate route: `/legacy/fw/wars/`\n\nAlternate route: `/v1/fw/wars/`\n\n---\nThis route expires daily at 11:05","summary":"Data about which NPC factions are at war","tags":["Faction Warfare"],"responses":{"200":{"description":"A list of NPC factions at war","examples":{"application/json":[{"faction_id":500001,"against_id":500002}]},"schema":{"type":"array","description":"List of factions at war","maxItems":8,"items":{"type":"object","required":["faction_id","against_id"],"properties":{"faction_id":{"type":"integer","format":"int32","title":"get_fw_wars_faction_id","description":"faction_id integer"},"against_id":{"description":"The faction ID of the enemy faction.","type":"integer","format":"int32","title":"get_fw_wars_against_id"}},"title":"get_fw_wars_200_ok","description":"200 ok object"},"title":"get_fw_wars_ok"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_fw_wars","x-alternate-versions":["dev","legacy","v1"]}},"/fw/stats/":{"get":{"summary":"An overview of statistics about factions involved in faction warfare","description":"Statistical overviews of factions involved in faction warfare\n\n---\nAlternate route: `/dev/fw/stats/`\n\nAlternate route: `/legacy/fw/stats/`\n\nAlternate route: `/v1/fw/stats/`\n\n---\nThis route expires daily at 11:05","tags":["Faction Warfare"],"responses":{"200":{"description":"Per faction breakdown of faction warfare statistics","examples":{"application/json":[{"faction_id":500001,"pilots":28863,"systems_controlled":20,"kills":{"yesterday":136,"last_week":893,"total":684350},"victory_points":{"yesterday":15980,"last_week":102640,"total":52658260}}]},"schema":{"type":"array","maxItems":4,"items":{"type":"object","required":["faction_id","pilots","systems_controlled","kills","victory_points"],"properties":{"faction_id":{"type":"integer","format":"int32","title":"get_fw_stats_faction_id","description":"faction_id integer"},"pilots":{"description":"How many pilots fight for the given faction","type":"integer","format":"int32","title":"get_fw_stats_pilots"},"systems_controlled":{"description":"The number of solar systems controlled by the given faction","type":"integer","format":"int32","title":"get_fw_stats_systems_controlled"},"kills":{"type":"object","description":"Summary of kills against an enemy faction for the given faction","required":["yesterday","last_week","total"],"properties":{"yesterday":{"type":"integer","format":"int32","description":"Yesterday's total number of kills against enemy factions","title":"get_fw_stats_yesterday"},"last_week":{"type":"integer","format":"int32","description":"Last week's total number of kills against enemy factions","title":"get_fw_stats_last_week"},"total":{"type":"integer","format":"int32","description":"Total number of kills against enemy factions since faction warfare began","title":"get_fw_stats_total"}},"title":"get_fw_stats_kills"},"victory_points":{"type":"object","description":"Summary of victory points gained for the given faction","required":["yesterday","last_week","total"],"properties":{"yesterday":{"type":"integer","format":"int32","description":"Yesterday's victory points gained","title":"get_fw_stats_yesterday"},"last_week":{"type":"integer","format":"int32","description":"Last week's victory points gained","title":"get_fw_stats_last_week"},"total":{"type":"integer","format":"int32","description":"Total victory points gained since faction warfare began","title":"get_fw_stats_total"}},"title":"get_fw_stats_victory_points"}},"title":"get_fw_stats_200_ok","description":"200 ok object"},"title":"get_fw_stats_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_fw_stats","x-alternate-versions":["dev","legacy","v1"]}},"/fw/systems/":{"get":{"summary":"Ownership of faction warfare systems","description":"An overview of the current ownership of faction warfare solar systems\n\n---\nAlternate route: `/dev/fw/systems/`\n\nAlternate route: `/legacy/fw/systems/`\n\nAlternate route: `/v1/fw/systems/`\n\n---\nThis route is cached for up to 1800 seconds","tags":["Faction Warfare"],"responses":{"200":{"description":"All faction war solar systems","examples":{"application/json":[{"solar_system_id":30002096,"owner_faction_id":500001,"occupier_faction_id":500001,"victory_points":60,"victory_points_threshold":3000,"contested":true}]},"schema":{"type":"array","maxItems":171,"items":{"type":"object","required":["solar_system_id","occupier_faction_id","owner_faction_id","victory_points","victory_points_threshold","contested"],"properties":{"solar_system_id":{"type":"integer","format":"int32","title":"get_fw_systems_solar_system_id","description":"solar_system_id integer"},"owner_faction_id":{"type":"integer","format":"int32","title":"get_fw_systems_owner_faction_id","description":"owner_faction_id integer"},"occupier_faction_id":{"type":"integer","format":"int32","title":"get_fw_systems_occupier_faction_id","description":"occupier_faction_id integer"},"victory_points":{"type":"integer","format":"int32","title":"get_fw_systems_victory_points","description":"victory_points integer"},"victory_points_threshold":{"type":"integer","format":"int32","title":"get_fw_systems_victory_points_threshold","description":"victory_points_threshold integer"},"contested":{"type":"boolean","title":"get_fw_systems_contested","description":"contested boolean"}},"title":"get_fw_systems_200_ok","description":"200 ok object"},"title":"get_fw_systems_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_fw_systems","x-cached-seconds":1800,"x-alternate-versions":["dev","legacy","v1"]}},"/fw/leaderboards/":{"get":{"summary":"List of the top factions in faction warfare","description":"Top 4 leaderboard of factions for kills and victory points separated by total, last week and yesterday.\n\n---\nAlternate route: `/dev/fw/leaderboards/`\n\nAlternate route: `/legacy/fw/leaderboards/`\n\nAlternate route: `/v1/fw/leaderboards/`\n\n---\nThis route expires daily at 11:05","tags":["Faction Warfare"],"responses":{"200":{"description":"Corporation leaderboard of kills and victory points within faction warfare.","examples":{"application/json":{"kills":{"yesterday":[{"faction_id":500001,"amount":100},{"faction_id":500004,"amount":50}],"last_week":[{"faction_id":500001,"amount":730},{"faction_id":500004,"amount":671}],"active_total":[{"faction_id":500004,"amount":832273},{"faction_id":500001,"amount":687915}]},"victory_points":{"yesterday":[{"faction_id":500002,"amount":5000},{"faction_id":500003,"amount":3500}],"last_week":[{"faction_id":500001,"amount":97360},{"faction_id":500004,"amount":84980}],"active_total":[{"faction_id":500001,"amount":53130500},{"faction_id":500004,"amount":50964263}]}}},"schema":{"type":"object","required":["kills","victory_points"],"properties":{"kills":{"type":"object","description":"Top 4 rankings of factions by number of kills from yesterday, last week and in total.","required":["yesterday","last_week","active_total"],"properties":{"yesterday":{"description":"Top 4 ranking of factions by kills in the past day","type":"array","maxItems":4,"items":{"type":"object","properties":{"faction_id":{"type":"integer","format":"int32","title":"get_fw_leaderboards_faction_id","description":"faction_id integer"},"amount":{"description":"Amount of kills","type":"integer","format":"int32","title":"get_fw_leaderboards_amount"}},"title":"get_fw_leaderboards_yesterday","description":"yesterday object"},"title":"get_fw_leaderboards_yesterday"},"last_week":{"description":"Top 4 ranking of factions by kills in the past week","type":"array","maxItems":4,"items":{"type":"object","properties":{"faction_id":{"type":"integer","format":"int32","title":"get_fw_leaderboards_faction_id","description":"faction_id integer"},"amount":{"description":"Amount of kills","type":"integer","format":"int32","title":"get_fw_leaderboards_amount"}},"title":"get_fw_leaderboards_last_week","description":"last_week object"},"title":"get_fw_leaderboards_last_week"},"active_total":{"description":"Top 4 ranking of factions active in faction warfare by total kills. A faction is considered \"active\" if they have participated in faction warfare in the past 14 days.","type":"array","maxItems":4,"items":{"type":"object","properties":{"faction_id":{"type":"integer","format":"int32","title":"get_fw_leaderboards_faction_id","description":"faction_id integer"},"amount":{"description":"Amount of kills","type":"integer","format":"int32","title":"get_fw_leaderboards_amount"}},"title":"get_fw_leaderboards_active_total","description":"active_total object"},"title":"get_fw_leaderboards_active_total"}},"title":"get_fw_leaderboards_kills"},"victory_points":{"description":"Top 4 rankings of factions by victory points from yesterday, last week and in total","type":"object","required":["yesterday","last_week","active_total"],"properties":{"yesterday":{"description":"Top 4 ranking of factions by victory points in the past day","type":"array","maxItems":4,"items":{"type":"object","properties":{"faction_id":{"type":"integer","format":"int32","title":"get_fw_leaderboards_faction_id","description":"faction_id integer"},"amount":{"description":"Amount of victory points","type":"integer","format":"int32","title":"get_fw_leaderboards_amount"}},"title":"get_fw_leaderboards_yesterday","description":"yesterday object"},"title":"get_fw_leaderboards_yesterday"},"last_week":{"description":"Top 4 ranking of factions by victory points in the past week","type":"array","maxItems":4,"items":{"type":"object","properties":{"faction_id":{"type":"integer","format":"int32","title":"get_fw_leaderboards_faction_id","description":"faction_id integer"},"amount":{"description":"Amount of victory points","type":"integer","format":"int32","title":"get_fw_leaderboards_amount"}},"title":"get_fw_leaderboards_last_week","description":"last_week object"},"title":"get_fw_leaderboards_last_week"},"active_total":{"description":"Top 4 ranking of factions active in faction warfare by total victory points. A faction is considered \"active\" if they have participated in faction warfare in the past 14 days.","type":"array","maxItems":4,"items":{"type":"object","properties":{"faction_id":{"type":"integer","format":"int32","title":"get_fw_leaderboards_faction_id","description":"faction_id integer"},"amount":{"description":"Amount of victory points","type":"integer","format":"int32","title":"get_fw_leaderboards_amount"}},"title":"get_fw_leaderboards_active_total","description":"active_total object"},"title":"get_fw_leaderboards_active_total"}},"title":"get_fw_leaderboards_victory_points"}},"title":"get_fw_leaderboards_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_fw_leaderboards","x-alternate-versions":["dev","legacy","v1"]}},"/fw/leaderboards/characters/":{"get":{"summary":"List of the top pilots in faction warfare","description":"Top 100 leaderboard of pilots for kills and victory points separated by total, last week and yesterday.\n\n---\nAlternate route: `/dev/fw/leaderboards/characters/`\n\nAlternate route: `/legacy/fw/leaderboards/characters/`\n\nAlternate route: `/v1/fw/leaderboards/characters/`\n\n---\nThis route expires daily at 11:05","tags":["Faction Warfare"],"responses":{"200":{"description":"Character leaderboard of kills and victory points within faction warfare.","examples":{"application/json":{"kills":{"yesterday":[{"character_id":2112625428,"amount":34},{"character_id":95465499,"amount":20}],"last_week":[{"character_id":2112625428,"amount":100},{"character_id":95465499,"amount":70}],"active_total":[{"character_id":2112625428,"amount":10000},{"character_id":95465499,"amount":8500}]},"victory_points":{"yesterday":[{"character_id":2112625428,"amount":620},{"character_id":95465499,"amount":550}],"last_week":[{"character_id":2112625428,"amount":2660},{"character_id":95465499,"amount":2000}],"active_total":[{"character_id":2112625428,"amount":1239158},{"character_id":95465499,"amount":1139029}]}}},"schema":{"type":"object","required":["kills","victory_points"],"properties":{"kills":{"type":"object","description":"Top 100 rankings of pilots by number of kills from yesterday, last week and in total.","required":["yesterday","last_week","active_total"],"properties":{"yesterday":{"description":"Top 100 ranking of pilots by kills in the past day","type":"array","maxItems":100,"items":{"type":"object","properties":{"character_id":{"type":"integer","format":"int32","title":"get_fw_leaderboards_characters_character_id","description":"character_id integer"},"amount":{"description":"Amount of kills","type":"integer","format":"int32","title":"get_fw_leaderboards_characters_amount"}},"title":"get_fw_leaderboards_characters_yesterday","description":"yesterday object"},"title":"get_fw_leaderboards_characters_yesterday"},"last_week":{"description":"Top 100 ranking of pilots by kills in the past week","type":"array","maxItems":100,"items":{"type":"object","properties":{"character_id":{"type":"integer","format":"int32","title":"get_fw_leaderboards_characters_character_id","description":"character_id integer"},"amount":{"description":"Amount of kills","type":"integer","format":"int32","title":"get_fw_leaderboards_characters_amount"}},"title":"get_fw_leaderboards_characters_last_week","description":"last_week object"},"title":"get_fw_leaderboards_characters_last_week"},"active_total":{"description":"Top 100 ranking of pilots active in faction warfare by total kills. A pilot is considered \"active\" if they have participated in faction warfare in the past 14 days.","type":"array","maxItems":100,"items":{"type":"object","properties":{"character_id":{"type":"integer","format":"int32","title":"get_fw_leaderboards_characters_character_id","description":"character_id integer"},"amount":{"description":"Amount of kills","type":"integer","format":"int32","title":"get_fw_leaderboards_characters_amount"}},"title":"get_fw_leaderboards_characters_active_total","description":"active_total object"},"title":"get_fw_leaderboards_characters_active_total"}},"title":"get_fw_leaderboards_characters_kills"},"victory_points":{"description":"Top 100 rankings of pilots by victory points from yesterday, last week and in total","type":"object","required":["yesterday","last_week","active_total"],"properties":{"yesterday":{"description":"Top 100 ranking of pilots by victory points in the past day","type":"array","maxItems":100,"items":{"type":"object","properties":{"character_id":{"type":"integer","format":"int32","title":"get_fw_leaderboards_characters_character_id","description":"character_id integer"},"amount":{"description":"Amount of victory points","type":"integer","format":"int32","title":"get_fw_leaderboards_characters_amount"}},"title":"get_fw_leaderboards_characters_yesterday","description":"yesterday object"},"title":"get_fw_leaderboards_characters_yesterday"},"last_week":{"description":"Top 100 ranking of pilots by victory points in the past week","type":"array","maxItems":100,"items":{"type":"object","properties":{"character_id":{"type":"integer","format":"int32","title":"get_fw_leaderboards_characters_character_id","description":"character_id integer"},"amount":{"description":"Amount of victory points","type":"integer","format":"int32","title":"get_fw_leaderboards_characters_amount"}},"title":"get_fw_leaderboards_characters_last_week","description":"last_week object"},"title":"get_fw_leaderboards_characters_last_week"},"active_total":{"description":"Top 100 ranking of pilots active in faction warfare by total victory points. A pilot is considered \"active\" if they have participated in faction warfare in the past 14 days.","type":"array","maxItems":100,"items":{"type":"object","properties":{"character_id":{"type":"integer","format":"int32","title":"get_fw_leaderboards_characters_character_id","description":"character_id integer"},"amount":{"description":"Amount of victory points","type":"integer","format":"int32","title":"get_fw_leaderboards_characters_amount"}},"title":"get_fw_leaderboards_characters_active_total","description":"active_total object"},"title":"get_fw_leaderboards_characters_active_total"}},"title":"get_fw_leaderboards_characters_victory_points"}},"title":"get_fw_leaderboards_characters_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_fw_leaderboards_characters","x-alternate-versions":["dev","legacy","v1"]}},"/fw/leaderboards/corporations/":{"get":{"summary":"List of the top corporations in faction warfare","description":"Top 10 leaderboard of corporations for kills and victory points separated by total, last week and yesterday.\n\n---\nAlternate route: `/dev/fw/leaderboards/corporations/`\n\nAlternate route: `/legacy/fw/leaderboards/corporations/`\n\nAlternate route: `/v1/fw/leaderboards/corporations/`\n\n---\nThis route expires daily at 11:05","tags":["Faction Warfare"],"responses":{"200":{"description":"Corporation leaderboard of kills and victory points within faction warfare.","examples":{"application/json":{"kills":{"yesterday":[{"corporation_id":1000180,"amount":51},{"corporation_id":1000182,"amount":39}],"last_week":[{"corporation_id":1000180,"amount":290},{"corporation_id":1000182,"amount":169}],"active_total":[{"corporation_id":1000180,"amount":81692},{"corporation_id":1000182,"amount":76793}]},"victory_points":{"yesterday":[{"corporation_id":1000180,"amount":12600},{"corporation_id":1000181,"amount":8240}],"last_week":[{"corporation_id":1000180,"amount":91980},{"corporation_id":1000181,"amount":58920}],"active_total":[{"corporation_id":1000180,"amount":18640927},{"corporation_id":1000181,"amount":18078265}]}}},"schema":{"type":"object","required":["kills","victory_points"],"properties":{"kills":{"type":"object","description":"Top 10 rankings of corporations by number of kills from yesterday, last week and in total.","required":["yesterday","last_week","active_total"],"properties":{"yesterday":{"description":"Top 10 ranking of corporations by kills in the past day","type":"array","maxItems":10,"items":{"type":"object","properties":{"corporation_id":{"type":"integer","format":"int32","title":"get_fw_leaderboards_corporations_corporation_id","description":"corporation_id integer"},"amount":{"description":"Amount of kills","type":"integer","format":"int32","title":"get_fw_leaderboards_corporations_amount"}},"title":"get_fw_leaderboards_corporations_yesterday","description":"yesterday object"},"title":"get_fw_leaderboards_corporations_yesterday"},"last_week":{"description":"Top 10 ranking of corporations by kills in the past week","type":"array","maxItems":10,"items":{"type":"object","properties":{"corporation_id":{"type":"integer","format":"int32","title":"get_fw_leaderboards_corporations_corporation_id","description":"corporation_id integer"},"amount":{"description":"Amount of kills","type":"integer","format":"int32","title":"get_fw_leaderboards_corporations_amount"}},"title":"get_fw_leaderboards_corporations_last_week","description":"last_week object"},"title":"get_fw_leaderboards_corporations_last_week"},"active_total":{"description":"Top 10 ranking of corporations active in faction warfare by total kills. A corporation is considered \"active\" if they have participated in faction warfare in the past 14 days.","type":"array","maxItems":10,"items":{"type":"object","properties":{"corporation_id":{"type":"integer","format":"int32","title":"get_fw_leaderboards_corporations_corporation_id","description":"corporation_id integer"},"amount":{"description":"Amount of kills","type":"integer","format":"int32","title":"get_fw_leaderboards_corporations_amount"}},"title":"get_fw_leaderboards_corporations_active_total","description":"active_total object"},"title":"get_fw_leaderboards_corporations_active_total"}},"title":"get_fw_leaderboards_corporations_kills"},"victory_points":{"description":"Top 10 rankings of corporations by victory points from yesterday, last week and in total","type":"object","required":["yesterday","last_week","active_total"],"properties":{"yesterday":{"description":"Top 10 ranking of corporations by victory points in the past day","type":"array","maxItems":10,"items":{"type":"object","properties":{"corporation_id":{"type":"integer","format":"int32","title":"get_fw_leaderboards_corporations_corporation_id","description":"corporation_id integer"},"amount":{"description":"Amount of victory points","type":"integer","format":"int32","title":"get_fw_leaderboards_corporations_amount"}},"title":"get_fw_leaderboards_corporations_yesterday","description":"yesterday object"},"title":"get_fw_leaderboards_corporations_yesterday"},"last_week":{"description":"Top 10 ranking of corporations by victory points in the past week","type":"array","maxItems":10,"items":{"type":"object","properties":{"corporation_id":{"type":"integer","format":"int32","title":"get_fw_leaderboards_corporations_corporation_id","description":"corporation_id integer"},"amount":{"description":"Amount of victory points","type":"integer","format":"int32","title":"get_fw_leaderboards_corporations_amount"}},"title":"get_fw_leaderboards_corporations_last_week","description":"last_week object"},"title":"get_fw_leaderboards_corporations_last_week"},"active_total":{"description":"Top 10 ranking of corporations active in faction warfare by total victory points. A corporation is considered \"active\" if they have participated in faction warfare in the past 14 days.","type":"array","maxItems":10,"items":{"type":"object","properties":{"corporation_id":{"type":"integer","format":"int32","title":"get_fw_leaderboards_corporations_corporation_id","description":"corporation_id integer"},"amount":{"description":"Amount of victory points","type":"integer","format":"int32","title":"get_fw_leaderboards_corporations_amount"}},"title":"get_fw_leaderboards_corporations_active_total","description":"active_total object"},"title":"get_fw_leaderboards_corporations_active_total"}},"title":"get_fw_leaderboards_corporations_victory_points"}},"title":"get_fw_leaderboards_corporations_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_fw_leaderboards_corporations","x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/fw/stats/":{"get":{"summary":"Overview of a corporation involved in faction warfare","description":"Statistics about a corporation involved in faction warfare\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/fw/stats/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/fw/stats/`\n\nAlternate route: `/v1/corporations/{corporation_id}/fw/stats/`\n\n---\nThis route expires daily at 11:05","tags":["Faction Warfare"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Faction warfare statistics for a given corporation","examples":{"application/json":{"faction_id":500001,"enlisted_on":"2017-10-17T00:00:00Z","pilots":28863,"kills":{"yesterday":136,"last_week":893,"total":684350},"victory_points":{"yesterday":15980,"last_week":102640,"total":52658260}}},"schema":{"type":"object","required":["kills","victory_points"],"properties":{"faction_id":{"type":"integer","format":"int32","description":"The faction the given corporation is enlisted to fight for. Will not be included if corporation is not enlisted in faction warfare","title":"get_corporations_corporation_id_fw_stats_faction_id"},"enlisted_on":{"type":"string","format":"date-time","description":"The enlistment date of the given corporation into faction warfare. Will not be included if corporation is not enlisted in faction warfare","title":"get_corporations_corporation_id_fw_stats_enlisted_on"},"pilots":{"type":"integer","format":"int32","description":"How many pilots the enlisted corporation has. Will not be included if corporation is not enlisted in faction warfare","title":"get_corporations_corporation_id_fw_stats_pilots"},"kills":{"type":"object","description":"Summary of kills done by the given corporation against enemy factions","required":["yesterday","last_week","total"],"properties":{"yesterday":{"type":"integer","format":"int32","description":"Yesterday's total number of kills by members of the given corporation against enemy factions","title":"get_corporations_corporation_id_fw_stats_yesterday"},"last_week":{"type":"integer","format":"int32","description":"Last week's total number of kills by members of the given corporation against enemy factions","title":"get_corporations_corporation_id_fw_stats_last_week"},"total":{"type":"integer","format":"int32","description":"Total number of kills by members of the given corporation against enemy factions since the corporation enlisted","title":"get_corporations_corporation_id_fw_stats_total"}},"title":"get_corporations_corporation_id_fw_stats_kills"},"victory_points":{"type":"object","description":"Summary of victory points gained by the given corporation for the enlisted faction","required":["yesterday","last_week","total"],"properties":{"yesterday":{"type":"integer","format":"int32","description":"Yesterday's victory points gained by members of the given corporation","title":"get_corporations_corporation_id_fw_stats_yesterday"},"last_week":{"type":"integer","format":"int32","description":"Last week's victory points gained by members of the given corporation","title":"get_corporations_corporation_id_fw_stats_last_week"},"total":{"type":"integer","format":"int32","description":"Total victory points gained since the given corporation enlisted","title":"get_corporations_corporation_id_fw_stats_total"}},"title":"get_corporations_corporation_id_fw_stats_victory_points"}},"title":"get_corporations_corporation_id_fw_stats_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":[],"security":[{"evesso":["esi-corporations.read_fw_stats.v1"]}],"operationId":"get_corporations_corporation_id_fw_stats","x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/fw/stats/":{"get":{"summary":"Overview of a character involved in faction warfare","description":"Statistical overview of a character involved in faction warfare\n\n---\nAlternate route: `/dev/characters/{character_id}/fw/stats/`\n\nAlternate route: `/legacy/characters/{character_id}/fw/stats/`\n\nAlternate route: `/v1/characters/{character_id}/fw/stats/`\n\n---\nThis route expires daily at 11:05","tags":["Faction Warfare"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Faction warfare statistics for a given character","examples":{"application/json":{"faction_id":500001,"enlisted_on":"2017-10-17T00:00:00Z","kills":{"yesterday":136,"last_week":893,"total":684350},"victory_points":{"yesterday":15980,"last_week":102640,"total":52658260}}},"schema":{"type":"object","required":["kills","victory_points"],"properties":{"faction_id":{"type":"integer","format":"int32","description":"The faction the given character is enlisted to fight for. Will not be included if character is not enlisted in faction warfare","title":"get_characters_character_id_fw_stats_faction_id"},"enlisted_on":{"type":"string","format":"date-time","description":"The enlistment date of the given character into faction warfare. Will not be included if character is not enlisted in faction warfare","title":"get_characters_character_id_fw_stats_enlisted_on"},"current_rank":{"type":"integer","format":"int32","description":"The given character's current faction rank","minimum":0,"maximum":9,"title":"get_characters_character_id_fw_stats_current_rank"},"highest_rank":{"type":"integer","format":"int32","description":"The given character's highest faction rank achieved","minimum":0,"maximum":9,"title":"get_characters_character_id_fw_stats_highest_rank"},"kills":{"type":"object","description":"Summary of kills done by the given character against enemy factions","required":["yesterday","last_week","total"],"properties":{"yesterday":{"type":"integer","format":"int32","description":"Yesterday's total number of kills by a given character against enemy factions","title":"get_characters_character_id_fw_stats_yesterday"},"last_week":{"type":"integer","format":"int32","description":"Last week's total number of kills by a given character against enemy factions","title":"get_characters_character_id_fw_stats_last_week"},"total":{"type":"integer","format":"int32","description":"Total number of kills by a given character against enemy factions since the character enlisted","title":"get_characters_character_id_fw_stats_total"}},"title":"get_characters_character_id_fw_stats_kills"},"victory_points":{"type":"object","description":"Summary of victory points gained by the given character for the enlisted faction","required":["yesterday","last_week","total"],"properties":{"yesterday":{"type":"integer","format":"int32","description":"Yesterday's victory points gained by the given character","title":"get_characters_character_id_fw_stats_yesterday"},"last_week":{"type":"integer","format":"int32","description":"Last week's victory points gained by the given character","title":"get_characters_character_id_fw_stats_last_week"},"total":{"type":"integer","format":"int32","description":"Total victory points gained since the given character enlisted","title":"get_characters_character_id_fw_stats_total"}},"title":"get_characters_character_id_fw_stats_victory_points"}},"title":"get_characters_character_id_fw_stats_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-characters.read_fw_stats.v1"]}],"operationId":"get_characters_character_id_fw_stats","x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/fittings/{fitting_id}/":{"delete":{"description":"Delete a fitting from a character\n\n---\nAlternate route: `/dev/characters/{character_id}/fittings/{fitting_id}/`\n\nAlternate route: `/legacy/characters/{character_id}/fittings/{fitting_id}/`\n\nAlternate route: `/v1/characters/{character_id}/fittings/{fitting_id}/`\n","summary":"Delete fitting","tags":["Fittings"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"name":"fitting_id","in":"path","description":"ID for a fitting of this character","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"204":{"description":"Fitting deleted"},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-fittings.write_fittings.v1"]}],"operationId":"delete_characters_character_id_fittings_fitting_id","x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/fittings/":{"get":{"description":"Return fittings of a character\n\n---\nAlternate route: `/dev/characters/{character_id}/fittings/`\n\nAlternate route: `/legacy/characters/{character_id}/fittings/`\n\nAlternate route: `/v1/characters/{character_id}/fittings/`\n\n---\nThis route is cached for up to 300 seconds","summary":"Get fittings","tags":["Fittings"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of fittings","examples":{"application/json":[{"fitting_id":1,"name":"Best Vindicator","description":"Awesome Vindi fitting","ship_type_id":123,"items":[{"type_id":1234,"flag":12,"quantity":1}]}]},"schema":{"type":"array","maxItems":250,"items":{"type":"object","required":["fitting_id","name","description","ship_type_id","items"],"properties":{"fitting_id":{"type":"integer","format":"int32","title":"get_characters_character_id_fittings_fitting_id","description":"fitting_id integer"},"name":{"type":"string","title":"get_characters_character_id_fittings_name","description":"name string"},"description":{"type":"string","title":"get_characters_character_id_fittings_description","description":"description string"},"ship_type_id":{"type":"integer","format":"int32","title":"get_characters_character_id_fittings_ship_type_id","description":"ship_type_id integer"},"items":{"type":"array","maxItems":255,"items":{"type":"object","required":["type_id","flag","quantity"],"properties":{"type_id":{"type":"integer","format":"int32","title":"get_characters_character_id_fittings_type_id","description":"type_id integer"},"flag":{"type":"integer","format":"int32","title":"get_characters_character_id_fittings_flag","description":"flag integer"},"quantity":{"type":"integer","format":"int32","title":"get_characters_character_id_fittings_quantity","description":"quantity integer"}},"title":"get_characters_character_id_fittings_item","description":"item object"},"title":"get_characters_character_id_fittings_items","description":"items array"}},"title":"get_characters_character_id_fittings_200_ok","description":"200 ok object"},"title":"get_characters_character_id_fittings_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-fittings.read_fittings.v1"]}],"operationId":"get_characters_character_id_fittings","x-cached-seconds":300,"x-alternate-versions":["dev","legacy","v1"]},"post":{"description":"Save a new fitting for a character\n\n---\nAlternate route: `/dev/characters/{character_id}/fittings/`\n\nAlternate route: `/legacy/characters/{character_id}/fittings/`\n\nAlternate route: `/v1/characters/{character_id}/fittings/`\n","summary":"Create fitting","tags":["Fittings"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"name":"fitting","in":"body","description":"Details about the new fitting","schema":{"type":"object","required":["name","description","ship_type_id","items"],"properties":{"name":{"type":"string","minLength":1,"maxLength":50,"title":"post_characters_character_id_fittings_name","description":"name string"},"description":{"type":"string","minLength":0,"maxLength":500,"title":"post_characters_character_id_fittings_description","description":"description string"},"ship_type_id":{"type":"integer","format":"int32","title":"post_characters_character_id_fittings_ship_type_id","description":"ship_type_id integer"},"items":{"type":"array","minItems":1,"maxItems":255,"items":{"type":"object","required":["type_id","flag","quantity"],"properties":{"type_id":{"type":"integer","format":"int32","title":"post_characters_character_id_fittings_type_id","description":"type_id integer"},"flag":{"type":"integer","format":"int32","title":"post_characters_character_id_fittings_flag","description":"flag integer"},"quantity":{"type":"integer","format":"int32","title":"post_characters_character_id_fittings_quantity","description":"quantity integer"}},"title":"post_characters_character_id_fittings_item","description":"item object"},"title":"post_characters_character_id_fittings_items","description":"items array"}},"title":"post_characters_character_id_fittings_fitting","description":"fitting object"}},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"201":{"description":"A list of fittings","examples":{"application/json":{"fitting_id":2}},"schema":{"type":"object","required":["fitting_id"],"properties":{"fitting_id":{"type":"integer","format":"int32","title":"post_characters_character_id_fittings_fitting_id","description":"fitting_id integer"}},"title":"post_characters_character_id_fittings_created","description":"201 created object"}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-fittings.write_fittings.v1"]}],"operationId":"post_characters_character_id_fittings","x-alternate-versions":["dev","legacy","v1"]}},"/fleets/{fleet_id}/":{"get":{"description":"Return details about a fleet\n\n---\nAlternate route: `/dev/fleets/{fleet_id}/`\n\nAlternate route: `/legacy/fleets/{fleet_id}/`\n\nAlternate route: `/v1/fleets/{fleet_id}/`\n\n---\nThis route is cached for up to 5 seconds","summary":"Get fleet information","tags":["Fleets"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"fleet_id","in":"path","description":"ID for a fleet","required":true,"type":"integer","format":"int64"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Details about a fleet","examples":{"application/json":{"motd":"This is an awesome fleet!","is_free_move":false,"is_registered":false,"is_voice_enabled":false}},"schema":{"type":"object","required":["motd","is_free_move","is_registered","is_voice_enabled"],"properties":{"motd":{"type":"string","description":"Fleet MOTD in CCP flavoured HTML","title":"get_fleets_fleet_id_motd"},"is_free_move":{"type":"boolean","description":"Is free-move enabled","title":"get_fleets_fleet_id_is_free_move"},"is_registered":{"type":"boolean","description":"Does the fleet have an active fleet advertisement","title":"get_fleets_fleet_id_is_registered"},"is_voice_enabled":{"type":"boolean","description":"Is EVE Voice enabled","title":"get_fleets_fleet_id_is_voice_enabled"}},"title":"get_fleets_fleet_id_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"The fleet does not exist or you don't have access to it","schema":{"type":"object","title":"get_fleets_fleet_id_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"get_fleets_fleet_id_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-fleets.read_fleet.v1"]}],"operationId":"get_fleets_fleet_id","x-cached-seconds":5,"x-alternate-versions":["dev","legacy","v1"]},"put":{"description":"Update settings about a fleet\n\n---\nAlternate route: `/dev/fleets/{fleet_id}/`\n\nAlternate route: `/legacy/fleets/{fleet_id}/`\n\nAlternate route: `/v1/fleets/{fleet_id}/`\n","summary":"Update fleet","tags":["Fleets"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"fleet_id","in":"path","description":"ID for a fleet","required":true,"type":"integer","format":"int64"},{"name":"new_settings","in":"body","description":"What to update for this fleet","required":true,"schema":{"type":"object","properties":{"motd":{"type":"string","description":"New fleet MOTD in CCP flavoured HTML","title":"put_fleets_fleet_id_motd"},"is_free_move":{"type":"boolean","description":"Should free-move be enabled in the fleet","title":"put_fleets_fleet_id_is_free_move"}},"title":"put_fleets_fleet_id_new_settings","description":"new_settings object"}},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"204":{"description":"Fleet updated"},"400":{"description":"Invalid request body","schema":{"type":"object","title":"put_fleets_fleet_id_bad_request","description":"Bad request","properties":{"error":{"type":"string","description":"Bad request message","title":"put_fleets_fleet_id_400_bad_request"}}},"examples":{"application/json":{"error":"Bad request message"}}},"404":{"description":"The fleet does not exist or you don't have access to it","schema":{"type":"object","title":"put_fleets_fleet_id_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"put_fleets_fleet_id_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-fleets.write_fleet.v1"]}],"operationId":"put_fleets_fleet_id","x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/fleet/":{"get":{"description":"Return the fleet ID the character is in, if any.\n\n---\nAlternate route: `/dev/characters/{character_id}/fleet/`\n\nAlternate route: `/legacy/characters/{character_id}/fleet/`\n\nAlternate route: `/v1/characters/{character_id}/fleet/`\n\n---\nThis route is cached for up to 60 seconds","summary":"Get character fleet info","tags":["Fleets"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Details about the character's fleet","examples":{"application/json":{"fleet_id":1234567890,"wing_id":-1,"squad_id":-1,"role":"fleet_commander"}},"schema":{"type":"object","required":["fleet_id","wing_id","squad_id","role"],"properties":{"fleet_id":{"type":"integer","format":"int64","description":"The character's current fleet ID","title":"get_characters_character_id_fleet_fleet_id"},"wing_id":{"type":"integer","format":"int64","description":"ID of the wing the member is in. If not applicable, will be set to -1","title":"get_characters_character_id_fleet_wing_id"},"squad_id":{"type":"integer","format":"int64","description":"ID of the squad the member is in. If not applicable, will be set to -1","title":"get_characters_character_id_fleet_squad_id"},"role":{"type":"string","description":"Member\u2019s role in fleet","enum":["fleet_commander","squad_commander","squad_member","wing_commander"],"title":"get_characters_character_id_fleet_role"}},"title":"get_characters_character_id_fleet_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"The character is not in a fleet","schema":{"type":"object","title":"get_characters_character_id_fleet_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"get_characters_character_id_fleet_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-fleets.read_fleet.v1"]}],"operationId":"get_characters_character_id_fleet","x-cached-seconds":60,"x-alternate-versions":["dev","legacy","v1"]}},"/fleets/{fleet_id}/members/":{"get":{"description":"Return information about fleet members\n\n---\nAlternate route: `/dev/fleets/{fleet_id}/members/`\n\nAlternate route: `/legacy/fleets/{fleet_id}/members/`\n\nAlternate route: `/v1/fleets/{fleet_id}/members/`\n\n---\nThis route is cached for up to 5 seconds","summary":"Get fleet members","tags":["Fleets"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"fleet_id","in":"path","description":"ID for a fleet","required":true,"type":"integer","format":"int64"},{"$ref":"#/parameters/language"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of fleet members","examples":{"application/json":[{"character_id":93265215,"ship_type_id":33328,"wing_id":2073711261968,"squad_id":3129411261968,"role":"squad_commander","role_name":"Squad Commander (Boss)","join_time":"2016-04-29T12:34:56Z","takes_fleet_warp":true,"solar_system_id":30003729,"station_id":61000180}]},"schema":{"type":"array","maxItems":256,"items":{"type":"object","required":["character_id","ship_type_id","wing_id","squad_id","role","role_name","join_time","takes_fleet_warp","solar_system_id"],"properties":{"character_id":{"type":"integer","format":"int32","title":"get_fleets_fleet_id_members_character_id","description":"character_id integer"},"ship_type_id":{"type":"integer","format":"int32","title":"get_fleets_fleet_id_members_ship_type_id","description":"ship_type_id integer"},"wing_id":{"type":"integer","format":"int64","description":"ID of the wing the member is in. If not applicable, will be set to -1","title":"get_fleets_fleet_id_members_wing_id"},"squad_id":{"type":"integer","format":"int64","description":"ID of the squad the member is in. If not applicable, will be set to -1","title":"get_fleets_fleet_id_members_squad_id"},"role":{"type":"string","description":"Member\u2019s role in fleet","enum":["fleet_commander","wing_commander","squad_commander","squad_member"],"title":"get_fleets_fleet_id_members_role"},"role_name":{"type":"string","description":"Localized role names","title":"get_fleets_fleet_id_members_role_name"},"join_time":{"type":"string","format":"date-time","title":"get_fleets_fleet_id_members_join_time","description":"join_time string"},"takes_fleet_warp":{"type":"boolean","description":"Whether the member take fleet warps","title":"get_fleets_fleet_id_members_takes_fleet_warp"},"solar_system_id":{"type":"integer","format":"int32","description":"Solar system the member is located in","title":"get_fleets_fleet_id_members_solar_system_id"},"station_id":{"type":"integer","format":"int64","description":"Station in which the member is docked in, if applicable","title":"get_fleets_fleet_id_members_station_id"}},"title":"get_fleets_fleet_id_members_200_ok","description":"200 ok object"},"title":"get_fleets_fleet_id_members_ok","description":"200 ok array"},"headers":{"Content-Language":{"description":"The language used in the response","type":"string","enum":["de","en-us","fr","ja","ru","zh"]},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"The fleet does not exist or you don't have access to it","schema":{"type":"object","title":"get_fleets_fleet_id_members_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"get_fleets_fleet_id_members_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-fleets.read_fleet.v1"]}],"operationId":"get_fleets_fleet_id_members","x-cached-seconds":5,"x-alternate-versions":["dev","legacy","v1"]},"post":{"description":"Invite a character into the fleet. If a character has a CSPA charge set it is not possible to invite them to the fleet using ESI\n\n---\nAlternate route: `/dev/fleets/{fleet_id}/members/`\n\nAlternate route: `/legacy/fleets/{fleet_id}/members/`\n\nAlternate route: `/v1/fleets/{fleet_id}/members/`\n","summary":"Create fleet invitation","tags":["Fleets"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"fleet_id","in":"path","description":"ID for a fleet","required":true,"type":"integer","format":"int64"},{"name":"invitation","in":"body","description":"Details of the invitation","required":true,"schema":{"type":"object","required":["character_id","role"],"properties":{"character_id":{"type":"integer","format":"int32","description":"The character you want to invite","title":"post_fleets_fleet_id_members_character_id"},"role":{"type":"string","description":"If a character is invited with the `fleet_commander` role, neither `wing_id` or `squad_id` should be specified. If a character is invited with the `wing_commander` role, only `wing_id` should be specified. If a character is invited with the `squad_commander` role, both `wing_id` and `squad_id` should be specified. If a character is invited with the `squad_member` role, `wing_id` and `squad_id` should either both be specified or not specified at all. If they aren\u2019t specified, the invited character will join any squad with available positions.","enum":["fleet_commander","wing_commander","squad_commander","squad_member"],"title":"post_fleets_fleet_id_members_role"},"wing_id":{"type":"integer","format":"int64","minimum":0,"title":"post_fleets_fleet_id_members_wing_id","description":"wing_id integer"},"squad_id":{"type":"integer","format":"int64","minimum":0,"title":"post_fleets_fleet_id_members_squad_id","description":"squad_id integer"}},"title":"post_fleets_fleet_id_members_invitation","description":"invitation object"}},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"204":{"description":"Fleet invitation sent"},"404":{"description":"The fleet does not exist or you don't have access to it","schema":{"type":"object","title":"post_fleets_fleet_id_members_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"post_fleets_fleet_id_members_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"422":{"description":"Errors in invitation","examples":{"application/json":{"error":"missing wing_id"}},"schema":{"type":"object","properties":{"error":{"type":"string","description":"error message","title":"post_fleets_fleet_id_members_error"}},"title":"post_fleets_fleet_id_members_unprocessable_entity","description":"422 unprocessable entity object"}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-fleets.write_fleet.v1"]}],"operationId":"post_fleets_fleet_id_members","x-alternate-versions":["dev","legacy","v1"]}},"/fleets/{fleet_id}/members/{member_id}/":{"delete":{"description":"Kick a fleet member\n\n---\nAlternate route: `/dev/fleets/{fleet_id}/members/{member_id}/`\n\nAlternate route: `/legacy/fleets/{fleet_id}/members/{member_id}/`\n\nAlternate route: `/v1/fleets/{fleet_id}/members/{member_id}/`\n","summary":"Kick fleet member","tags":["Fleets"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"fleet_id","in":"path","description":"ID for a fleet","required":true,"type":"integer","format":"int64"},{"name":"member_id","in":"path","description":"The character ID of a member in this fleet","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"204":{"description":"Fleet member kicked"},"404":{"description":"The fleet does not exist or you don't have access to it","schema":{"type":"object","title":"delete_fleets_fleet_id_members_member_id_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"delete_fleets_fleet_id_members_member_id_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-fleets.write_fleet.v1"]}],"operationId":"delete_fleets_fleet_id_members_member_id","x-alternate-versions":["dev","legacy","v1"]},"put":{"description":"Move a fleet member around\n\n---\nAlternate route: `/dev/fleets/{fleet_id}/members/{member_id}/`\n\nAlternate route: `/legacy/fleets/{fleet_id}/members/{member_id}/`\n\nAlternate route: `/v1/fleets/{fleet_id}/members/{member_id}/`\n","summary":"Move fleet member","tags":["Fleets"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"fleet_id","in":"path","description":"ID for a fleet","required":true,"type":"integer","format":"int64"},{"name":"member_id","in":"path","description":"The character ID of a member in this fleet","required":true,"type":"integer","format":"int32"},{"name":"movement","in":"body","description":"Details of the invitation","required":true,"schema":{"type":"object","required":["role"],"properties":{"role":{"type":"string","description":"If a character is moved to the `fleet_commander` role, neither `wing_id` or `squad_id` should be specified. If a character is moved to the `wing_commander` role, only `wing_id` should be specified. If a character is moved to the `squad_commander` role, both `wing_id` and `squad_id` should be specified. If a character is moved to the `squad_member` role, both `wing_id` and `squad_id` should be specified.","enum":["fleet_commander","wing_commander","squad_commander","squad_member"],"title":"put_fleets_fleet_id_members_member_id_role"},"wing_id":{"type":"integer","format":"int64","minimum":0,"title":"put_fleets_fleet_id_members_member_id_wing_id","description":"wing_id integer"},"squad_id":{"type":"integer","format":"int64","minimum":0,"title":"put_fleets_fleet_id_members_member_id_squad_id","description":"squad_id integer"}},"title":"put_fleets_fleet_id_members_member_id_movement","description":"movement object"}},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"204":{"description":"Fleet invitation sent"},"404":{"description":"The fleet does not exist or you don't have access to it","schema":{"type":"object","title":"put_fleets_fleet_id_members_member_id_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"put_fleets_fleet_id_members_member_id_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"422":{"description":"Errors in invitation","examples":{"application/json":{"error":"missing wing_id"}},"schema":{"type":"object","properties":{"error":{"type":"string","description":"error message","title":"put_fleets_fleet_id_members_member_id_error"}},"title":"put_fleets_fleet_id_members_member_id_unprocessable_entity","description":"422 unprocessable entity object"}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-fleets.write_fleet.v1"]}],"operationId":"put_fleets_fleet_id_members_member_id","x-alternate-versions":["dev","legacy","v1"]}},"/fleets/{fleet_id}/wings/":{"get":{"description":"Return information about wings in a fleet\n\n---\nAlternate route: `/dev/fleets/{fleet_id}/wings/`\n\nAlternate route: `/legacy/fleets/{fleet_id}/wings/`\n\nAlternate route: `/v1/fleets/{fleet_id}/wings/`\n\n---\nThis route is cached for up to 5 seconds","summary":"Get fleet wings","tags":["Fleets"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"fleet_id","in":"path","description":"ID for a fleet","required":true,"type":"integer","format":"int64"},{"$ref":"#/parameters/language"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of fleet wings","examples":{"application/json":[{"name":"Wing 1","id":2073711261968,"squads":[{"name":"Squad 1","id":3129411261968}]}]},"schema":{"type":"array","maxItems":25,"items":{"type":"object","required":["name","id","squads"],"properties":{"name":{"type":"string","title":"get_fleets_fleet_id_wings_name","description":"name string"},"id":{"type":"integer","format":"int64","title":"get_fleets_fleet_id_wings_id","description":"id integer"},"squads":{"type":"array","maxItems":25,"items":{"type":"object","required":["name","id"],"properties":{"name":{"type":"string","title":"get_fleets_fleet_id_wings_name","description":"name string"},"id":{"type":"integer","format":"int64","title":"get_fleets_fleet_id_wings_id","description":"id integer"}},"title":"get_fleets_fleet_id_wings_squad","description":"squad object"},"title":"get_fleets_fleet_id_wings_squads","description":"squads array"}},"title":"get_fleets_fleet_id_wings_200_ok","description":"200 ok object"},"title":"get_fleets_fleet_id_wings_ok","description":"200 ok array"},"headers":{"Content-Language":{"description":"The language used in the response","type":"string","enum":["de","en-us","fr","ja","ru","zh"]},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"The fleet does not exist or you don't have access to it","schema":{"type":"object","title":"get_fleets_fleet_id_wings_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"get_fleets_fleet_id_wings_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-fleets.read_fleet.v1"]}],"operationId":"get_fleets_fleet_id_wings","x-cached-seconds":5,"x-alternate-versions":["dev","legacy","v1"]},"post":{"description":"Create a new wing in a fleet\n\n---\nAlternate route: `/dev/fleets/{fleet_id}/wings/`\n\nAlternate route: `/legacy/fleets/{fleet_id}/wings/`\n\nAlternate route: `/v1/fleets/{fleet_id}/wings/`\n","summary":"Create fleet wing","tags":["Fleets"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"fleet_id","in":"path","description":"ID for a fleet","required":true,"type":"integer","format":"int64"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"201":{"description":"Wing created","examples":{"application/json":{"wing_id":123}},"schema":{"type":"object","required":["wing_id"],"properties":{"wing_id":{"type":"integer","format":"int64","description":"The wing_id of the newly created wing","title":"post_fleets_fleet_id_wings_wing_id"}},"title":"post_fleets_fleet_id_wings_created","description":"201 created object"}},"404":{"description":"The fleet does not exist or you don't have access to it","schema":{"type":"object","title":"post_fleets_fleet_id_wings_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"post_fleets_fleet_id_wings_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-fleets.write_fleet.v1"]}],"operationId":"post_fleets_fleet_id_wings","x-alternate-versions":["dev","legacy","v1"]}},"/fleets/{fleet_id}/wings/{wing_id}/":{"delete":{"description":"Delete a fleet wing, only empty wings can be deleted. The wing may contain squads, but the squads must be empty\n\n---\nAlternate route: `/dev/fleets/{fleet_id}/wings/{wing_id}/`\n\nAlternate route: `/legacy/fleets/{fleet_id}/wings/{wing_id}/`\n\nAlternate route: `/v1/fleets/{fleet_id}/wings/{wing_id}/`\n","summary":"Delete fleet wing","tags":["Fleets"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"fleet_id","in":"path","description":"ID for a fleet","required":true,"type":"integer","format":"int64"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"name":"wing_id","in":"path","description":"The wing to delete","required":true,"type":"integer","format":"int64"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"204":{"description":"Wing deleted"},"404":{"description":"The fleet does not exist or you don't have access to it","schema":{"type":"object","title":"delete_fleets_fleet_id_wings_wing_id_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"delete_fleets_fleet_id_wings_wing_id_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-fleets.write_fleet.v1"]}],"operationId":"delete_fleets_fleet_id_wings_wing_id","x-alternate-versions":["dev","legacy","v1"]},"put":{"description":"Rename a fleet wing\n\n---\nAlternate route: `/dev/fleets/{fleet_id}/wings/{wing_id}/`\n\nAlternate route: `/legacy/fleets/{fleet_id}/wings/{wing_id}/`\n\nAlternate route: `/v1/fleets/{fleet_id}/wings/{wing_id}/`\n","summary":"Rename fleet wing","tags":["Fleets"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"fleet_id","in":"path","description":"ID for a fleet","required":true,"type":"integer","format":"int64"},{"name":"naming","in":"body","description":"New name of the wing","required":true,"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","maxLength":10,"title":"put_fleets_fleet_id_wings_wing_id_name","description":"name string"}},"title":"put_fleets_fleet_id_wings_wing_id_naming","description":"naming object"}},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"name":"wing_id","in":"path","description":"The wing to rename","required":true,"type":"integer","format":"int64"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"204":{"description":"Wing renamed"},"404":{"description":"The fleet does not exist or you don't have access to it","schema":{"type":"object","title":"put_fleets_fleet_id_wings_wing_id_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"put_fleets_fleet_id_wings_wing_id_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-fleets.write_fleet.v1"]}],"operationId":"put_fleets_fleet_id_wings_wing_id","x-alternate-versions":["dev","legacy","v1"]}},"/fleets/{fleet_id}/wings/{wing_id}/squads/":{"post":{"description":"Create a new squad in a fleet\n\n---\nAlternate route: `/dev/fleets/{fleet_id}/wings/{wing_id}/squads/`\n\nAlternate route: `/legacy/fleets/{fleet_id}/wings/{wing_id}/squads/`\n\nAlternate route: `/v1/fleets/{fleet_id}/wings/{wing_id}/squads/`\n","summary":"Create fleet squad","tags":["Fleets"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"fleet_id","in":"path","description":"ID for a fleet","required":true,"type":"integer","format":"int64"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"name":"wing_id","in":"path","description":"The wing_id to create squad in","required":true,"type":"integer","format":"int64"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"201":{"description":"Squad created","examples":{"application/json":{"squad_id":123}},"schema":{"type":"object","required":["squad_id"],"properties":{"squad_id":{"type":"integer","format":"int64","description":"The squad_id of the newly created squad","title":"post_fleets_fleet_id_wings_wing_id_squads_squad_id"}},"title":"post_fleets_fleet_id_wings_wing_id_squads_created","description":"201 created object"}},"404":{"description":"The fleet does not exist or you don't have access to it","schema":{"type":"object","title":"post_fleets_fleet_id_wings_wing_id_squads_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"post_fleets_fleet_id_wings_wing_id_squads_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-fleets.write_fleet.v1"]}],"operationId":"post_fleets_fleet_id_wings_wing_id_squads","x-alternate-versions":["dev","legacy","v1"]}},"/fleets/{fleet_id}/squads/{squad_id}/":{"delete":{"description":"Delete a fleet squad, only empty squads can be deleted\n\n---\nAlternate route: `/dev/fleets/{fleet_id}/squads/{squad_id}/`\n\nAlternate route: `/legacy/fleets/{fleet_id}/squads/{squad_id}/`\n\nAlternate route: `/v1/fleets/{fleet_id}/squads/{squad_id}/`\n","summary":"Delete fleet squad","tags":["Fleets"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"fleet_id","in":"path","description":"ID for a fleet","required":true,"type":"integer","format":"int64"},{"name":"squad_id","in":"path","description":"The squad to delete","required":true,"type":"integer","format":"int64"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"204":{"description":"Squad deleted"},"404":{"description":"The fleet does not exist or you don't have access to it","schema":{"type":"object","title":"delete_fleets_fleet_id_squads_squad_id_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"delete_fleets_fleet_id_squads_squad_id_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-fleets.write_fleet.v1"]}],"operationId":"delete_fleets_fleet_id_squads_squad_id","x-alternate-versions":["dev","legacy","v1"]},"put":{"description":"Rename a fleet squad\n\n---\nAlternate route: `/dev/fleets/{fleet_id}/squads/{squad_id}/`\n\nAlternate route: `/legacy/fleets/{fleet_id}/squads/{squad_id}/`\n\nAlternate route: `/v1/fleets/{fleet_id}/squads/{squad_id}/`\n","summary":"Rename fleet squad","tags":["Fleets"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"fleet_id","in":"path","description":"ID for a fleet","required":true,"type":"integer","format":"int64"},{"name":"naming","in":"body","description":"New name of the squad","required":true,"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","maxLength":10,"title":"put_fleets_fleet_id_squads_squad_id_name","description":"name string"}},"title":"put_fleets_fleet_id_squads_squad_id_naming","description":"naming object"}},{"name":"squad_id","in":"path","description":"The squad to rename","required":true,"type":"integer","format":"int64"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"204":{"description":"Squad renamed"},"404":{"description":"The fleet does not exist or you don't have access to it","schema":{"type":"object","title":"put_fleets_fleet_id_squads_squad_id_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"put_fleets_fleet_id_squads_squad_id_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-fleets.write_fleet.v1"]}],"operationId":"put_fleets_fleet_id_squads_squad_id","x-alternate-versions":["dev","legacy","v1"]}},"/incursions/":{"get":{"description":"Return a list of current incursions\n\n---\nAlternate route: `/dev/incursions/`\n\nAlternate route: `/legacy/incursions/`\n\nAlternate route: `/v1/incursions/`\n\n---\nThis route is cached for up to 300 seconds","summary":"List incursions","tags":["Incursions"],"responses":{"200":{"description":"A list of incursions","examples":{"application/json":[{"type":"Incursion","state":"mobilizing","influence":0.9,"has_boss":true,"faction_id":500019,"constellation_id":20000607,"staging_solar_system_id":30004154,"infested_solar_systems":[30004148,30004149,30004150,30004151,30004152,30004153,30004154]}]},"schema":{"type":"array","maxItems":100,"items":{"type":"object","required":["type","state","influence","has_boss","faction_id","constellation_id","staging_solar_system_id","infested_solar_systems"],"properties":{"type":{"type":"string","description":"The type of this incursion","title":"get_incursions_type"},"state":{"type":"string","enum":["withdrawing","mobilizing","established"],"description":"The state of this incursion","title":"get_incursions_state"},"influence":{"type":"number","format":"float","description":"Influence of this incursion as a float from 0 to 1","title":"get_incursions_influence"},"has_boss":{"type":"boolean","description":"Whether the final encounter has boss or not","title":"get_incursions_has_boss"},"faction_id":{"type":"integer","format":"int32","description":"The attacking faction's id","title":"get_incursions_faction_id"},"constellation_id":{"type":"integer","format":"int32","description":"The constellation id in which this incursion takes place","title":"get_incursions_constellation_id"},"staging_solar_system_id":{"type":"integer","format":"int32","description":"Staging solar system for this incursion","title":"get_incursions_staging_solar_system_id"},"infested_solar_systems":{"type":"array","description":"A list of infested solar system ids that are a part of this incursion","maxItems":100,"items":{"type":"integer","format":"int32","title":"get_incursions_infested_solar_system","description":"infested_solar_system integer"},"title":"get_incursions_infested_solar_systems"}},"title":"get_incursions_200_ok","description":"200 ok object"},"title":"get_incursions_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_incursions","x-cached-seconds":300,"x-alternate-versions":["dev","legacy","v1"]}},"/industry/facilities/":{"get":{"description":"Return a list of industry facilities\n\n---\nAlternate route: `/dev/industry/facilities/`\n\nAlternate route: `/legacy/industry/facilities/`\n\nAlternate route: `/v1/industry/facilities/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"List industry facilities","tags":["Industry"],"responses":{"200":{"description":"A list of facilities","examples":{"application/json":[{"facility_id":60012544,"tax":0.1,"owner_id":1000126,"type_id":2502,"solar_system_id":30000032,"region_id":10000001}]},"schema":{"type":"array","maxItems":10000,"items":{"type":"object","required":["facility_id","owner_id","type_id","solar_system_id","region_id"],"properties":{"facility_id":{"type":"integer","format":"int64","description":"ID of the facility","title":"get_industry_facilities_facility_id"},"tax":{"type":"number","format":"float","description":"Tax imposed by the facility","title":"get_industry_facilities_tax"},"owner_id":{"type":"integer","format":"int32","description":"Owner of the facility","title":"get_industry_facilities_owner_id"},"type_id":{"type":"integer","format":"int32","description":"Type ID of the facility","title":"get_industry_facilities_type_id"},"solar_system_id":{"type":"integer","format":"int32","description":"Solar system ID where the facility is","title":"get_industry_facilities_solar_system_id"},"region_id":{"type":"integer","format":"int32","description":"Region ID where the facility is","title":"get_industry_facilities_region_id"}},"title":"get_industry_facilities_200_ok","description":"200 ok object"},"title":"get_industry_facilities_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_industry_facilities","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/industry/systems/":{"get":{"description":"Return cost indices for solar systems\n\n---\nAlternate route: `/dev/industry/systems/`\n\nAlternate route: `/legacy/industry/systems/`\n\nAlternate route: `/v1/industry/systems/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"List solar system cost indices","tags":["Industry"],"responses":{"200":{"description":"A list of cost indicies","examples":{"application/json":[{"solar_system_id":30011392,"cost_indices":[{"activity":"invention","cost_index":0.00480411064973412}]}]},"schema":{"type":"array","maxItems":10000,"items":{"type":"object","required":["solar_system_id","cost_indices"],"properties":{"solar_system_id":{"type":"integer","format":"int32","title":"get_industry_systems_solar_system_id","description":"solar_system_id integer"},"cost_indices":{"type":"array","maxItems":10,"items":{"type":"object","required":["activity","cost_index"],"properties":{"activity":{"type":"string","enum":["copying","duplicating","invention","manufacturing","none","reaction","researching_material_efficiency","researching_technology","researching_time_efficiency","reverse_engineering"],"title":"get_industry_systems_activity","description":"activity string"},"cost_index":{"type":"number","format":"float","title":"get_industry_systems_cost_index","description":"cost_index number"}},"title":"get_industry_systems_cost_indice","description":"cost_indice object"},"title":"get_industry_systems_cost_indices","description":"cost_indices array"}},"title":"get_industry_systems_200_ok","description":"200 ok object"},"title":"get_industry_systems_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_industry_systems","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/industry/jobs/":{"get":{"summary":"List character industry jobs","description":"List industry jobs placed by a character\n\n---\nAlternate route: `/dev/characters/{character_id}/industry/jobs/`\n\nAlternate route: `/legacy/characters/{character_id}/industry/jobs/`\n\nAlternate route: `/v1/characters/{character_id}/industry/jobs/`\n\n---\nThis route is cached for up to 300 seconds","tags":["Industry"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"name":"include_completed","in":"query","description":"Whether retrieve completed character industry jobs as well","required":false,"type":"boolean"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Industry jobs placed by a character","examples":{"application/json":[{"job_id":229136101,"installer_id":498338451,"facility_id":60006382,"station_id":60006382,"activity_id":1,"blueprint_id":1015116533326,"blueprint_type_id":2047,"blueprint_location_id":60006382,"output_location_id":60006382,"runs":1,"cost":118.01,"licensed_runs":200,"status":"active","duration":548,"start_date":"2014-07-19T15:47:06Z","end_date":"2014-07-19T15:56:14Z"}]},"schema":{"type":"array","maxItems":2000,"items":{"type":"object","required":["job_id","installer_id","facility_id","station_id","activity_id","blueprint_id","blueprint_type_id","blueprint_location_id","output_location_id","runs","status","duration","start_date","end_date"],"properties":{"job_id":{"type":"integer","format":"int32","description":"Unique job ID","title":"get_characters_character_id_industry_jobs_job_id"},"installer_id":{"type":"integer","format":"int32","description":"ID of the character which installed this job","title":"get_characters_character_id_industry_jobs_installer_id"},"facility_id":{"type":"integer","format":"int64","description":"ID of the facility where this job is running","title":"get_characters_character_id_industry_jobs_facility_id"},"station_id":{"type":"integer","format":"int64","description":"ID of the station where industry facility is located","title":"get_characters_character_id_industry_jobs_station_id"},"activity_id":{"type":"integer","format":"int32","description":"Job activity ID","title":"get_characters_character_id_industry_jobs_activity_id"},"blueprint_id":{"type":"integer","format":"int64","title":"get_characters_character_id_industry_jobs_blueprint_id","description":"blueprint_id integer"},"blueprint_type_id":{"type":"integer","format":"int32","title":"get_characters_character_id_industry_jobs_blueprint_type_id","description":"blueprint_type_id integer"},"blueprint_location_id":{"type":"integer","format":"int64","description":"Location ID of the location from which the blueprint was installed. Normally a station ID, but can also be an asset (e.g. container) or corporation facility","title":"get_characters_character_id_industry_jobs_blueprint_location_id"},"output_location_id":{"type":"integer","format":"int64","description":"Location ID of the location to which the output of the job will be delivered. Normally a station ID, but can also be a corporation facility","title":"get_characters_character_id_industry_jobs_output_location_id"},"runs":{"type":"integer","format":"int32","description":"Number of runs for a manufacturing job, or number of copies to make for a blueprint copy","title":"get_characters_character_id_industry_jobs_runs"},"cost":{"type":"number","format":"double","description":"The sume of job installation fee and industry facility tax","title":"get_characters_character_id_industry_jobs_cost"},"licensed_runs":{"type":"integer","format":"int32","description":"Number of runs blueprint is licensed for","title":"get_characters_character_id_industry_jobs_licensed_runs"},"probability":{"type":"number","format":"float","description":"Chance of success for invention","title":"get_characters_character_id_industry_jobs_probability"},"product_type_id":{"type":"integer","format":"int32","description":"Type ID of product (manufactured, copied or invented)","title":"get_characters_character_id_industry_jobs_product_type_id"},"status":{"type":"string","enum":["active","cancelled","delivered","paused","ready","reverted"],"title":"get_characters_character_id_industry_jobs_status","description":"status string"},"duration":{"type":"integer","format":"int32","description":"Job duration in seconds","title":"get_characters_character_id_industry_jobs_duration"},"start_date":{"type":"string","format":"date-time","description":"Date and time when this job started","title":"get_characters_character_id_industry_jobs_start_date"},"end_date":{"type":"string","format":"date-time","description":"Date and time when this job finished","title":"get_characters_character_id_industry_jobs_end_date"},"pause_date":{"type":"string","format":"date-time","description":"Date and time when this job was paused (i.e. time when the facility where this job was installed went offline)","title":"get_characters_character_id_industry_jobs_pause_date"},"completed_date":{"type":"string","format":"date-time","description":"Date and time when this job was completed","title":"get_characters_character_id_industry_jobs_completed_date"},"completed_character_id":{"type":"integer","format":"int32","description":"ID of the character which completed this job","title":"get_characters_character_id_industry_jobs_completed_character_id"},"successful_runs":{"type":"integer","format":"int32","description":"Number of successful runs for this job. Equal to runs unless this is an invention job","title":"get_characters_character_id_industry_jobs_successful_runs"}},"title":"get_characters_character_id_industry_jobs_200_ok","description":"200 ok object"},"title":"get_characters_character_id_industry_jobs_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-industry.read_character_jobs.v1"]}],"operationId":"get_characters_character_id_industry_jobs","x-cached-seconds":300,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/mining/":{"get":{"summary":"Character mining ledger","description":"Paginated record of all mining done by a character for the past 30 days\n\n\n---\nAlternate route: `/dev/characters/{character_id}/mining/`\n\nAlternate route: `/legacy/characters/{character_id}/mining/`\n\nAlternate route: `/v1/characters/{character_id}/mining/`\n\n---\nThis route is cached for up to 600 seconds","tags":["Industry"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Mining ledger of a character","examples":{"application/json":[{"date":"2017-09-19","solar_system_id":30003707,"type_id":17471,"quantity":7004},{"date":"2017-09-18","solar_system_id":30003707,"type_id":17471,"quantity":5199}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["date","solar_system_id","type_id","quantity"],"properties":{"date":{"type":"string","format":"date","title":"get_characters_character_id_mining_date","description":"date string"},"solar_system_id":{"type":"integer","format":"int32","title":"get_characters_character_id_mining_solar_system_id","description":"solar_system_id integer"},"type_id":{"type":"integer","format":"int32","title":"get_characters_character_id_mining_type_id","description":"type_id integer"},"quantity":{"type":"integer","format":"int64","title":"get_characters_character_id_mining_quantity","description":"quantity integer"}},"title":"get_characters_character_id_mining_200_ok","description":"200 ok object"},"title":"get_characters_character_id_mining_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-industry.read_character_mining.v1"]}],"operationId":"get_characters_character_id_mining","x-cached-seconds":600,"x-alternate-versions":["dev","legacy","v1"]}},"/corporation/{corporation_id}/mining/observers/":{"get":{"summary":"Corporation mining observers","description":"Paginated list of all entities capable of observing and recording mining for a corporation\n\n\n---\nAlternate route: `/dev/corporation/{corporation_id}/mining/observers/`\n\nAlternate route: `/legacy/corporation/{corporation_id}/mining/observers/`\n\nAlternate route: `/v1/corporation/{corporation_id}/mining/observers/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\nRequires one of the following EVE corporation role(s): Accountant\n","tags":["Industry"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Observer list of a corporation","examples":{"application/json":[{"last_updated":"2017-09-19","observer_id":1,"observer_type":"structure"}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["last_updated","observer_id","observer_type"],"properties":{"last_updated":{"type":"string","format":"date","title":"get_corporation_corporation_id_mining_observers_last_updated","description":"last_updated string"},"observer_id":{"type":"integer","format":"int64","description":"The entity that was observing the asteroid field when it was mined.\n","title":"get_corporation_corporation_id_mining_observers_observer_id"},"observer_type":{"description":"The category of the observing entity","type":"string","enum":["structure"],"title":"get_corporation_corporation_id_mining_observers_observer_type"}},"title":"get_corporation_corporation_id_mining_observers_200_ok","description":"200 ok object"},"title":"get_corporation_corporation_id_mining_observers_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":["Accountant"],"security":[{"evesso":["esi-industry.read_corporation_mining.v1"]}],"operationId":"get_corporation_corporation_id_mining_observers","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/corporation/{corporation_id}/mining/observers/{observer_id}/":{"get":{"summary":"Observed corporation mining","description":"Paginated record of all mining seen by an observer\n\n\n---\nAlternate route: `/dev/corporation/{corporation_id}/mining/observers/{observer_id}/`\n\nAlternate route: `/legacy/corporation/{corporation_id}/mining/observers/{observer_id}/`\n\nAlternate route: `/v1/corporation/{corporation_id}/mining/observers/{observer_id}/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\nRequires one of the following EVE corporation role(s): Accountant\n","tags":["Industry"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"name":"observer_id","in":"path","description":"A mining observer id","required":true,"type":"integer","format":"int64"},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Mining ledger of an observer","examples":{"application/json":[{"last_updated":"2017-09-19","character_id":95465499,"recorded_corporation_id":109299958,"type_id":1230,"quantity":500}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["last_updated","character_id","recorded_corporation_id","type_id","quantity"],"properties":{"last_updated":{"type":"string","format":"date","title":"get_corporation_corporation_id_mining_observers_observer_id_last_updated","description":"last_updated string"},"character_id":{"type":"integer","format":"int32","description":"The character that did the mining\n","title":"get_corporation_corporation_id_mining_observers_observer_id_character_id"},"recorded_corporation_id":{"description":"The corporation id of the character at the time data was recorded.\n","type":"integer","format":"int32","title":"get_corporation_corporation_id_mining_observers_observer_id_recorded_corporation_id"},"type_id":{"type":"integer","format":"int32","title":"get_corporation_corporation_id_mining_observers_observer_id_type_id","description":"type_id integer"},"quantity":{"type":"integer","format":"int64","title":"get_corporation_corporation_id_mining_observers_observer_id_quantity","description":"quantity integer"}},"title":"get_corporation_corporation_id_mining_observers_observer_id_200_ok","description":"200 ok object"},"title":"get_corporation_corporation_id_mining_observers_observer_id_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":["Accountant"],"security":[{"evesso":["esi-industry.read_corporation_mining.v1"]}],"operationId":"get_corporation_corporation_id_mining_observers_observer_id","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/industry/jobs/":{"get":{"summary":"List corporation industry jobs","description":"List industry jobs run by a corporation\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/industry/jobs/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/industry/jobs/`\n\nAlternate route: `/v1/corporations/{corporation_id}/industry/jobs/`\n\n---\nThis route is cached for up to 300 seconds\n\n---\nRequires one of the following EVE corporation role(s): FactoryManager\n","tags":["Industry"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"name":"include_completed","in":"query","description":"Whether retrieve completed industry jobs as well","required":false,"type":"boolean","default":false},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of corporation industry jobs","examples":{"application/json":[{"job_id":229136101,"installer_id":498338451,"facility_id":60006382,"location_id":60006382,"activity_id":1,"blueprint_id":1015116533326,"blueprint_type_id":2047,"blueprint_location_id":60006382,"output_location_id":60006382,"runs":1,"cost":118.01,"licensed_runs":200,"status":"active","duration":548,"start_date":"2014-07-19T15:47:06Z","end_date":"2014-07-19T15:56:14Z"}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["job_id","installer_id","facility_id","location_id","activity_id","blueprint_id","blueprint_type_id","blueprint_location_id","output_location_id","runs","status","duration","start_date","end_date"],"properties":{"job_id":{"type":"integer","format":"int32","description":"Unique job ID","title":"get_corporations_corporation_id_industry_jobs_job_id"},"installer_id":{"type":"integer","format":"int32","description":"ID of the character which installed this job","title":"get_corporations_corporation_id_industry_jobs_installer_id"},"facility_id":{"type":"integer","format":"int64","description":"ID of the facility where this job is running","title":"get_corporations_corporation_id_industry_jobs_facility_id"},"location_id":{"type":"integer","format":"int64","description":"ID of the location for the industry facility","title":"get_corporations_corporation_id_industry_jobs_location_id"},"activity_id":{"type":"integer","format":"int32","description":"Job activity ID","title":"get_corporations_corporation_id_industry_jobs_activity_id"},"blueprint_id":{"type":"integer","format":"int64","title":"get_corporations_corporation_id_industry_jobs_blueprint_id","description":"blueprint_id integer"},"blueprint_type_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_industry_jobs_blueprint_type_id","description":"blueprint_type_id integer"},"blueprint_location_id":{"type":"integer","format":"int64","description":"Location ID of the location from which the blueprint was installed. Normally a station ID, but can also be an asset (e.g. container) or corporation facility","title":"get_corporations_corporation_id_industry_jobs_blueprint_location_id"},"output_location_id":{"type":"integer","format":"int64","description":"Location ID of the location to which the output of the job will be delivered. Normally a station ID, but can also be a corporation facility","title":"get_corporations_corporation_id_industry_jobs_output_location_id"},"runs":{"type":"integer","format":"int32","description":"Number of runs for a manufacturing job, or number of copies to make for a blueprint copy","title":"get_corporations_corporation_id_industry_jobs_runs"},"cost":{"type":"number","format":"double","description":"The sume of job installation fee and industry facility tax","title":"get_corporations_corporation_id_industry_jobs_cost"},"licensed_runs":{"type":"integer","format":"int32","description":"Number of runs blueprint is licensed for","title":"get_corporations_corporation_id_industry_jobs_licensed_runs"},"probability":{"type":"number","format":"float","description":"Chance of success for invention","title":"get_corporations_corporation_id_industry_jobs_probability"},"product_type_id":{"type":"integer","format":"int32","description":"Type ID of product (manufactured, copied or invented)","title":"get_corporations_corporation_id_industry_jobs_product_type_id"},"status":{"type":"string","enum":["active","cancelled","delivered","paused","ready","reverted"],"title":"get_corporations_corporation_id_industry_jobs_status","description":"status string"},"duration":{"type":"integer","format":"int32","description":"Job duration in seconds","title":"get_corporations_corporation_id_industry_jobs_duration"},"start_date":{"type":"string","format":"date-time","description":"Date and time when this job started","title":"get_corporations_corporation_id_industry_jobs_start_date"},"end_date":{"type":"string","format":"date-time","description":"Date and time when this job finished","title":"get_corporations_corporation_id_industry_jobs_end_date"},"pause_date":{"type":"string","format":"date-time","description":"Date and time when this job was paused (i.e. time when the facility where this job was installed went offline)","title":"get_corporations_corporation_id_industry_jobs_pause_date"},"completed_date":{"type":"string","format":"date-time","description":"Date and time when this job was completed","title":"get_corporations_corporation_id_industry_jobs_completed_date"},"completed_character_id":{"type":"integer","format":"int32","description":"ID of the character which completed this job","title":"get_corporations_corporation_id_industry_jobs_completed_character_id"},"successful_runs":{"type":"integer","format":"int32","description":"Number of successful runs for this job. Equal to runs unless this is an invention job","title":"get_corporations_corporation_id_industry_jobs_successful_runs"}},"title":"get_corporations_corporation_id_industry_jobs_200_ok","description":"200 ok object"},"title":"get_corporations_corporation_id_industry_jobs_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":["FactoryManager"],"security":[{"evesso":["esi-industry.read_corporation_jobs.v1"]}],"operationId":"get_corporations_corporation_id_industry_jobs","x-cached-seconds":300,"x-alternate-versions":["dev","legacy","v1"]}},"/corporation/{corporation_id}/mining/extractions/":{"get":{"summary":"Moon extraction timers","description":"Extraction timers for all moon chunks being extracted by refineries belonging to a corporation.\n\n\n---\nAlternate route: `/dev/corporation/{corporation_id}/mining/extractions/`\n\nAlternate route: `/legacy/corporation/{corporation_id}/mining/extractions/`\n\nAlternate route: `/v1/corporation/{corporation_id}/mining/extractions/`\n\n---\nThis route is cached for up to 1800 seconds\n\n---\nRequires one of the following EVE corporation role(s): Structure_manager\n","tags":["Industry"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of chunk timers","examples":{"application/json":[{"structure_id":1000000010579,"moon_id":40307229,"extraction_start_time":"2017-10-11T10:37:04Z","chunk_arrival_time":"2017-10-17T11:00:59Z","natural_decay_time":"2017-10-17T14:00:59Z"}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["structure_id","moon_id","extraction_start_time","chunk_arrival_time","natural_decay_time"],"properties":{"structure_id":{"type":"integer","format":"int64","title":"get_corporation_corporation_id_mining_extractions_structure_id","description":"structure_id integer"},"moon_id":{"type":"integer","format":"int32","title":"get_corporation_corporation_id_mining_extractions_moon_id","description":"moon_id integer"},"extraction_start_time":{"description":"The time at which the current extraction was initiated.\n","type":"string","format":"date-time","title":"get_corporation_corporation_id_mining_extractions_extraction_start_time"},"chunk_arrival_time":{"description":"The time at which the chunk being extracted will arrive and can be fractured by the moon mining drill.\n","type":"string","format":"date-time","title":"get_corporation_corporation_id_mining_extractions_chunk_arrival_time"},"natural_decay_time":{"description":"The time at which the chunk being extracted will naturally fracture if it is not first fractured by the moon mining drill.\n","type":"string","format":"date-time","title":"get_corporation_corporation_id_mining_extractions_natural_decay_time"}},"title":"get_corporation_corporation_id_mining_extractions_200_ok","description":"200 ok object"},"title":"get_corporation_corporation_id_mining_extractions_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":["Structure_manager"],"security":[{"evesso":["esi-industry.read_corporation_mining.v1"]}],"operationId":"get_corporation_corporation_id_mining_extractions","x-cached-seconds":1800,"x-alternate-versions":["dev","legacy","v1"]}},"/insurance/prices/":{"get":{"description":"Return available insurance levels for all ship types\n\n---\nAlternate route: `/dev/insurance/prices/`\n\nAlternate route: `/legacy/insurance/prices/`\n\nAlternate route: `/v1/insurance/prices/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"List insurance levels","tags":["Insurance"],"responses":{"200":{"description":"A list of insurance levels for all ship types","examples":{"application/json":[{"type_id":1,"levels":[{"cost":10.01,"payout":20.01,"name":"Basic"}]}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["type_id","levels"],"properties":{"type_id":{"type":"integer","format":"int32","title":"get_insurance_prices_type_id","description":"type_id integer"},"levels":{"type":"array","description":"A list of a available insurance levels for this ship type","maxItems":6,"items":{"type":"object","required":["cost","payout","name"],"properties":{"cost":{"type":"number","format":"float","title":"get_insurance_prices_cost","description":"cost number"},"payout":{"type":"number","format":"float","title":"get_insurance_prices_payout","description":"payout number"},"name":{"type":"string","description":"Localized insurance level","title":"get_insurance_prices_name"}},"title":"get_insurance_prices_level","description":"level object"},"title":"get_insurance_prices_levels"}},"title":"get_insurance_prices_200_ok","description":"200 ok object"},"title":"get_insurance_prices_ok","description":"200 ok array"},"headers":{"Content-Language":{"description":"The language used in the response","type":"string","enum":["de","en-us","fr","ja","ru","zh"]},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/language"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_insurance_prices","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/killmails/{killmail_id}/{killmail_hash}/":{"get":{"description":"Return a single killmail from its ID and hash\n\n---\nAlternate route: `/dev/killmails/{killmail_id}/{killmail_hash}/`\n\nAlternate route: `/legacy/killmails/{killmail_id}/{killmail_hash}/`\n\nAlternate route: `/v1/killmails/{killmail_id}/{killmail_hash}/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get a single killmail","tags":["Killmails"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"killmail_hash","in":"path","description":"The killmail hash for verification","required":true,"type":"string"},{"name":"killmail_id","in":"path","description":"The killmail ID to be queried","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A killmail","examples":{"application/json":{"solar_system_id":30002976,"killmail_id":56733821,"killmail_time":"2016-10-22T17:13:36Z","attackers":[{"ship_type_id":17841,"faction_id":500003,"corporation_id":1000179,"character_id":95810944,"weapon_type_id":3074,"final_blow":true,"security_status":-0.3,"damage_done":5745}],"victim":{"alliance_id":621338554,"damage_taken":5745,"items":[{"singleton":0,"item_type_id":5973,"flag":20,"quantity_dropped":1}],"character_id":92796241,"ship_type_id":17812,"corporation_id":841363671,"position":{"y":146704961490.90222,"x":452186600569.4748,"z":109514596532.54477}}}},"schema":{"type":"object","required":["killmail_id","killmail_time","victim","attackers","solar_system_id"],"properties":{"killmail_id":{"type":"integer","format":"int32","description":"ID of the killmail","title":"get_killmails_killmail_id_killmail_hash_killmail_id"},"killmail_time":{"type":"string","format":"date-time","description":"Time that the victim was killed and the killmail generated\n","title":"get_killmails_killmail_id_killmail_hash_killmail_time"},"victim":{"type":"object","required":["damage_taken","ship_type_id"],"properties":{"character_id":{"type":"integer","format":"int32","title":"get_killmails_killmail_id_killmail_hash_character_id","description":"character_id integer"},"corporation_id":{"type":"integer","format":"int32","title":"get_killmails_killmail_id_killmail_hash_corporation_id","description":"corporation_id integer"},"alliance_id":{"type":"integer","format":"int32","title":"get_killmails_killmail_id_killmail_hash_alliance_id","description":"alliance_id integer"},"faction_id":{"type":"integer","format":"int32","title":"get_killmails_killmail_id_killmail_hash_faction_id","description":"faction_id integer"},"damage_taken":{"type":"integer","description":"How much total damage was taken by the victim\n","format":"int32","title":"get_killmails_killmail_id_killmail_hash_damage_taken"},"ship_type_id":{"type":"integer","format":"int32","description":"The ship that the victim was piloting and was destroyed\n","title":"get_killmails_killmail_id_killmail_hash_ship_type_id"},"items":{"type":"array","maxItems":10000,"items":{"type":"object","required":["item_type_id","singleton","flag"],"properties":{"item_type_id":{"type":"integer","format":"int32","title":"get_killmails_killmail_id_killmail_hash_item_type_id","description":"item_type_id integer"},"quantity_destroyed":{"type":"integer","description":"How many of the item were destroyed if any\n","format":"int64","title":"get_killmails_killmail_id_killmail_hash_quantity_destroyed"},"quantity_dropped":{"type":"integer","description":"How many of the item were dropped if any\n","format":"int64","title":"get_killmails_killmail_id_killmail_hash_quantity_dropped"},"singleton":{"type":"integer","format":"int32","title":"get_killmails_killmail_id_killmail_hash_singleton","description":"singleton integer"},"flag":{"type":"integer","description":"Flag for the location of the item\n","format":"int32","title":"get_killmails_killmail_id_killmail_hash_flag"},"items":{"type":"array","maxItems":10000,"items":{"type":"object","required":["item_type_id","singleton","flag"],"properties":{"item_type_id":{"type":"integer","format":"int32","title":"get_killmails_killmail_id_killmail_hash_item_type_id","description":"item_type_id integer"},"quantity_destroyed":{"type":"integer","format":"int64","title":"get_killmails_killmail_id_killmail_hash_quantity_destroyed","description":"quantity_destroyed integer"},"quantity_dropped":{"type":"integer","format":"int64","title":"get_killmails_killmail_id_killmail_hash_quantity_dropped","description":"quantity_dropped integer"},"singleton":{"type":"integer","format":"int32","title":"get_killmails_killmail_id_killmail_hash_singleton","description":"singleton integer"},"flag":{"type":"integer","format":"int32","title":"get_killmails_killmail_id_killmail_hash_flag","description":"flag integer"}},"title":"get_killmails_killmail_id_killmail_hash_item","description":"item object"},"title":"get_killmails_killmail_id_killmail_hash_items","description":"items array"}},"title":"get_killmails_killmail_id_killmail_hash_item","description":"item object"},"title":"get_killmails_killmail_id_killmail_hash_items","description":"items array"},"position":{"type":"object","description":"Coordinates of the victim in Cartesian space relative to the Sun\n","required":["x","y","z"],"properties":{"x":{"type":"number","format":"double","title":"get_killmails_killmail_id_killmail_hash_x","description":"x number"},"y":{"type":"number","format":"double","title":"get_killmails_killmail_id_killmail_hash_y","description":"y number"},"z":{"type":"number","format":"double","title":"get_killmails_killmail_id_killmail_hash_z","description":"z number"}},"title":"get_killmails_killmail_id_killmail_hash_position"}},"title":"get_killmails_killmail_id_killmail_hash_victim","description":"victim object"},"attackers":{"type":"array","maxItems":10000,"items":{"type":"object","required":["security_status","final_blow","damage_done"],"properties":{"character_id":{"type":"integer","format":"int32","title":"get_killmails_killmail_id_killmail_hash_character_id","description":"character_id integer"},"corporation_id":{"type":"integer","format":"int32","title":"get_killmails_killmail_id_killmail_hash_corporation_id","description":"corporation_id integer"},"alliance_id":{"type":"integer","format":"int32","title":"get_killmails_killmail_id_killmail_hash_alliance_id","description":"alliance_id integer"},"faction_id":{"type":"integer","format":"int32","title":"get_killmails_killmail_id_killmail_hash_faction_id","description":"faction_id integer"},"security_status":{"type":"number","format":"float","description":"Security status for the attacker\n","title":"get_killmails_killmail_id_killmail_hash_security_status"},"final_blow":{"type":"boolean","description":"Was the attacker the one to achieve the final blow\n","title":"get_killmails_killmail_id_killmail_hash_final_blow"},"damage_done":{"type":"integer","format":"int32","title":"get_killmails_killmail_id_killmail_hash_damage_done","description":"damage_done integer"},"ship_type_id":{"type":"integer","format":"int32","description":"What ship was the attacker flying\n","title":"get_killmails_killmail_id_killmail_hash_ship_type_id"},"weapon_type_id":{"type":"integer","format":"int32","description":"What weapon was used by the attacker for the kill\n","title":"get_killmails_killmail_id_killmail_hash_weapon_type_id"}},"title":"get_killmails_killmail_id_killmail_hash_attacker","description":"attacker object"},"title":"get_killmails_killmail_id_killmail_hash_attackers","description":"attackers array"},"solar_system_id":{"type":"integer","format":"int32","description":"Solar system that the kill took place in\n","title":"get_killmails_killmail_id_killmail_hash_solar_system_id"},"moon_id":{"type":"integer","format":"int32","description":"Moon if the kill took place at one","title":"get_killmails_killmail_id_killmail_hash_moon_id"},"war_id":{"type":"integer","format":"int32","description":"War if the killmail is generated in relation to an official war\n","title":"get_killmails_killmail_id_killmail_hash_war_id"}},"title":"get_killmails_killmail_id_killmail_hash_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"422":{"description":"Invalid killmail_id and/or killmail_hash","schema":{"type":"object","title":"get_killmails_killmail_id_killmail_hash_unprocessable_entity","description":"Unprocessable entity","properties":{"error":{"type":"string","description":"Unprocessable entity message","title":"get_killmails_killmail_id_killmail_hash_422_unprocessable_entity"}}},"examples":{"application/json":{"error":"Unprocessable entity message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_killmails_killmail_id_killmail_hash","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/killmails/recent/":{"get":{"description":"Return a list of character's recent kills and losses\n\n---\nAlternate route: `/dev/characters/{character_id}/killmails/recent/`\n\nAlternate route: `/legacy/characters/{character_id}/killmails/recent/`\n\nAlternate route: `/v1/characters/{character_id}/killmails/recent/`\n\n---\nThis route is cached for up to 120 seconds","summary":"Get character kills and losses","tags":["Killmails"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"name":"max_count","in":"query","description":"How many killmails to return at maximum","type":"integer","format":"int32","required":false,"default":50,"maximum":5000},{"name":"max_kill_id","in":"query","description":"Only return killmails with ID smaller than this.\n","required":false,"type":"integer","format":"int32"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of killmail IDs and hashes","examples":{"application/json":[{"killmail_id":2,"killmail_hash":"8eef5e8fb6b88fe3407c489df33822b2e3b57a5e"},{"killmail_id":1,"killmail_hash":"b41ccb498ece33d64019f64c0db392aa3aa701fb"}]},"schema":{"type":"array","maxItems":5000,"items":{"type":"object","required":["killmail_id","killmail_hash"],"properties":{"killmail_id":{"type":"integer","format":"int32","description":"ID of this killmail","title":"get_characters_character_id_killmails_recent_killmail_id"},"killmail_hash":{"type":"string","description":"A hash of this killmail","title":"get_characters_character_id_killmails_recent_killmail_hash"}},"title":"get_characters_character_id_killmails_recent_200_ok","description":"200 ok object"},"title":"get_characters_character_id_killmails_recent_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-killmails.read_killmails.v1"]}],"operationId":"get_characters_character_id_killmails_recent","x-cached-seconds":120,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/killmails/recent/":{"get":{"description":"Get a list of corporation's recent kills and losses\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/killmails/recent/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/killmails/recent/`\n\nAlternate route: `/v1/corporations/{corporation_id}/killmails/recent/`\n\n---\nThis route is cached for up to 300 seconds\n\n---\nRequires one of the following EVE corporation role(s): Director\n","summary":"Get corporation kills and losses","tags":["Killmails"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"name":"max_kill_id","in":"query","description":"Only return killmails with ID smaller than this","required":false,"type":"integer","format":"int32"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of killmail IDs and hashes","examples":{"application/json":[{"killmail_id":2,"killmail_hash":"8eef5e8fb6b88fe3407c489df33822b2e3b57a5e"},{"killmail_id":1,"killmail_hash":"b41ccb498ece33d64019f64c0db392aa3aa701fb"}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["killmail_id","killmail_hash"],"properties":{"killmail_id":{"type":"integer","format":"int32","description":"ID of this killmail","title":"get_corporations_corporation_id_killmails_recent_killmail_id"},"killmail_hash":{"type":"string","description":"A hash of this killmail","title":"get_corporations_corporation_id_killmails_recent_killmail_hash"}},"title":"get_corporations_corporation_id_killmails_recent_200_ok","description":"200 ok object"},"title":"get_corporations_corporation_id_killmails_recent_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":["Director"],"security":[{"evesso":["esi-killmails.read_corporation_killmails.v1"]}],"operationId":"get_corporations_corporation_id_killmails_recent","x-cached-seconds":300,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/location/":{"get":{"description":"Information about the characters current location. Returns the current solar system id, and also the current station or structure ID if applicable.\n\n---\nAlternate route: `/dev/characters/{character_id}/location/`\n\nAlternate route: `/legacy/characters/{character_id}/location/`\n\nAlternate route: `/v1/characters/{character_id}/location/`\n\n---\nThis route is cached for up to 5 seconds","summary":"Get character location","tags":["Location"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Information about the characters current location. Returns the current solar system id, and also the current station or structure ID if applicable.","examples":{"application/json":{"solar_system_id":30002505,"structure_id":1000000016989}},"schema":{"type":"object","required":["solar_system_id"],"properties":{"solar_system_id":{"type":"integer","format":"int32","title":"get_characters_character_id_location_solar_system_id","description":"solar_system_id integer"},"station_id":{"type":"integer","format":"int32","title":"get_characters_character_id_location_station_id","description":"station_id integer"},"structure_id":{"type":"integer","format":"int64","title":"get_characters_character_id_location_structure_id","description":"structure_id integer"}},"title":"get_characters_character_id_location_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-location.read_location.v1"]}],"operationId":"get_characters_character_id_location","x-cached-seconds":5,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/ship/":{"get":{"description":"Get the current ship type, name and id\n\n---\nAlternate route: `/dev/characters/{character_id}/ship/`\n\nAlternate route: `/legacy/characters/{character_id}/ship/`\n\nAlternate route: `/v1/characters/{character_id}/ship/`\n\n---\nThis route is cached for up to 5 seconds","summary":"Get current ship","tags":["Location"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Get the current ship type, name and id","examples":{"application/json":{"ship_type_id":1233,"ship_name":"SPACESHIPS!!!","ship_item_id":1000000016991}},"schema":{"type":"object","required":["ship_type_id","ship_item_id","ship_name"],"properties":{"ship_type_id":{"type":"integer","format":"int32","title":"get_characters_character_id_ship_ship_type_id","description":"ship_type_id integer"},"ship_item_id":{"type":"integer","format":"int64","description":"Item id's are unique to a ship and persist until it is repackaged. This value can be used to track repeated uses of a ship, or detect when a pilot changes into a different instance of the same ship type.","title":"get_characters_character_id_ship_ship_item_id"},"ship_name":{"type":"string","title":"get_characters_character_id_ship_ship_name","description":"ship_name string"}},"title":"get_characters_character_id_ship_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-location.read_ship_type.v1"]}],"operationId":"get_characters_character_id_ship","x-cached-seconds":5,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/online/":{"get":{"description":"Checks if the character is currently online\n\n---\nAlternate route: `/legacy/characters/{character_id}/online/`\n\nAlternate route: `/v1/characters/{character_id}/online/`\n\n---\nThis route is cached for up to 60 seconds\n\n---\n[This route has an available update](https://esi.tech.ccp.is/diff/latest/dev/#GET-/characters/{character_id}/online/)","summary":"Get character online","tags":["Location"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Boolean of if the character is currently online","examples":{"application/json":true},"schema":{"type":"boolean","title":"get_characters_character_id_online_ok","description":"200 ok boolean"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-location.read_online.v1"]}],"operationId":"get_characters_character_id_online","x-cached-seconds":60,"x-alternate-versions":["legacy","v1"]}},"/loyalty/stores/{corporation_id}/offers/":{"get":{"description":"Return a list of offers from a specific corporation's loyalty store\n\n---\nAlternate route: `/dev/loyalty/stores/{corporation_id}/offers/`\n\nAlternate route: `/legacy/loyalty/stores/{corporation_id}/offers/`\n\nAlternate route: `/v1/loyalty/stores/{corporation_id}/offers/`\n\n---\nThis route expires daily at 11:05","summary":"List loyalty store offers","tags":["Loyalty"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of offers","examples":{"application/json":[{"offer_id":1,"type_id":123,"quantity":1,"lp_cost":100,"isk_cost":0,"required_items":[]},{"offer_id":2,"type_id":1235,"quantity":10,"lp_cost":100,"isk_cost":1000,"required_items":[{"type_id":1234,"quantity":10}]}]},"schema":{"type":"array","maxItems":10000,"items":{"type":"object","required":["offer_id","type_id","quantity","lp_cost","isk_cost","required_items"],"properties":{"offer_id":{"type":"integer","format":"int32","title":"get_loyalty_stores_corporation_id_offers_offer_id","description":"offer_id integer"},"type_id":{"type":"integer","format":"int32","title":"get_loyalty_stores_corporation_id_offers_type_id","description":"type_id integer"},"quantity":{"type":"integer","format":"int32","title":"get_loyalty_stores_corporation_id_offers_quantity","description":"quantity integer"},"lp_cost":{"type":"integer","format":"int32","title":"get_loyalty_stores_corporation_id_offers_lp_cost","description":"lp_cost integer"},"isk_cost":{"type":"number","format":"int32","title":"get_loyalty_stores_corporation_id_offers_isk_cost","description":"isk_cost number"},"required_items":{"type":"array","maxItems":100,"items":{"type":"object","required":["type_id","quantity"],"properties":{"type_id":{"type":"integer","format":"int32","title":"get_loyalty_stores_corporation_id_offers_type_id","description":"type_id integer"},"quantity":{"type":"integer","format":"int32","title":"get_loyalty_stores_corporation_id_offers_quantity","description":"quantity integer"}},"title":"get_loyalty_stores_corporation_id_offers_required_item","description":"required_item object"},"title":"get_loyalty_stores_corporation_id_offers_required_items","description":"required_items array"}},"title":"get_loyalty_stores_corporation_id_offers_200_ok","description":"200 ok object"},"title":"get_loyalty_stores_corporation_id_offers_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_loyalty_stores_corporation_id_offers","x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/loyalty/points/":{"get":{"description":"Return a list of loyalty points for all corporations the character has worked for\n\n---\nAlternate route: `/dev/characters/{character_id}/loyalty/points/`\n\nAlternate route: `/legacy/characters/{character_id}/loyalty/points/`\n\nAlternate route: `/v1/characters/{character_id}/loyalty/points/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get loyalty points","tags":["Loyalty"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of loyalty points","examples":{"application/json":[{"corporation_id":123,"loyalty_points":100}]},"schema":{"type":"array","maxItems":500,"items":{"type":"object","required":["corporation_id","loyalty_points"],"properties":{"corporation_id":{"type":"integer","format":"int32","title":"get_characters_character_id_loyalty_points_corporation_id","description":"corporation_id integer"},"loyalty_points":{"type":"integer","format":"int32","title":"get_characters_character_id_loyalty_points_loyalty_points","description":"loyalty_points integer"}},"title":"get_characters_character_id_loyalty_points_200_ok","description":"200 ok object"},"title":"get_characters_character_id_loyalty_points_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-characters.read_loyalty.v1"]}],"operationId":"get_characters_character_id_loyalty_points","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/mail/":{"get":{"description":"Return the 50 most recent mail headers belonging to the character that match the query criteria. Queries can be filtered by label, and last_mail_id can be used to paginate backwards.\n\n---\nAlternate route: `/dev/characters/{character_id}/mail/`\n\nAlternate route: `/legacy/characters/{character_id}/mail/`\n\nAlternate route: `/v1/characters/{character_id}/mail/`\n\n---\nThis route is cached for up to 30 seconds","summary":"Return mail headers","tags":["Mail"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"name":"labels","in":"query","required":false,"description":"Fetch only mails that match one or more of the given labels","type":"array","uniqueItems":true,"minItems":1,"maxItems":25,"items":{"type":"integer","format":"int64","minimum":0}},{"name":"last_mail_id","in":"query","description":"List only mail with an ID lower than the given ID, if present","type":"integer","format":"int32"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"The requested mail","examples":{"application/json":[{"mail_id":7,"subject":"Title for EVE Mail","from":90000001,"timestamp":"2015-09-30T16:07:00Z","labels":[3],"recipients":[{"recipient_type":"character","recipient_id":90000002}],"is_read":true}]},"schema":{"type":"array","maxItems":50,"items":{"type":"object","properties":{"mail_id":{"type":"integer","format":"int64","title":"get_characters_character_id_mail_mail_id","description":"mail_id integer"},"subject":{"type":"string","description":"Mail subject","title":"get_characters_character_id_mail_subject"},"from":{"type":"integer","format":"int32","description":"From whom the mail was sent","title":"get_characters_character_id_mail_from"},"timestamp":{"type":"string","format":"date-time","description":"When the mail was sent","title":"get_characters_character_id_mail_timestamp"},"labels":{"type":"array","uniqueItems":true,"maxItems":25,"minimum":0,"items":{"type":"integer","format":"int64","title":"get_characters_character_id_mail_label","description":"label integer"},"title":"get_characters_character_id_mail_labels","description":"labels array"},"recipients":{"type":"array","description":"Recipients of the mail","uniqueItems":true,"minItems":1,"maxItems":52,"items":{"type":"object","required":["recipient_type","recipient_id"],"properties":{"recipient_type":{"type":"string","enum":["alliance","character","corporation","mailing_list"],"title":"get_characters_character_id_mail_recipient_type","description":"recipient_type string"},"recipient_id":{"type":"integer","format":"int32","title":"get_characters_character_id_mail_recipient_id","description":"recipient_id integer"}},"title":"get_characters_character_id_mail_recipient","description":"recipient object"},"title":"get_characters_character_id_mail_recipients"},"is_read":{"type":"boolean","title":"get_characters_character_id_mail_is_read","description":"is_read boolean"}},"title":"get_characters_character_id_mail_200_ok","description":"200 ok object"},"title":"get_characters_character_id_mail_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-mail.read_mail.v1"]}],"operationId":"get_characters_character_id_mail","x-cached-seconds":30,"x-alternate-versions":["dev","legacy","v1"]},"post":{"description":"Create and send a new mail\n\n---\nAlternate route: `/dev/characters/{character_id}/mail/`\n\nAlternate route: `/legacy/characters/{character_id}/mail/`\n\nAlternate route: `/v1/characters/{character_id}/mail/`\n","summary":"Send a new mail","tags":["Mail"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"name":"mail","in":"body","required":true,"description":"The mail to send","schema":{"required":["recipients","subject","body"],"properties":{"recipients":{"type":"array","minItems":1,"maxItems":50,"items":{"type":"object","required":["recipient_type","recipient_id"],"properties":{"recipient_type":{"type":"string","enum":["alliance","character","corporation","mailing_list"],"title":"post_characters_character_id_mail_recipient_type","description":"recipient_type string"},"recipient_id":{"type":"integer","format":"int32","title":"post_characters_character_id_mail_recipient_id","description":"recipient_id integer"}},"title":"post_characters_character_id_mail_recipient","description":"recipient object"},"title":"post_characters_character_id_mail_recipients","description":"recipients array"},"subject":{"type":"string","maxLength":1000,"title":"post_characters_character_id_mail_subject","description":"subject string"},"body":{"type":"string","maxLength":10000,"title":"post_characters_character_id_mail_body","description":"body string"},"approved_cost":{"type":"integer","format":"int64","default":0,"title":"post_characters_character_id_mail_approved_cost","description":"approved_cost integer"}},"title":"post_characters_character_id_mail_mail","description":"mail schema"}},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"201":{"description":"Mail created","examples":{"application/json":13},"schema":{"type":"integer","format":"int32","description":"Mail ID","title":"post_characters_character_id_mail_created"}},"400":{"description":"Only one corporation, alliance, or mailing list can be the recipient of a mail","schema":{"type":"object","title":"post_characters_character_id_mail_bad_request","description":"Bad request","properties":{"error":{"type":"string","description":"Bad request message","title":"post_characters_character_id_mail_400_bad_request"}}},"examples":{"application/json":{"error":"Bad request message"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-mail.send_mail.v1"]}],"operationId":"post_characters_character_id_mail","x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/mail/labels/":{"get":{"description":"Return a list of the users mail labels, unread counts for each label and a total unread count.\n\n---\nAlternate route: `/dev/characters/{character_id}/mail/labels/`\n\nAlternate route: `/v3/characters/{character_id}/mail/labels/`\n\n---\nThis route is cached for up to 30 seconds","summary":"Get mail labels and unread counts","tags":["Mail"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of mail labels and unread counts","examples":{"application/json":{"total_unread_count":5,"labels":[{"label_id":16,"name":"PINK","color_hex":"#660066","unread_count":4},{"label_id":17,"name":"WHITE","color_hex":"#ffffff","unread_count":1}]}},"schema":{"type":"object","properties":{"total_unread_count":{"type":"integer","format":"int32","minimum":0,"title":"get_characters_character_id_mail_labels_total_unread_count","description":"total_unread_count integer"},"labels":{"type":"array","maxItems":30,"items":{"type":"object","properties":{"unread_count":{"type":"integer","format":"int32","minimum":0,"title":"get_characters_character_id_mail_labels_unread_count","description":"unread_count integer"},"label_id":{"type":"integer","format":"int32","minimum":0,"title":"get_characters_character_id_mail_labels_label_id","description":"label_id integer"},"name":{"type":"string","maxLength":40,"title":"get_characters_character_id_mail_labels_name","description":"name string"},"color":{"type":"string","default":"#ffffff","enum":["#0000fe","#006634","#0099ff","#00ff33","#01ffff","#349800","#660066","#666666","#999999","#99ffff","#9a0000","#ccff9a","#e6e6e6","#fe0000","#ff6600","#ffff01","#ffffcd","#ffffff"],"title":"get_characters_character_id_mail_labels_color","description":"color string"}},"title":"get_characters_character_id_mail_labels_label","description":"label object"},"title":"get_characters_character_id_mail_labels_labels","description":"labels array"}},"title":"get_characters_character_id_mail_labels_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-mail.read_mail.v1"]}],"operationId":"get_characters_character_id_mail_labels","x-cached-seconds":30,"x-alternate-versions":["dev","v3"]},"post":{"description":"Create a mail label\n\n---\nAlternate route: `/dev/characters/{character_id}/mail/labels/`\n\nAlternate route: `/legacy/characters/{character_id}/mail/labels/`\n\nAlternate route: `/v2/characters/{character_id}/mail/labels/`\n","summary":"Create a mail label","tags":["Mail"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"name":"label","description":"Label to create","in":"body","schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":40,"title":"post_characters_character_id_mail_labels_name","description":"name string"},"color":{"type":"string","description":"Hexadecimal string representing label color, in RGB format","default":"#ffffff","enum":["#0000fe","#006634","#0099ff","#00ff33","#01ffff","#349800","#660066","#666666","#999999","#99ffff","#9a0000","#ccff9a","#e6e6e6","#fe0000","#ff6600","#ffff01","#ffffcd","#ffffff"],"title":"post_characters_character_id_mail_labels_color"}},"title":"post_characters_character_id_mail_labels_label","description":"label object"}},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"201":{"description":"Label created","examples":{"application/json":128},"schema":{"type":"integer","format":"int64","description":"Label ID","title":"post_characters_character_id_mail_labels_created"}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-mail.organize_mail.v1"]}],"operationId":"post_characters_character_id_mail_labels","x-alternate-versions":["dev","legacy","v2"]}},"/characters/{character_id}/mail/labels/{label_id}/":{"delete":{"description":"Delete a mail label\n\n---\nAlternate route: `/dev/characters/{character_id}/mail/labels/{label_id}/`\n\nAlternate route: `/legacy/characters/{character_id}/mail/labels/{label_id}/`\n\nAlternate route: `/v1/characters/{character_id}/mail/labels/{label_id}/`\n","summary":"Delete a mail label","tags":["Mail"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"name":"label_id","in":"path","description":"An EVE label id","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"204":{"description":"Label deleted"},"422":{"description":"Default labels cannot be deleted","schema":{"type":"object","title":"delete_characters_character_id_mail_labels_label_id_unprocessable_entity","description":"Unprocessable entity","properties":{"error":{"type":"string","description":"Unprocessable entity message","title":"delete_characters_character_id_mail_labels_label_id_422_unprocessable_entity"}}},"examples":{"application/json":{"error":"Unprocessable entity message"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-mail.organize_mail.v1"]}],"operationId":"delete_characters_character_id_mail_labels_label_id","x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/mail/lists/":{"get":{"description":"Return all mailing lists that the character is subscribed to\n\n---\nAlternate route: `/dev/characters/{character_id}/mail/lists/`\n\nAlternate route: `/legacy/characters/{character_id}/mail/lists/`\n\nAlternate route: `/v1/characters/{character_id}/mail/lists/`\n\n---\nThis route is cached for up to 120 seconds","summary":"Return mailing list subscriptions","tags":["Mail"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Mailing lists","examples":{"application/json":[{"mailing_list_id":1,"name":"test_mailing_list"}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["mailing_list_id","name"],"properties":{"mailing_list_id":{"description":"Mailing list ID","format":"int32","type":"integer","title":"get_characters_character_id_mail_lists_mailing_list_id"},"name":{"type":"string","title":"get_characters_character_id_mail_lists_name","description":"name string"}},"title":"get_characters_character_id_mail_lists_200_ok","description":"200 ok object"},"title":"get_characters_character_id_mail_lists_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-mail.read_mail.v1"]}],"operationId":"get_characters_character_id_mail_lists","x-cached-seconds":120,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/mail/{mail_id}/":{"delete":{"description":"Delete a mail\n\n---\nAlternate route: `/dev/characters/{character_id}/mail/{mail_id}/`\n\nAlternate route: `/legacy/characters/{character_id}/mail/{mail_id}/`\n\nAlternate route: `/v1/characters/{character_id}/mail/{mail_id}/`\n","summary":"Delete a mail","tags":["Mail"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"name":"mail_id","in":"path","description":"An EVE mail ID","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"204":{"description":"Mail deleted"},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-mail.organize_mail.v1"]}],"operationId":"delete_characters_character_id_mail_mail_id","x-alternate-versions":["dev","legacy","v1"]},"get":{"description":"Return the contents of an EVE mail\n\n---\nAlternate route: `/dev/characters/{character_id}/mail/{mail_id}/`\n\nAlternate route: `/legacy/characters/{character_id}/mail/{mail_id}/`\n\nAlternate route: `/v1/characters/{character_id}/mail/{mail_id}/`\n\n---\nThis route is cached for up to 30 seconds","summary":"Return a mail","tags":["Mail"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"name":"mail_id","in":"path","description":"An EVE mail ID","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Contents of a mail","examples":{"application/json":{"subject":"test","from":90000001,"timestamp":"2015-09-30T16:07:00Z","body":"blah blah blah","labels":[2,32],"read":true}},"schema":{"type":"object","properties":{"subject":{"type":"string","description":"Mail subject","title":"get_characters_character_id_mail_mail_id_subject"},"from":{"type":"integer","format":"int32","description":"From whom the mail was sent","title":"get_characters_character_id_mail_mail_id_from"},"timestamp":{"type":"string","format":"date-time","description":"When the mail was sent","title":"get_characters_character_id_mail_mail_id_timestamp"},"recipients":{"type":"array","description":"Recipients of the mail","uniqueItems":true,"minItems":1,"maxItems":52,"items":{"type":"object","required":["recipient_type","recipient_id"],"properties":{"recipient_type":{"type":"string","enum":["alliance","character","corporation","mailing_list"],"title":"get_characters_character_id_mail_mail_id_recipient_type","description":"recipient_type string"},"recipient_id":{"type":"integer","format":"int32","title":"get_characters_character_id_mail_mail_id_recipient_id","description":"recipient_id integer"}},"title":"get_characters_character_id_mail_mail_id_recipient","description":"recipient object"},"title":"get_characters_character_id_mail_mail_id_recipients"},"body":{"type":"string","description":"Mail's body","title":"get_characters_character_id_mail_mail_id_body"},"labels":{"type":"array","maxItems":25,"description":"Labels attached to the mail","items":{"type":"integer","format":"int64","uniqueItems":true,"minimum":0,"title":"get_characters_character_id_mail_mail_id_label","description":"label integer"},"title":"get_characters_character_id_mail_mail_id_labels"},"read":{"type":"boolean","description":"Whether the mail is flagged as read","title":"get_characters_character_id_mail_mail_id_read"}},"title":"get_characters_character_id_mail_mail_id_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"Mail not found","schema":{"type":"object","title":"get_characters_character_id_mail_mail_id_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"get_characters_character_id_mail_mail_id_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-mail.read_mail.v1"]}],"operationId":"get_characters_character_id_mail_mail_id","x-cached-seconds":30,"x-alternate-versions":["dev","legacy","v1"]},"put":{"description":"Update metadata about a mail\n\n---\nAlternate route: `/dev/characters/{character_id}/mail/{mail_id}/`\n\nAlternate route: `/legacy/characters/{character_id}/mail/{mail_id}/`\n\nAlternate route: `/v1/characters/{character_id}/mail/{mail_id}/`\n","summary":"Update metadata about a mail","tags":["Mail"],"parameters":[{"$ref":"#/parameters/character_id"},{"name":"contents","in":"body","description":"Data used to update the mail","required":true,"schema":{"type":"object","properties":{"read":{"type":"boolean","description":"Whether the mail is flagged as read","title":"put_characters_character_id_mail_mail_id_read"},"labels":{"type":"array","maxItems":25,"description":"Labels to assign to the mail. Pre-existing labels are unassigned.","items":{"type":"integer","format":"int64","uniqueItems":true,"minimum":0,"title":"put_characters_character_id_mail_mail_id_label","description":"label integer"},"title":"put_characters_character_id_mail_mail_id_labels"}},"title":"put_characters_character_id_mail_mail_id_contents","description":"contents object"}},{"$ref":"#/parameters/datasource"},{"name":"mail_id","in":"path","description":"An EVE mail ID","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"204":{"description":"Mail updated"},"400":{"description":"Invalid label ID; or No parameters in body -- nothing to do","schema":{"type":"object","title":"put_characters_character_id_mail_mail_id_bad_request","description":"Bad request","properties":{"error":{"type":"string","description":"Bad request message","title":"put_characters_character_id_mail_mail_id_400_bad_request"}}},"examples":{"application/json":{"error":"Bad request message"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-mail.organize_mail.v1"]}],"operationId":"put_characters_character_id_mail_mail_id","x-alternate-versions":["dev","legacy","v1"]}},"/markets/prices/":{"get":{"description":"Return a list of prices\n\n---\nAlternate route: `/dev/markets/prices/`\n\nAlternate route: `/legacy/markets/prices/`\n\nAlternate route: `/v1/markets/prices/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"List market prices","tags":["Market"],"responses":{"200":{"description":"A list of prices","examples":{"application/json":[{"type_id":32772,"average_price":306292.67,"adjusted_price":306988.09}]},"schema":{"type":"array","maxItems":20000,"items":{"type":"object","required":["type_id"],"properties":{"type_id":{"type":"integer","format":"int32","title":"get_markets_prices_type_id","description":"type_id integer"},"average_price":{"type":"number","format":"double","title":"get_markets_prices_average_price","description":"average_price number"},"adjusted_price":{"type":"number","format":"double","title":"get_markets_prices_adjusted_price","description":"adjusted_price number"}},"title":"get_markets_prices_200_ok","description":"200 ok object"},"title":"get_markets_prices_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_markets_prices","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/markets/{region_id}/orders/":{"get":{"description":"Return a list of orders in a region\n\n---\nAlternate route: `/dev/markets/{region_id}/orders/`\n\nAlternate route: `/legacy/markets/{region_id}/orders/`\n\nAlternate route: `/v1/markets/{region_id}/orders/`\n\n---\nThis route is cached for up to 300 seconds","summary":"List orders in a region","tags":["Market"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"order_type","in":"query","description":"Filter buy/sell orders, return all orders by default. If you query without type_id, we always return both buy and sell orders.","required":true,"type":"string","enum":["buy","sell","all"],"default":"all"},{"$ref":"#/parameters/page"},{"name":"region_id","in":"path","description":"Return orders in this region","required":true,"type":"integer","format":"int32"},{"name":"type_id","in":"query","description":"Return orders only for this type","required":false,"type":"integer","format":"int32"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of orders","examples":{"application/json":[{"order_id":4623824223,"type_id":34,"location_id":60005599,"volume_total":2000000,"volume_remain":1296000,"min_volume":1,"price":9.9,"is_buy_order":false,"duration":90,"issued":"2016-09-03T05:12:25Z","range":"region"}]},"schema":{"type":"array","maxItems":10000,"items":{"type":"object","required":["order_id","type_id","location_id","volume_total","volume_remain","min_volume","price","is_buy_order","duration","issued","range"],"properties":{"order_id":{"type":"integer","format":"int64","title":"get_markets_region_id_orders_order_id","description":"order_id integer"},"type_id":{"type":"integer","format":"int32","title":"get_markets_region_id_orders_type_id","description":"type_id integer"},"location_id":{"type":"integer","format":"int64","title":"get_markets_region_id_orders_location_id","description":"location_id integer"},"volume_total":{"type":"integer","format":"int32","title":"get_markets_region_id_orders_volume_total","description":"volume_total integer"},"volume_remain":{"type":"integer","format":"int32","title":"get_markets_region_id_orders_volume_remain","description":"volume_remain integer"},"min_volume":{"type":"integer","format":"int32","title":"get_markets_region_id_orders_min_volume","description":"min_volume integer"},"price":{"type":"number","format":"double","title":"get_markets_region_id_orders_price","description":"price number"},"is_buy_order":{"type":"boolean","title":"get_markets_region_id_orders_is_buy_order","description":"is_buy_order boolean"},"duration":{"type":"integer","format":"int32","title":"get_markets_region_id_orders_duration","description":"duration integer"},"issued":{"type":"string","format":"date-time","title":"get_markets_region_id_orders_issued","description":"issued string"},"range":{"type":"string","enum":["station","region","solarsystem","1","2","3","4","5","10","20","30","40"],"title":"get_markets_region_id_orders_range","description":"range string"}},"title":"get_markets_region_id_orders_200_ok","description":"200 ok object"},"title":"get_markets_region_id_orders_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"422":{"description":"Not found","schema":{"type":"object","title":"get_markets_region_id_orders_unprocessable_entity","description":"Unprocessable entity","properties":{"error":{"type":"string","description":"Unprocessable entity message","title":"get_markets_region_id_orders_422_unprocessable_entity"}}},"examples":{"application/json":{"error":"Unprocessable entity message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_markets_region_id_orders","x-cached-seconds":300,"x-alternate-versions":["dev","legacy","v1"]}},"/markets/{region_id}/history/":{"get":{"description":"Return a list of historical market statistics for the specified type in a region\n\n---\nAlternate route: `/dev/markets/{region_id}/history/`\n\nAlternate route: `/legacy/markets/{region_id}/history/`\n\nAlternate route: `/v1/markets/{region_id}/history/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"List historical market statistics in a region","tags":["Market"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"region_id","in":"path","description":"Return statistics in this region","required":true,"type":"integer","format":"int32"},{"name":"type_id","in":"query","description":"Return statistics for this type","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of historical market statistics","examples":{"application/json":[{"date":"2015-05-01","order_count":2267,"volume":16276782035,"highest":5.27,"average":5.25,"lowest":5.11}]},"schema":{"type":"array","maxItems":500,"items":{"type":"object","required":["date","order_count","volume","highest","average","lowest"],"properties":{"date":{"type":"string","format":"date","description":"The date of this historical statistic entry","title":"get_markets_region_id_history_date"},"order_count":{"type":"integer","format":"int64","description":"Total number of orders happened that day","title":"get_markets_region_id_history_order_count"},"volume":{"type":"integer","format":"int64","description":"Total","title":"get_markets_region_id_history_volume"},"highest":{"type":"number","format":"double","title":"get_markets_region_id_history_highest","description":"highest number"},"average":{"type":"number","format":"double","title":"get_markets_region_id_history_average","description":"average number"},"lowest":{"type":"number","format":"double","title":"get_markets_region_id_history_lowest","description":"lowest number"}},"title":"get_markets_region_id_history_200_ok","description":"200 ok object"},"title":"get_markets_region_id_history_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"422":{"description":"Not found","schema":{"type":"object","title":"get_markets_region_id_history_unprocessable_entity","description":"Unprocessable entity","properties":{"error":{"type":"string","description":"Unprocessable entity message","title":"get_markets_region_id_history_422_unprocessable_entity"}}},"examples":{"application/json":{"error":"Unprocessable entity message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_markets_region_id_history","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/markets/structures/{structure_id}/":{"get":{"description":"Return all orders in a structure\n\n---\nAlternate route: `/dev/markets/structures/{structure_id}/`\n\nAlternate route: `/legacy/markets/structures/{structure_id}/`\n\nAlternate route: `/v1/markets/structures/{structure_id}/`\n\n---\nThis route is cached for up to 300 seconds","summary":"List orders in a structure","tags":["Market"],"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"name":"structure_id","in":"path","description":"Return orders in this structure","required":true,"type":"integer","format":"int64"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of orders","examples":{"application/json":[{"order_id":4623824223,"type_id":34,"location_id":1020988381992,"volume_total":2000000,"volume_remain":1296000,"min_volume":1,"price":9.9,"is_buy_order":false,"duration":90,"issued":"2016-09-03T05:12:25Z","range":"region"}]},"schema":{"type":"array","maxItems":5000,"items":{"type":"object","required":["order_id","type_id","location_id","volume_total","volume_remain","min_volume","price","is_buy_order","duration","issued","range"],"properties":{"order_id":{"type":"integer","format":"int64","title":"get_markets_structures_structure_id_order_id","description":"order_id integer"},"type_id":{"type":"integer","format":"int32","title":"get_markets_structures_structure_id_type_id","description":"type_id integer"},"location_id":{"type":"integer","format":"int64","title":"get_markets_structures_structure_id_location_id","description":"location_id integer"},"volume_total":{"type":"integer","format":"int32","title":"get_markets_structures_structure_id_volume_total","description":"volume_total integer"},"volume_remain":{"type":"integer","format":"int32","title":"get_markets_structures_structure_id_volume_remain","description":"volume_remain integer"},"min_volume":{"type":"integer","format":"int32","title":"get_markets_structures_structure_id_min_volume","description":"min_volume integer"},"price":{"type":"number","format":"double","title":"get_markets_structures_structure_id_price","description":"price number"},"is_buy_order":{"type":"boolean","title":"get_markets_structures_structure_id_is_buy_order","description":"is_buy_order boolean"},"duration":{"type":"integer","format":"int32","title":"get_markets_structures_structure_id_duration","description":"duration integer"},"issued":{"type":"string","format":"date-time","title":"get_markets_structures_structure_id_issued","description":"issued string"},"range":{"type":"string","enum":["station","region","solarsystem","1","2","3","4","5","10","20","30","40"],"title":"get_markets_structures_structure_id_range","description":"range string"}},"title":"get_markets_structures_structure_id_200_ok","description":"200 ok object"},"title":"get_markets_structures_structure_id_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-markets.structure_markets.v1"]}],"operationId":"get_markets_structures_structure_id","x-cached-seconds":300,"x-alternate-versions":["dev","legacy","v1"]}},"/markets/groups/":{"get":{"description":"Get a list of item groups\n\n---\nAlternate route: `/dev/markets/groups/`\n\nAlternate route: `/legacy/markets/groups/`\n\nAlternate route: `/v1/markets/groups/`\n\n---\nThis route expires daily at 11:05","summary":"Get item groups","tags":["Market"],"responses":{"200":{"description":"A list of item group ids","examples":{"application/json":[1,2,3]},"schema":{"type":"array","maxItems":5000,"items":{"type":"integer","format":"int32","title":"get_markets_groups_200_ok","description":"200 ok integer"},"title":"get_markets_groups_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_markets_groups","x-alternate-versions":["dev","legacy","v1"]}},"/markets/groups/{market_group_id}/":{"get":{"description":"Get information on an item group\n\n---\nAlternate route: `/dev/markets/groups/{market_group_id}/`\n\nAlternate route: `/legacy/markets/groups/{market_group_id}/`\n\nAlternate route: `/v1/markets/groups/{market_group_id}/`\n\n---\nThis route expires daily at 11:05","summary":"Get item group information","tags":["Market"],"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/language"},{"name":"market_group_id","in":"path","description":"An Eve item group ID","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Information about an item group","examples":{"application/json":{"market_group_id":5,"name":"Standard Frigates","description":"Small, fast vessels suited to a variety of purposes.","types":[582,583],"parent_group_id":1361}},"schema":{"type":"object","required":["market_group_id","name","description","types"],"properties":{"market_group_id":{"type":"integer","format":"int32","title":"get_markets_groups_market_group_id_market_group_id","description":"market_group_id integer"},"name":{"type":"string","title":"get_markets_groups_market_group_id_name","description":"name string"},"description":{"type":"string","title":"get_markets_groups_market_group_id_description","description":"description string"},"types":{"type":"array","maxItems":5000,"items":{"type":"integer","format":"int32","title":"get_markets_groups_market_group_id_type","description":"type integer"},"title":"get_markets_groups_market_group_id_types","description":"types array"},"parent_group_id":{"type":"integer","format":"int32","title":"get_markets_groups_market_group_id_parent_group_id","description":"parent_group_id integer"}},"title":"get_markets_groups_market_group_id_ok","description":"200 ok object"},"headers":{"Content-Language":{"description":"The language used in the response","type":"string","enum":["de","en-us","fr","ja","ru","zh"]},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"Market group not found","schema":{"type":"object","title":"get_markets_groups_market_group_id_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"get_markets_groups_market_group_id_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_markets_groups_market_group_id","x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/orders/":{"get":{"summary":"List orders from a character","description":"List market orders placed by a character\n\n---\nAlternate route: `/dev/characters/{character_id}/orders/`\n\nAlternate route: `/legacy/characters/{character_id}/orders/`\n\nAlternate route: `/v1/characters/{character_id}/orders/`\n\n---\nThis route is cached for up to 3600 seconds","tags":["Market"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Market orders placed by a character","examples":{"application/json":[{"order_id":123,"type_id":456,"region_id":123,"location_id":456,"range":"station","is_buy_order":true,"price":33.3,"volume_total":123456,"volume_remain":4422,"issued":"2016-09-03T05:12:25Z","state":"open","min_volume":1,"account_id":1000,"duration":30,"is_corp":false,"escrow":45.6}]},"schema":{"type":"array","maxItems":500,"items":{"type":"object","required":["order_id","type_id","region_id","location_id","range","is_buy_order","price","volume_total","volume_remain","issued","state","min_volume","account_id","duration","is_corp","escrow"],"properties":{"order_id":{"type":"integer","format":"int64","description":"Unique order ID","title":"get_characters_character_id_orders_order_id"},"type_id":{"type":"integer","format":"int32","description":"The type ID of the item transacted in this order","title":"get_characters_character_id_orders_type_id"},"region_id":{"type":"integer","format":"int32","description":"ID of the region where order was placed","title":"get_characters_character_id_orders_region_id"},"location_id":{"type":"integer","format":"int64","description":"ID of the location where order was placed","title":"get_characters_character_id_orders_location_id"},"range":{"type":"string","description":"Valid order range, numbers are ranges in jumps","enum":["1","10","2","20","3","30","4","40","5","region","solarsystem","station"],"title":"get_characters_character_id_orders_range"},"is_buy_order":{"type":"boolean","description":"True for a bid (buy) order. False for an offer (sell) order","title":"get_characters_character_id_orders_is_buy_order"},"price":{"type":"number","format":"double","description":"Cost per unit for this order","title":"get_characters_character_id_orders_price"},"volume_total":{"type":"integer","format":"int32","description":"Quantity of items required or offered at time order was placed","title":"get_characters_character_id_orders_volume_total"},"volume_remain":{"type":"integer","format":"int32","description":"Quantity of items still required or offered","title":"get_characters_character_id_orders_volume_remain"},"issued":{"type":"string","format":"date-time","description":"Date and time when this order was issued","title":"get_characters_character_id_orders_issued"},"state":{"type":"string","description":"Current order state","enum":["cancelled","character_deleted","closed","expired","open","pending"],"title":"get_characters_character_id_orders_state"},"min_volume":{"type":"integer","format":"int32","description":"For bids (buy orders), the minimum quantity that will be accepted in a matching offer (sell order)","title":"get_characters_character_id_orders_min_volume"},"account_id":{"type":"integer","format":"int32","description":"Wallet division for the buyer or seller of this order. Always 1000 for characters. Currently 1000 through 1006 for corporations","title":"get_characters_character_id_orders_account_id"},"duration":{"type":"integer","format":"int32","description":"Numer of days for which order is valid (starting from the issued date). An order expires at time issued + duration","title":"get_characters_character_id_orders_duration"},"is_corp":{"type":"boolean","title":"get_characters_character_id_orders_is_corp","description":"is_corp boolean"},"escrow":{"type":"number","format":"double","description":"For buy orders, the amount of ISK in escrow","title":"get_characters_character_id_orders_escrow"}},"title":"get_characters_character_id_orders_200_ok","description":"200 ok object"},"title":"get_characters_character_id_orders_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-markets.read_character_orders.v1"]}],"operationId":"get_characters_character_id_orders","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/markets/{region_id}/types/":{"get":{"description":"Return a list of type IDs that have active orders in the region, for efficient market indexing.\n\n---\nAlternate route: `/dev/markets/{region_id}/types/`\n\nAlternate route: `/legacy/markets/{region_id}/types/`\n\nAlternate route: `/v1/markets/{region_id}/types/`\n\n---\nThis route is cached for up to 600 seconds","summary":"List type IDs relevant to a market","tags":["Market"],"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"name":"region_id","in":"path","description":"Return statistics in this region","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of type IDs","examples":{"application/json":[587,593,597]},"schema":{"type":"array","maxItems":1000,"items":{"type":"integer","format":"int32","title":"get_markets_region_id_types_200_ok","description":"200 ok integer"},"title":"get_markets_region_id_types_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_markets_region_id_types","x-cached-seconds":600,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/orders/":{"get":{"summary":"List orders from a corporation","description":"List market orders placed on behalf of a corporation\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/orders/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/orders/`\n\nAlternate route: `/v1/corporations/{corporation_id}/orders/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\nRequires one of the following EVE corporation role(s): Accountant, Trader\n","tags":["Market"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of market orders","examples":{"application/json":[{"order_id":123,"type_id":456,"region_id":123,"location_id":456,"range":"station","is_buy_order":true,"price":33.3,"volume_total":123456,"volume_remain":4422,"issued":"2016-09-03T05:12:25Z","state":"open","min_volume":1,"wallet_division":1,"duration":30,"escrow":45.6}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["order_id","type_id","region_id","location_id","range","is_buy_order","price","volume_total","volume_remain","issued","state","min_volume","wallet_division","duration","escrow"],"properties":{"order_id":{"type":"integer","format":"int64","description":"Unique order ID","title":"get_corporations_corporation_id_orders_order_id"},"type_id":{"type":"integer","format":"int32","description":"The type ID of the item transacted in this order","title":"get_corporations_corporation_id_orders_type_id"},"region_id":{"type":"integer","format":"int32","description":"ID of the region where order was placed","title":"get_corporations_corporation_id_orders_region_id"},"location_id":{"type":"integer","format":"int64","description":"ID of the location where order was placed","title":"get_corporations_corporation_id_orders_location_id"},"range":{"type":"string","description":"Valid order range, numbers are ranges in jumps","enum":["1","10","2","20","3","30","4","40","5","region","solarsystem","station"],"title":"get_corporations_corporation_id_orders_range"},"is_buy_order":{"type":"boolean","description":"True for a bid (buy) order. False for an offer (sell) order","title":"get_corporations_corporation_id_orders_is_buy_order"},"price":{"type":"number","format":"double","description":"Cost per unit for this order","title":"get_corporations_corporation_id_orders_price"},"volume_total":{"type":"integer","format":"int32","description":"Quantity of items required or offered at time order was placed","title":"get_corporations_corporation_id_orders_volume_total"},"volume_remain":{"type":"integer","format":"int32","description":"Quantity of items still required or offered","title":"get_corporations_corporation_id_orders_volume_remain"},"issued":{"type":"string","format":"date-time","description":"Date and time when this order was issued","title":"get_corporations_corporation_id_orders_issued"},"state":{"type":"string","description":"Current order state","enum":["cancelled","character_deleted","closed","expired","open","pending"],"title":"get_corporations_corporation_id_orders_state"},"min_volume":{"type":"integer","format":"int32","description":"For bids (buy orders), the minimum quantity that will be accepted in a matching offer (sell order)","title":"get_corporations_corporation_id_orders_min_volume"},"wallet_division":{"type":"integer","format":"int32","minimum":1,"maximum":7,"description":"Wallet division of which this order used","title":"get_corporations_corporation_id_orders_wallet_division"},"duration":{"type":"integer","format":"int32","description":"Numer of days for which order is valid (starting from the issued date). An order expires at time issued + duration","title":"get_corporations_corporation_id_orders_duration"},"escrow":{"type":"number","format":"double","description":"For buy orders, the amount of ISK in escrow","title":"get_corporations_corporation_id_orders_escrow"}},"title":"get_corporations_corporation_id_orders_200_ok","description":"200 ok object"},"title":"get_corporations_corporation_id_orders_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":["Accountant","Trader"],"security":[{"evesso":["esi-markets.read_corporation_orders.v1"]}],"operationId":"get_corporations_corporation_id_orders","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/opportunities/groups/":{"get":{"description":"Return a list of opportunities groups\n\n---\nAlternate route: `/dev/opportunities/groups/`\n\nAlternate route: `/legacy/opportunities/groups/`\n\nAlternate route: `/v1/opportunities/groups/`\n\n---\nThis route expires daily at 11:05","summary":"Get opportunities groups","tags":["Opportunities"],"responses":{"200":{"description":"A list of opportunities group ids","examples":{"application/json":[100,101,102,103]},"schema":{"type":"array","maxItems":50,"items":{"type":"integer","format":"int32","title":"get_opportunities_groups_200_ok","description":"200 ok integer"},"title":"get_opportunities_groups_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_opportunities_groups","x-alternate-versions":["dev","legacy","v1"]}},"/opportunities/groups/{group_id}/":{"get":{"description":"Return information of an opportunities group\n\n---\nAlternate route: `/dev/opportunities/groups/{group_id}/`\n\nAlternate route: `/legacy/opportunities/groups/{group_id}/`\n\nAlternate route: `/v1/opportunities/groups/{group_id}/`\n\n---\nThis route expires daily at 11:05","summary":"Get opportunities group","tags":["Opportunities"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"group_id","in":"path","description":"ID of an opportunities group","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/language"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Details of an opportunities group","examples":{"application/json":{"group_id":103,"name":"Welcome to New Eden","description":"As a capsuleer...","notification":"Completed:
Welcome to New Eden","required_tasks":[19],"connected_groups":[100]}},"schema":{"type":"object","required":["group_id","name","description","notification","required_tasks","connected_groups"],"properties":{"group_id":{"type":"integer","format":"int32","title":"get_opportunities_groups_group_id_group_id","description":"group_id integer"},"name":{"type":"string","title":"get_opportunities_groups_group_id_name","description":"name string"},"description":{"type":"string","title":"get_opportunities_groups_group_id_description","description":"description string"},"notification":{"type":"string","title":"get_opportunities_groups_group_id_notification","description":"notification string"},"required_tasks":{"description":"Tasks need to complete for this group","type":"array","maxItems":100,"items":{"type":"integer","format":"int32","title":"get_opportunities_groups_group_id_required_task","description":"required_task integer"},"title":"get_opportunities_groups_group_id_required_tasks"},"connected_groups":{"description":"The groups that are connected to this group on the opportunities map","type":"array","maxItems":50,"items":{"type":"integer","format":"int32","title":"get_opportunities_groups_group_id_connected_group","description":"connected_group integer"},"title":"get_opportunities_groups_group_id_connected_groups"}},"title":"get_opportunities_groups_group_id_ok","description":"200 ok object"},"headers":{"Content-Language":{"description":"The language used in the response","type":"string","enum":["de","en-us","fr","ja","ru","zh"]},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_opportunities_groups_group_id","x-alternate-versions":["dev","legacy","v1"]}},"/opportunities/tasks/":{"get":{"description":"Return a list of opportunities tasks\n\n---\nAlternate route: `/dev/opportunities/tasks/`\n\nAlternate route: `/legacy/opportunities/tasks/`\n\nAlternate route: `/v1/opportunities/tasks/`\n\n---\nThis route expires daily at 11:05","summary":"Get opportunities tasks","tags":["Opportunities"],"responses":{"200":{"description":"A list of opportunities task ids","examples":{"application/json":[1,2,3,4]},"schema":{"type":"array","maxItems":100,"items":{"type":"integer","format":"int32","title":"get_opportunities_tasks_200_ok","description":"200 ok integer"},"title":"get_opportunities_tasks_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_opportunities_tasks","x-alternate-versions":["dev","legacy","v1"]}},"/opportunities/tasks/{task_id}/":{"get":{"description":"Return information of an opportunities task\n\n---\nAlternate route: `/dev/opportunities/tasks/{task_id}/`\n\nAlternate route: `/legacy/opportunities/tasks/{task_id}/`\n\nAlternate route: `/v1/opportunities/tasks/{task_id}/`\n\n---\nThis route expires daily at 11:05","summary":"Get opportunities task","tags":["Opportunities"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"task_id","in":"path","description":"ID of an opportunities task","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Details of an opportunities task","examples":{"application/json":{"task_id":10,"name":"Dock in the station","description":"To use station services...","notification":"Completed:
Docked in a station!"}},"schema":{"type":"object","required":["task_id","name","description","notification"],"properties":{"task_id":{"type":"integer","format":"int32","title":"get_opportunities_tasks_task_id_task_id","description":"task_id integer"},"name":{"type":"string","title":"get_opportunities_tasks_task_id_name","description":"name string"},"description":{"type":"string","title":"get_opportunities_tasks_task_id_description","description":"description string"},"notification":{"type":"string","title":"get_opportunities_tasks_task_id_notification","description":"notification string"}},"title":"get_opportunities_tasks_task_id_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_opportunities_tasks_task_id","x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/opportunities/":{"get":{"description":"Return a list of tasks finished by a character\n\n---\nAlternate route: `/dev/characters/{character_id}/opportunities/`\n\nAlternate route: `/legacy/characters/{character_id}/opportunities/`\n\nAlternate route: `/v1/characters/{character_id}/opportunities/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get a character's completed tasks","tags":["Opportunities"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of opportunities task ids","examples":{"application/json":[{"task_id":1,"completed_at":"2016-04-29T12:34:56Z"}]},"schema":{"type":"array","maxItems":100,"items":{"type":"object","required":["task_id","completed_at"],"properties":{"task_id":{"type":"integer","format":"int32","title":"get_characters_character_id_opportunities_task_id","description":"task_id integer"},"completed_at":{"type":"string","format":"date-time","title":"get_characters_character_id_opportunities_completed_at","description":"completed_at string"}},"title":"get_characters_character_id_opportunities_200_ok","description":"200 ok object"},"title":"get_characters_character_id_opportunities_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-characters.read_opportunities.v1"]}],"operationId":"get_characters_character_id_opportunities","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/planets/":{"get":{"description":"Returns a list of all planetary colonies owned by a character.\n\n---\nAlternate route: `/dev/characters/{character_id}/planets/`\n\nAlternate route: `/legacy/characters/{character_id}/planets/`\n\nAlternate route: `/v1/characters/{character_id}/planets/`\n\n---\nThis route is cached for up to 600 seconds","summary":"Get colonies","tags":["Planetary Interaction"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"List of colonies","examples":{"application/json":[{"solar_system_id":30000379,"upgrade_level":0,"planet_id":40023691,"last_update":"2016-11-28T16:42:51Z","owner_id":90000001,"planet_type":"plasma","num_pins":1},{"solar_system_id":30000379,"upgrade_level":0,"planet_id":40023697,"last_update":"2016-11-28T16:41:54Z","owner_id":90000001,"planet_type":"barren","num_pins":1}]},"schema":{"type":"array","maxItems":10,"items":{"type":"object","required":["solar_system_id","planet_id","planet_type","owner_id","last_update","upgrade_level","num_pins"],"properties":{"solar_system_id":{"type":"integer","format":"int32","title":"get_characters_character_id_planets_solar_system_id","description":"solar_system_id integer"},"planet_id":{"type":"integer","format":"int32","title":"get_characters_character_id_planets_planet_id","description":"planet_id integer"},"owner_id":{"type":"integer","format":"int32","title":"get_characters_character_id_planets_owner_id","description":"owner_id integer"},"upgrade_level":{"type":"integer","format":"int32","minimum":0,"maximum":5,"title":"get_characters_character_id_planets_upgrade_level","description":"upgrade_level integer"},"num_pins":{"type":"integer","format":"int32","minimum":1,"title":"get_characters_character_id_planets_num_pins","description":"num_pins integer"},"last_update":{"type":"string","format":"date-time","title":"get_characters_character_id_planets_last_update","description":"last_update string"},"planet_type":{"type":"string","enum":["temperate","barren","oceanic","ice","gas","lava","storm","plasma"],"title":"get_characters_character_id_planets_planet_type","description":"planet_type string"}},"title":"get_characters_character_id_planets_200_ok","description":"200 ok object"},"title":"get_characters_character_id_planets_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-planets.manage_planets.v1"]}],"operationId":"get_characters_character_id_planets","x-cached-seconds":600,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/planets/{planet_id}/":{"get":{"description":"Returns full details on the layout of a single planetary colony, including links, pins and routes. Note: Planetary information is only recalculated when the colony is viewed through the client. Information will not update until this criteria is met.\n\n---\nAlternate route: `/dev/characters/{character_id}/planets/{planet_id}/`\n\nAlternate route: `/v3/characters/{character_id}/planets/{planet_id}/`\n\n---\nThis route is cached for up to 600 seconds","summary":"Get colony layout","tags":["Planetary Interaction"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"description":"Planet id of the target planet","format":"int32","in":"path","name":"planet_id","required":true,"type":"integer"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Colony layout","examples":{"application/json":{"links":[{"source_pin_id":1000000017021,"destination_pin_id":1000000017022,"link_level":0}],"pins":[{"longitude":0.717145933308,"is_running":true,"type_id":2254,"pin_id":1000000017021,"latitude":1.55087844973},{"longitude":0.709775584394,"is_running":true,"type_id":2256,"pin_id":1000000017022,"latitude":1.53360639935}],"routes":[{"source_pin_id":1000000017029,"quantity":20,"content_type_id":2393,"route_id":4,"destination_pin_id":1000000017030}]}},"schema":{"type":"object","required":["links","pins","routes"],"properties":{"links":{"type":"array","maxItems":500,"items":{"type":"object","required":["source_pin_id","destination_pin_id","link_level"],"properties":{"source_pin_id":{"type":"integer","format":"int64","title":"get_characters_character_id_planets_planet_id_source_pin_id","description":"source_pin_id integer"},"destination_pin_id":{"type":"integer","format":"int64","title":"get_characters_character_id_planets_planet_id_destination_pin_id","description":"destination_pin_id integer"},"link_level":{"type":"integer","format":"int32","minimum":0,"maximum":10,"title":"get_characters_character_id_planets_planet_id_link_level","description":"link_level integer"}},"title":"get_characters_character_id_planets_planet_id_link","description":"link object"},"title":"get_characters_character_id_planets_planet_id_links","description":"links array"},"pins":{"type":"array","maxItems":100,"items":{"type":"object","required":["pin_id","type_id","latitude","longitude"],"properties":{"latitude":{"type":"number","format":"float","title":"get_characters_character_id_planets_planet_id_latitude","description":"latitude number"},"longitude":{"type":"number","format":"float","title":"get_characters_character_id_planets_planet_id_longitude","description":"longitude number"},"pin_id":{"type":"integer","format":"int64","title":"get_characters_character_id_planets_planet_id_pin_id","description":"pin_id integer"},"type_id":{"type":"integer","format":"int32","title":"get_characters_character_id_planets_planet_id_type_id","description":"type_id integer"},"schematic_id":{"type":"integer","format":"int32","title":"get_characters_character_id_planets_planet_id_schematic_id","description":"schematic_id integer"},"extractor_details":{"type":"object","required":["heads"],"properties":{"heads":{"type":"array","maxItems":10,"items":{"type":"object","required":["head_id","latitude","longitude"],"properties":{"head_id":{"type":"integer","format":"int32","minimum":0,"maximum":9,"title":"get_characters_character_id_planets_planet_id_head_id","description":"head_id integer"},"latitude":{"type":"number","format":"float","title":"get_characters_character_id_planets_planet_id_latitude","description":"latitude number"},"longitude":{"type":"number","format":"float","title":"get_characters_character_id_planets_planet_id_longitude","description":"longitude number"}},"title":"get_characters_character_id_planets_planet_id_head","description":"head object"},"title":"get_characters_character_id_planets_planet_id_heads","description":"heads array"},"product_type_id":{"type":"integer","format":"int32","title":"get_characters_character_id_planets_planet_id_product_type_id","description":"product_type_id integer"},"cycle_time":{"description":"in seconds","type":"integer","format":"int32","title":"get_characters_character_id_planets_planet_id_cycle_time"},"head_radius":{"type":"number","format":"float","title":"get_characters_character_id_planets_planet_id_head_radius","description":"head_radius number"},"qty_per_cycle":{"type":"integer","format":"int32","title":"get_characters_character_id_planets_planet_id_qty_per_cycle","description":"qty_per_cycle integer"}},"title":"get_characters_character_id_planets_planet_id_extractor_details","description":"extractor_details object"},"factory_details":{"type":"object","required":["schematic_id"],"properties":{"schematic_id":{"type":"integer","format":"int32","title":"get_characters_character_id_planets_planet_id_schematic_id","description":"schematic_id integer"}},"title":"get_characters_character_id_planets_planet_id_factory_details","description":"factory_details object"},"contents":{"type":"array","maxItems":90,"items":{"type":"object","required":["type_id","amount"],"properties":{"type_id":{"type":"integer","format":"int32","title":"get_characters_character_id_planets_planet_id_type_id","description":"type_id integer"},"amount":{"type":"integer","format":"int64","title":"get_characters_character_id_planets_planet_id_amount","description":"amount integer"}},"title":"get_characters_character_id_planets_planet_id_content","description":"content object"},"title":"get_characters_character_id_planets_planet_id_contents","description":"contents array"},"install_time":{"type":"string","format":"date-time","title":"get_characters_character_id_planets_planet_id_install_time","description":"install_time string"},"expiry_time":{"type":"string","format":"date-time","title":"get_characters_character_id_planets_planet_id_expiry_time","description":"expiry_time string"},"last_cycle_start":{"type":"string","format":"date-time","title":"get_characters_character_id_planets_planet_id_last_cycle_start","description":"last_cycle_start string"}},"title":"get_characters_character_id_planets_planet_id_pin","description":"pin object"},"title":"get_characters_character_id_planets_planet_id_pins","description":"pins array"},"routes":{"type":"array","maxItems":1000,"items":{"type":"object","required":["route_id","source_pin_id","destination_pin_id","content_type_id","quantity"],"properties":{"route_id":{"type":"integer","format":"int64","title":"get_characters_character_id_planets_planet_id_route_id","description":"route_id integer"},"source_pin_id":{"type":"integer","format":"int64","title":"get_characters_character_id_planets_planet_id_source_pin_id","description":"source_pin_id integer"},"destination_pin_id":{"type":"integer","format":"int64","title":"get_characters_character_id_planets_planet_id_destination_pin_id","description":"destination_pin_id integer"},"content_type_id":{"type":"integer","format":"int32","title":"get_characters_character_id_planets_planet_id_content_type_id","description":"content_type_id integer"},"quantity":{"type":"number","format":"float","title":"get_characters_character_id_planets_planet_id_quantity","description":"quantity number"},"waypoints":{"type":"array","maxItems":5,"description":"list of pin ID waypoints","items":{"type":"integer","format":"int64","title":"get_characters_character_id_planets_planet_id_waypoint","description":"waypoint integer"},"title":"get_characters_character_id_planets_planet_id_waypoints"}},"title":"get_characters_character_id_planets_planet_id_route","description":"route object"},"title":"get_characters_character_id_planets_planet_id_routes","description":"routes array"}},"title":"get_characters_character_id_planets_planet_id_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"Colony not found","examples":{"application/json":{"error":"Colony not found"}},"schema":{"type":"object","description":"Colony not found","properties":{"error":{"type":"string","description":"error message","title":"get_characters_character_id_planets_planet_id_error"}},"title":"get_characters_character_id_planets_planet_id_not_found"}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-planets.manage_planets.v1"]}],"operationId":"get_characters_character_id_planets_planet_id","x-cached-seconds":600,"x-alternate-versions":["dev","v3"]}},"/universe/schematics/{schematic_id}/":{"get":{"description":"Get information on a planetary factory schematic\n\n---\nAlternate route: `/dev/universe/schematics/{schematic_id}/`\n\nAlternate route: `/legacy/universe/schematics/{schematic_id}/`\n\nAlternate route: `/v1/universe/schematics/{schematic_id}/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get schematic information","tags":["Planetary Interaction"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"schematic_id","in":"path","description":"A PI schematic ID","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Public data about a schematic","examples":{"application/json":{"schematic_name":"Bacteria","cycle_time":1800}},"schema":{"type":"object","required":["schematic_name","cycle_time"],"properties":{"schematic_name":{"type":"string","title":"get_universe_schematics_schematic_id_schematic_name","description":"schematic_name string"},"cycle_time":{"type":"integer","format":"int32","description":"Time in seconds to process a run","title":"get_universe_schematics_schematic_id_cycle_time"}},"title":"get_universe_schematics_schematic_id_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"Schematic not found","examples":{"application/json":{"error":"Schematic not found"}},"schema":{"type":"object","description":"Schematic not found","properties":{"error":{"type":"string","description":"error message","title":"get_universe_schematics_schematic_id_error"}},"title":"get_universe_schematics_schematic_id_not_found"}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_universe_schematics_schematic_id","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/customs_offices/":{"get":{"description":"List customs offices owned by a corporation\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/customs_offices/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/customs_offices/`\n\nAlternate route: `/v1/corporations/{corporation_id}/customs_offices/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\nRequires one of the following EVE corporation role(s): Director\n","summary":"List corporation customs offices","tags":["Planetary Interaction"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of customs offices and their settings","examples":{"application/json":[{"office_id":1000000014530,"system_id":30003657,"reinforce_exit_start":23,"reinforce_exit_end":1,"allow_alliance_access":false,"allow_access_with_standings":true,"standing_level":"neutral","corporation_tax_rate":0.02,"alliance_tax_rate":0.1,"excellent_standing_tax_rate":0.05,"good_standing_tax_rate":0.2,"neutral_standing_tax_rate":0.5}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["office_id","system_id","reinforce_exit_start","reinforce_exit_end","allow_alliance_access","allow_access_with_standings"],"properties":{"office_id":{"type":"integer","format":"int64","description":"unique ID of this customs office","title":"get_corporations_corporation_id_customs_offices_office_id"},"system_id":{"type":"integer","format":"int32","description":"ID of the solar system this customs office is located in","title":"get_corporations_corporation_id_customs_offices_system_id"},"reinforce_exit_start":{"type":"integer","format":"int32","minimum":0,"maximum":23,"description":"Together with reinforce_exit_end, marks a 2-hour period where this customs office could exit reinforcement mode during the day after initial attack","title":"get_corporations_corporation_id_customs_offices_reinforce_exit_start"},"reinforce_exit_end":{"type":"integer","format":"int32","minimum":0,"maximum":23,"title":"get_corporations_corporation_id_customs_offices_reinforce_exit_end","description":"reinforce_exit_end integer"},"corporation_tax_rate":{"type":"number","format":"float","title":"get_corporations_corporation_id_customs_offices_corporation_tax_rate","description":"corporation_tax_rate number"},"allow_alliance_access":{"type":"boolean","title":"get_corporations_corporation_id_customs_offices_allow_alliance_access","description":"allow_alliance_access boolean"},"alliance_tax_rate":{"type":"number","format":"float","description":"Only present if alliance access is allowed","title":"get_corporations_corporation_id_customs_offices_alliance_tax_rate"},"allow_access_with_standings":{"type":"boolean","description":"standing_level and any standing related tax rate only present when this is true","title":"get_corporations_corporation_id_customs_offices_allow_access_with_standings"},"standing_level":{"type":"string","enum":["bad","excellent","good","neutral","terrible"],"description":"Access is allowed only for entities with this level of standing or better","title":"get_corporations_corporation_id_customs_offices_standing_level"},"excellent_standing_tax_rate":{"type":"number","format":"float","description":"Tax rate for entities with excellent level of standing, only present if this level is allowed, same for all other standing related tax rates","title":"get_corporations_corporation_id_customs_offices_excellent_standing_tax_rate"},"good_standing_tax_rate":{"type":"number","format":"float","title":"get_corporations_corporation_id_customs_offices_good_standing_tax_rate","description":"good_standing_tax_rate number"},"neutral_standing_tax_rate":{"type":"number","format":"float","title":"get_corporations_corporation_id_customs_offices_neutral_standing_tax_rate","description":"neutral_standing_tax_rate number"},"bad_standing_tax_rate":{"type":"number","format":"float","title":"get_corporations_corporation_id_customs_offices_bad_standing_tax_rate","description":"bad_standing_tax_rate number"},"terrible_standing_tax_rate":{"type":"number","format":"float","title":"get_corporations_corporation_id_customs_offices_terrible_standing_tax_rate","description":"terrible_standing_tax_rate number"}},"title":"get_corporations_corporation_id_customs_offices_200_ok","description":"200 ok object"},"title":"get_corporations_corporation_id_customs_offices_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":["Director"],"security":[{"evesso":["esi-planets.read_customs_offices.v1"]}],"operationId":"get_corporations_corporation_id_customs_offices","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/route/{origin}/{destination}/":{"get":{"description":"Get the systems between origin and destination\n\n---\nAlternate route: `/dev/route/{origin}/{destination}/`\n\nAlternate route: `/legacy/route/{origin}/{destination}/`\n\nAlternate route: `/v1/route/{origin}/{destination}/`\n\n---\nThis route is cached for up to 86400 seconds","summary":"Get route","tags":["Routes"],"parameters":[{"name":"avoid","in":"query","description":"avoid solar system ID(s)","type":"array","maxItems":100,"uniqueItems":true,"items":{"type":"integer","format":"int32"}},{"name":"connections","in":"query","type":"array","description":"connected solar system pairs","maxItems":100,"uniqueItems":true,"items":{"type":"array","minItems":2,"maxItems":2,"uniqueItems":true,"collectionFormat":"pipes","items":{"type":"integer","format":"int32"}}},{"$ref":"#/parameters/datasource"},{"name":"destination","in":"path","description":"destination solar system ID","type":"integer","format":"int32","required":true},{"name":"flag","in":"query","description":"route security preference","default":"shortest","type":"string","enum":["shortest","secure","insecure"]},{"name":"origin","in":"path","description":"origin solar system ID","type":"integer","format":"int32","required":true},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Solar systems in route from origin to destination","examples":{"application/json":[30002771,30002770,30002769,30002772]},"schema":{"type":"array","maxItems":1000,"description":"Solar systems in route","items":{"description":"Solar system in route","type":"integer","format":"int32","title":"Solar system ID"},"title":"get_route_origin_destination_ok"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"No route found","schema":{"type":"object","title":"get_route_origin_destination_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"get_route_origin_destination_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_route_origin_destination","x-cached-seconds":86400,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/search/":{"get":{"description":"Search for entities that match a given sub-string.\n\n---\nAlternate route: `/v2/characters/{character_id}/search/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\n[This route has an available update](https://esi.tech.ccp.is/diff/latest/dev/#GET-/characters/{character_id}/search/)","summary":"Search on a string","tags":["Search"],"parameters":[{"name":"categories","in":"query","description":"Type of entities to search for","required":true,"type":"array","minItems":1,"maxItems":12,"uniqueItems":true,"items":{"type":"string","enum":["agent","alliance","character","constellation","corporation","faction","inventorytype","region","solarsystem","station","structure","wormhole"]}},{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/language"},{"name":"search","in":"query","description":"The string to search on","required":true,"type":"string","minLength":3},{"name":"strict","in":"query","description":"Whether the search should be a strict match","type":"boolean","default":false},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of search results","examples":{"application/json":{"station":[60004588,60004594,60005725,60009106,60012721,60012724,60012727],"solarsystem":[30002510]}},"schema":{"type":"object","properties":{"agent":{"type":"array","maxItems":500,"items":{"type":"integer","format":"int32","title":"get_characters_character_id_search_agent","description":"agent integer"},"title":"get_characters_character_id_search_agent","description":"agent array"},"alliance":{"type":"array","maxItems":500,"items":{"type":"integer","format":"int32","title":"get_characters_character_id_search_alliance","description":"alliance integer"},"title":"get_characters_character_id_search_alliance","description":"alliance array"},"character":{"type":"array","maxItems":500,"items":{"type":"integer","format":"int32","title":"get_characters_character_id_search_character","description":"character integer"},"title":"get_characters_character_id_search_character","description":"character array"},"constellation":{"type":"array","maxItems":500,"items":{"type":"integer","format":"int32","title":"get_characters_character_id_search_constellation","description":"constellation integer"},"title":"get_characters_character_id_search_constellation","description":"constellation array"},"corporation":{"type":"array","maxItems":500,"items":{"type":"integer","format":"int32","title":"get_characters_character_id_search_corporation","description":"corporation integer"},"title":"get_characters_character_id_search_corporation","description":"corporation array"},"faction":{"type":"array","maxItems":500,"items":{"type":"integer","format":"int32","title":"get_characters_character_id_search_faction","description":"faction integer"},"title":"get_characters_character_id_search_faction","description":"faction array"},"inventorytype":{"type":"array","maxItems":500,"items":{"type":"integer","format":"int32","title":"get_characters_character_id_search_inventorytype","description":"inventorytype integer"},"title":"get_characters_character_id_search_inventorytype","description":"inventorytype array"},"region":{"type":"array","maxItems":500,"items":{"type":"integer","format":"int32","title":"get_characters_character_id_search_region","description":"region integer"},"title":"get_characters_character_id_search_region","description":"region array"},"solarsystem":{"type":"array","maxItems":500,"items":{"type":"integer","format":"int32","title":"get_characters_character_id_search_solarsystem","description":"solarsystem integer"},"title":"get_characters_character_id_search_solarsystem","description":"solarsystem array"},"station":{"type":"array","maxItems":500,"items":{"type":"integer","format":"int32","title":"get_characters_character_id_search_station","description":"station integer"},"title":"get_characters_character_id_search_station","description":"station array"},"structure":{"type":"array","maxItems":500,"items":{"type":"integer","format":"int64","title":"get_characters_character_id_search_structure","description":"structure integer"},"title":"get_characters_character_id_search_structure","description":"structure array"},"wormhole":{"type":"array","maxItems":500,"items":{"type":"integer","format":"int32","title":"get_characters_character_id_search_wormhole","description":"wormhole integer"},"title":"get_characters_character_id_search_wormhole","description":"wormhole array"}},"title":"get_characters_character_id_search_ok","description":"200 ok object"},"headers":{"Content-Language":{"description":"The language used in the response","type":"string","enum":["de","en-us","fr","ja","ru","zh"]},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-search.search_structures.v1"]}],"operationId":"get_characters_character_id_search","x-cached-seconds":3600,"x-alternate-versions":["v2"]}},"/search/":{"get":{"description":"Search for entities that match a given sub-string.\n\n---\nAlternate route: `/legacy/search/`\n\nAlternate route: `/v1/search/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\n[This route has an available update](https://esi.tech.ccp.is/diff/latest/dev/#GET-/search/)","summary":"Search on a string","tags":["Search"],"parameters":[{"name":"categories","in":"query","description":"Type of entities to search for","required":true,"type":"array","minItems":1,"maxItems":10,"uniqueItems":true,"items":{"type":"string","enum":["agent","alliance","character","constellation","corporation","faction","inventorytype","region","solarsystem","station","wormhole"]}},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/language"},{"name":"search","in":"query","description":"The string to search on","required":true,"type":"string","minLength":3},{"name":"strict","in":"query","description":"Whether the search should be a strict match","type":"boolean","default":false},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of search results","examples":{"application/json":{"station":[60004588,60004594,60005725,60009106,60012721,60012724,60012727],"solarsystem":[30002510]}},"schema":{"type":"object","properties":{"agent":{"type":"array","maxItems":500,"items":{"type":"integer","format":"int32","title":"get_search_agent","description":"agent integer"},"title":"get_search_agent","description":"agent array"},"alliance":{"type":"array","maxItems":500,"items":{"type":"integer","format":"int32","title":"get_search_alliance","description":"alliance integer"},"title":"get_search_alliance","description":"alliance array"},"character":{"type":"array","maxItems":500,"items":{"type":"integer","format":"int32","title":"get_search_character","description":"character integer"},"title":"get_search_character","description":"character array"},"constellation":{"type":"array","maxItems":500,"items":{"type":"integer","format":"int32","title":"get_search_constellation","description":"constellation integer"},"title":"get_search_constellation","description":"constellation array"},"corporation":{"type":"array","maxItems":500,"items":{"type":"integer","format":"int32","title":"get_search_corporation","description":"corporation integer"},"title":"get_search_corporation","description":"corporation array"},"faction":{"type":"array","maxItems":500,"items":{"type":"integer","format":"int32","title":"get_search_faction","description":"faction integer"},"title":"get_search_faction","description":"faction array"},"inventorytype":{"type":"array","maxItems":500,"items":{"type":"integer","format":"int32","title":"get_search_inventorytype","description":"inventorytype integer"},"title":"get_search_inventorytype","description":"inventorytype array"},"region":{"type":"array","maxItems":500,"items":{"type":"integer","format":"int32","title":"get_search_region","description":"region integer"},"title":"get_search_region","description":"region array"},"solarsystem":{"type":"array","maxItems":500,"items":{"type":"integer","format":"int32","title":"get_search_solarsystem","description":"solarsystem integer"},"title":"get_search_solarsystem","description":"solarsystem array"},"station":{"type":"array","maxItems":500,"items":{"type":"integer","format":"int32","title":"get_search_station","description":"station integer"},"title":"get_search_station","description":"station array"},"wormhole":{"type":"array","maxItems":500,"items":{"type":"integer","format":"int32","title":"get_search_wormhole","description":"wormhole integer"},"title":"get_search_wormhole","description":"wormhole array"}},"title":"get_search_ok","description":"200 ok object"},"headers":{"Content-Language":{"description":"The language used in the response","type":"string","enum":["de","en-us","fr","ja","ru","zh"]},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_search","x-cached-seconds":3600,"x-alternate-versions":["legacy","v1"]}},"/characters/{character_id}/skillqueue/":{"get":{"description":"List the configured skill queue for the given character\n\n---\nAlternate route: `/dev/characters/{character_id}/skillqueue/`\n\nAlternate route: `/legacy/characters/{character_id}/skillqueue/`\n\nAlternate route: `/v2/characters/{character_id}/skillqueue/`\n\n---\nThis route is cached for up to 120 seconds","summary":"Get character's skill queue","tags":["Skills"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"The current skill queue, sorted ascending by finishing time","examples":{"application/json":[{"skill_id":1,"finish_date":"2016-06-29T10:47:00Z","start_date":"2016-06-29T10:46:00Z","finished_level":3,"queue_position":0},{"skill_id":1,"finish_date":"2016-07-15T10:47:00Z","start_date":"2016-06-29T10:47:00Z","finished_level":4,"queue_position":1},{"skill_id":2,"finish_date":"2016-08-30T10:47:00Z","start_date":"2016-07-15T10:47:00Z","finished_level":2,"queue_position":2}]},"schema":{"type":"array","maxItems":51,"items":{"type":"object","required":["skill_id","finished_level","queue_position"],"properties":{"skill_id":{"type":"integer","format":"int32","title":"get_characters_character_id_skillqueue_skill_id","description":"skill_id integer"},"finish_date":{"type":"string","format":"date-time","title":"get_characters_character_id_skillqueue_finish_date","description":"finish_date string"},"start_date":{"type":"string","format":"date-time","title":"get_characters_character_id_skillqueue_start_date","description":"start_date string"},"finished_level":{"type":"integer","format":"int32","minimum":0,"maximum":5,"title":"get_characters_character_id_skillqueue_finished_level","description":"finished_level integer"},"queue_position":{"type":"integer","format":"int32","title":"get_characters_character_id_skillqueue_queue_position","description":"queue_position integer"},"training_start_sp":{"type":"integer","format":"int32","title":"get_characters_character_id_skillqueue_training_start_sp","description":"training_start_sp integer"},"level_end_sp":{"type":"integer","format":"int32","title":"get_characters_character_id_skillqueue_level_end_sp","description":"level_end_sp integer"},"level_start_sp":{"type":"integer","format":"int32","description":"Amount of SP that was in the skill when it started training it's current level. Used to calculate % of current level complete.","title":"get_characters_character_id_skillqueue_level_start_sp"}},"title":"get_characters_character_id_skillqueue_200_ok","description":"200 ok object"},"title":"get_characters_character_id_skillqueue_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-skills.read_skillqueue.v1"]}],"operationId":"get_characters_character_id_skillqueue","x-cached-seconds":120,"x-alternate-versions":["dev","legacy","v2"]}},"/characters/{character_id}/skills/":{"get":{"description":"List all trained skills for the given character\n\n---\nAlternate route: `/v3/characters/{character_id}/skills/`\n\n---\nThis route is cached for up to 120 seconds\n\n---\n[This route has an available update](https://esi.tech.ccp.is/diff/latest/dev/#GET-/characters/{character_id}/skills/)","summary":"Get character skills","tags":["Skills"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Known skills for the character","examples":{"application/json":{"skills":[{"skill_id":1,"skillpoints_in_skill":10000,"current_skill_level":1},{"skill_id":2,"skillpoints_in_skill":10000,"current_skill_level":1}],"total_sp":20000}},"schema":{"type":"object","properties":{"skills":{"type":"array","maxItems":1000,"items":{"type":"object","properties":{"skill_id":{"type":"integer","format":"int32","title":"get_characters_character_id_skills_skill_id","description":"skill_id integer"},"skillpoints_in_skill":{"type":"integer","format":"int64","title":"get_characters_character_id_skills_skillpoints_in_skill","description":"skillpoints_in_skill integer"},"current_skill_level":{"type":"integer","format":"int32","title":"get_characters_character_id_skills_current_skill_level","description":"current_skill_level integer"}},"title":"get_characters_character_id_skills_skill","description":"skill object"},"title":"get_characters_character_id_skills_skills","description":"skills array"},"total_sp":{"type":"integer","format":"int64","title":"get_characters_character_id_skills_total_sp","description":"total_sp integer"}},"title":"get_characters_character_id_skills_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-skills.read_skills.v1"]}],"operationId":"get_characters_character_id_skills","x-cached-seconds":120,"x-alternate-versions":["v3"]}},"/characters/{character_id}/attributes/":{"get":{"description":"Return attributes of a character\n\n---\nAlternate route: `/dev/characters/{character_id}/attributes/`\n\nAlternate route: `/legacy/characters/{character_id}/attributes/`\n\nAlternate route: `/v1/characters/{character_id}/attributes/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get character attributes","tags":["Skills"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Attributes of a character","examples":{"application/json":{"charisma":20,"intelligence":20,"memory":20,"perception":20,"willpower":20}},"schema":{"type":"object","required":["charisma","intelligence","memory","perception","willpower"],"properties":{"charisma":{"type":"integer","format":"int32","title":"get_characters_character_id_attributes_charisma","description":"charisma integer"},"intelligence":{"type":"integer","format":"int32","title":"get_characters_character_id_attributes_intelligence","description":"intelligence integer"},"memory":{"type":"integer","format":"int32","title":"get_characters_character_id_attributes_memory","description":"memory integer"},"perception":{"type":"integer","format":"int32","title":"get_characters_character_id_attributes_perception","description":"perception integer"},"willpower":{"type":"integer","format":"int32","title":"get_characters_character_id_attributes_willpower","description":"willpower integer"},"bonus_remaps":{"type":"integer","format":"int32","description":"Number of available bonus character neural remaps","title":"get_characters_character_id_attributes_bonus_remaps"},"last_remap_date":{"type":"string","format":"date-time","description":"Datetime of last neural remap, including usage of bonus remaps","title":"get_characters_character_id_attributes_last_remap_date"},"accrued_remap_cooldown_date":{"type":"string","format":"date-time","description":"Neural remapping cooldown after a character uses remap accrued over time","title":"get_characters_character_id_attributes_accrued_remap_cooldown_date"}},"title":"get_characters_character_id_attributes_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-skills.read_skills.v1"]}],"operationId":"get_characters_character_id_attributes","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/sovereignty/structures/":{"get":{"description":"Shows sovereignty data for structures.\n\n---\nAlternate route: `/dev/sovereignty/structures/`\n\nAlternate route: `/legacy/sovereignty/structures/`\n\nAlternate route: `/v1/sovereignty/structures/`\n\n---\nThis route is cached for up to 120 seconds","summary":"List sovereignty structures","tags":["Sovereignty"],"responses":{"200":{"description":"A list of sovereignty structures","examples":{"application/json":[{"alliance_id":498125261,"solar_system_id":30000570,"structure_id":1018253388776,"structure_type_id":32226,"vulnerability_occupancy_level":2,"vulnerable_start_time":"2016-10-28T20:30:00Z","vulnerable_end_time":"2016-10-29T05:30:00Z"}]},"schema":{"type":"array","maxItems":10000,"items":{"type":"object","required":["alliance_id","solar_system_id","structure_id","structure_type_id"],"properties":{"alliance_id":{"type":"integer","format":"int32","description":"The alliance that owns the structure.\n","title":"get_sovereignty_structures_alliance_id"},"solar_system_id":{"type":"integer","format":"int32","description":"Solar system in which the structure is located.\n","title":"get_sovereignty_structures_solar_system_id"},"structure_id":{"type":"integer","format":"int64","description":"Unique item ID for this structure.","title":"get_sovereignty_structures_structure_id"},"structure_type_id":{"type":"integer","format":"int32","description":"A reference to the type of structure this is.\n","title":"get_sovereignty_structures_structure_type_id"},"vulnerability_occupancy_level":{"type":"number","format":"float","description":"The occupancy level for the next or current vulnerability window. This takes into account all development indexes and capital system bonuses. Also known as Activity Defense Multiplier from in the client. It increases the time that attackers must spend using their entosis links on the structure.\n","title":"get_sovereignty_structures_vulnerability_occupancy_level"},"vulnerable_start_time":{"type":"string","format":"date-time","description":"The next time at which the structure will become vulnerable. Or the start time of the current window if current time is between this and vulnerableEndTime.\n","title":"get_sovereignty_structures_vulnerable_start_time"},"vulnerable_end_time":{"type":"string","format":"date-time","description":"The time at which the next or current vulnerability window ends. At the end of a vulnerability window the next window is recalculated and locked in along with the vulnerabilityOccupancyLevel. If the structure is not in 100% entosis control of the defender, it will go in to 'overtime' and stay vulnerable for as long as that situation persists. Only once the defenders have 100% entosis control and has the vulnerableEndTime passed does the vulnerability interval expire and a new one is calculated.\n","title":"get_sovereignty_structures_vulnerable_end_time"}},"title":"get_sovereignty_structures_200_ok","description":"200 ok object"},"title":"get_sovereignty_structures_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_sovereignty_structures","x-cached-seconds":120,"x-alternate-versions":["dev","legacy","v1"]}},"/sovereignty/campaigns/":{"get":{"description":"Shows sovereignty data for campaigns.\n\n---\nAlternate route: `/dev/sovereignty/campaigns/`\n\nAlternate route: `/legacy/sovereignty/campaigns/`\n\nAlternate route: `/v1/sovereignty/campaigns/`\n\n---\nThis route is cached for up to 5 seconds","summary":"List sovereignty campaigns","tags":["Sovereignty"],"responses":{"200":{"description":"A list of sovereignty campaigns","examples":{"application/json":[{"campaign_id":32833,"structure_id":61001096,"solar_system_id":30000856,"constellation_id":20000125,"event_type":"station_defense","start_time":"2016-10-29T14:34:40Z","defender_id":1695357456,"defender_score":0.6,"attackers_score":0.4}]},"schema":{"type":"array","maxItems":10000,"items":{"type":"object","required":["campaign_id","structure_id","solar_system_id","constellation_id","event_type","start_time"],"properties":{"campaign_id":{"type":"integer","format":"int32","description":"Unique ID for this campaign.","title":"get_sovereignty_campaigns_campaign_id"},"structure_id":{"type":"integer","format":"int64","description":"The structure item ID that is related to this campaign.\n","title":"get_sovereignty_campaigns_structure_id"},"solar_system_id":{"type":"integer","format":"int32","description":"The solar system the structure is located in.\n","title":"get_sovereignty_campaigns_solar_system_id"},"constellation_id":{"type":"integer","format":"int32","description":"The constellation in which the campaign will take place.\n","title":"get_sovereignty_campaigns_constellation_id"},"event_type":{"type":"string","description":"Type of event this campaign is for. tcu_defense, ihub_defense and station_defense are referred to as \"Defense Events\", station_freeport as \"Freeport Events\".\n","enum":["tcu_defense","ihub_defense","station_defense","station_freeport"],"title":"get_sovereignty_campaigns_event_type"},"start_time":{"type":"string","format":"date-time","description":"Time the event is scheduled to start.\n","title":"get_sovereignty_campaigns_start_time"},"defender_id":{"type":"integer","format":"int32","description":"Defending alliance, only present in Defense Events\n","title":"get_sovereignty_campaigns_defender_id"},"defender_score":{"type":"number","format":"float","description":"Score for the defending alliance, only present in Defense Events.\n","title":"get_sovereignty_campaigns_defender_score"},"attackers_score":{"type":"number","format":"float","description":"Score for all attacking parties, only present in Defense Events.\n","title":"get_sovereignty_campaigns_attackers_score"},"participants":{"type":"array","description":"Alliance participating and their respective scores, only present in Freeport Events.\n","maxItems":5000,"items":{"type":"object","required":["alliance_id","score"],"properties":{"alliance_id":{"type":"integer","format":"int32","title":"get_sovereignty_campaigns_alliance_id","description":"alliance_id integer"},"score":{"type":"number","format":"float","title":"get_sovereignty_campaigns_score","description":"score number"}},"title":"get_sovereignty_campaigns_participant","description":"participant object"},"title":"get_sovereignty_campaigns_participants"}},"title":"get_sovereignty_campaigns_200_ok","description":"200 ok object"},"title":"get_sovereignty_campaigns_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_sovereignty_campaigns","x-cached-seconds":5,"x-alternate-versions":["dev","legacy","v1"]}},"/sovereignty/map/":{"get":{"description":"Shows sovereignty information for solar systems\n\n---\nAlternate route: `/dev/sovereignty/map/`\n\nAlternate route: `/legacy/sovereignty/map/`\n\nAlternate route: `/v1/sovereignty/map/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"List sovereignty of systems","tags":["Sovereignty"],"responses":{"200":{"description":"A list of sovereignty information for solar systems in New Eden","examples":{"application/json":[{"system_id":30045334,"faction_id":500001}]},"schema":{"type":"array","maxItems":10000,"items":{"type":"object","required":["system_id"],"properties":{"system_id":{"type":"integer","format":"int32","title":"get_sovereignty_map_system_id","description":"system_id integer"},"alliance_id":{"type":"integer","format":"int32","title":"get_sovereignty_map_alliance_id","description":"alliance_id integer"},"corporation_id":{"type":"integer","format":"int32","title":"get_sovereignty_map_corporation_id","description":"corporation_id integer"},"faction_id":{"type":"integer","format":"int32","title":"get_sovereignty_map_faction_id","description":"faction_id integer"}},"title":"get_sovereignty_map_200_ok","description":"200 ok object"},"title":"get_sovereignty_map_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_sovereignty_map","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/status/":{"get":{"description":"EVE Server status\n\n---\nAlternate route: `/dev/status/`\n\nAlternate route: `/legacy/status/`\n\nAlternate route: `/v1/status/`\n\n---\nThis route is cached for up to 30 seconds","summary":"Retrieve the uptime and player counts","tags":["Status"],"responses":{"200":{"description":"Server status","examples":{"application/json":{"start_time":"2017-01-02T12:34:56Z","players":12345,"server_version":"1132976"}},"schema":{"type":"object","required":["start_time","players","server_version"],"properties":{"start_time":{"type":"string","format":"date-time","description":"Server start timestamp","title":"get_status_start_time"},"players":{"type":"integer","description":"Current online player count","title":"get_status_players"},"server_version":{"type":"string","description":"Running version as string","title":"get_status_server_version"},"vip":{"type":"boolean","description":"If the server is in VIP mode","title":"get_status_vip"}},"title":"get_status_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_status","x-cached-seconds":30,"x-alternate-versions":["dev","legacy","v1"]}},"/universe/planets/{planet_id}/":{"get":{"description":"Get information on a planet\n\n---\nAlternate route: `/dev/universe/planets/{planet_id}/`\n\nAlternate route: `/legacy/universe/planets/{planet_id}/`\n\nAlternate route: `/v1/universe/planets/{planet_id}/`\n\n---\nThis route expires daily at 11:05","summary":"Get planet information","tags":["Universe"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"planet_id","in":"path","required":true,"type":"integer","format":"int32","description":"planet_id integer"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Information about a planet","examples":{"application/json":{"planet_id":40000046,"name":"Akpivem III","type_id":13,"position":{"x":-189226344497,"y":9901605317,"z":-254852632979},"system_id":30000003}},"schema":{"type":"object","required":["planet_id","name","type_id","position","system_id"],"properties":{"planet_id":{"type":"integer","format":"int32","title":"get_universe_planets_planet_id_planet_id","description":"planet_id integer"},"name":{"type":"string","title":"get_universe_planets_planet_id_name","description":"name string"},"type_id":{"type":"integer","format":"int32","title":"get_universe_planets_planet_id_type_id","description":"type_id integer"},"position":{"type":"object","required":["x","y","z"],"properties":{"x":{"type":"number","format":"double","title":"get_universe_planets_planet_id_x","description":"x number"},"y":{"type":"number","format":"double","title":"get_universe_planets_planet_id_y","description":"y number"},"z":{"type":"number","format":"double","title":"get_universe_planets_planet_id_z","description":"z number"}},"title":"get_universe_planets_planet_id_position","description":"position object"},"system_id":{"type":"integer","format":"int32","description":"The solar system this planet is in","title":"get_universe_planets_planet_id_system_id"}},"title":"get_universe_planets_planet_id_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"Planet not found","schema":{"type":"object","title":"get_universe_planets_planet_id_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"get_universe_planets_planet_id_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_universe_planets_planet_id","x-alternate-versions":["dev","legacy","v1"]}},"/universe/stations/{station_id}/":{"get":{"description":"Get information on a station\n\n---\nAlternate route: `/dev/universe/stations/{station_id}/`\n\nAlternate route: `/v2/universe/stations/{station_id}/`\n\n---\nThis route is cached for up to 300 seconds","summary":"Get station information","tags":["Universe"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"station_id","in":"path","required":true,"type":"integer","format":"int32","description":"station_id integer"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Information about a station","examples":{"application/json":{"station_id":60000277,"name":"Jakanerva III - Moon 15 - Prompt Delivery Storage","type_id":1531,"position":{"x":165632286720,"y":2771804160,"z":-2455331266560},"system_id":30000148,"reprocessing_efficiency":0.5,"reprocessing_stations_take":0.05,"max_dockable_ship_volume":50000000,"office_rental_cost":10000,"services":["courier-missions","reprocessing-plant","market","repair-facilities","fitting","news","storage","insurance","docking","office-rental","loyalty-point-store","navy-offices"],"owner":1000003,"race_id":1}},"schema":{"type":"object","required":["station_id","name","type_id","position","system_id","reprocessing_efficiency","reprocessing_stations_take","max_dockable_ship_volume","office_rental_cost","services"],"properties":{"station_id":{"type":"integer","format":"int32","title":"get_universe_stations_station_id_station_id","description":"station_id integer"},"name":{"type":"string","title":"get_universe_stations_station_id_name","description":"name string"},"owner":{"type":"integer","format":"int32","description":"ID of the corporation that controls this station","title":"get_universe_stations_station_id_owner"},"type_id":{"type":"integer","format":"int32","title":"get_universe_stations_station_id_type_id","description":"type_id integer"},"race_id":{"type":"integer","format":"int32","title":"get_universe_stations_station_id_race_id","description":"race_id integer"},"position":{"type":"object","required":["x","y","z"],"properties":{"x":{"type":"number","format":"double","title":"get_universe_stations_station_id_x","description":"x number"},"y":{"type":"number","format":"double","title":"get_universe_stations_station_id_y","description":"y number"},"z":{"type":"number","format":"double","title":"get_universe_stations_station_id_z","description":"z number"}},"title":"get_universe_stations_station_id_position","description":"position object"},"system_id":{"type":"integer","format":"int32","description":"The solar system this station is in","title":"get_universe_stations_station_id_system_id"},"reprocessing_efficiency":{"type":"number","format":"float","title":"get_universe_stations_station_id_reprocessing_efficiency","description":"reprocessing_efficiency number"},"reprocessing_stations_take":{"type":"number","format":"float","title":"get_universe_stations_station_id_reprocessing_stations_take","description":"reprocessing_stations_take number"},"max_dockable_ship_volume":{"type":"number","format":"float","title":"get_universe_stations_station_id_max_dockable_ship_volume","description":"max_dockable_ship_volume number"},"office_rental_cost":{"type":"number","format":"float","title":"get_universe_stations_station_id_office_rental_cost","description":"office_rental_cost number"},"services":{"type":"array","maxItems":30,"items":{"type":"string","enum":["bounty-missions","assasination-missions","courier-missions","interbus","reprocessing-plant","refinery","market","black-market","stock-exchange","cloning","surgery","dna-therapy","repair-facilities","factory","labratory","gambling","fitting","paintshop","news","storage","insurance","docking","office-rental","jump-clone-facility","loyalty-point-store","navy-offices","security-offices"],"title":"get_universe_stations_station_id_service","description":"service string"},"title":"get_universe_stations_station_id_services","description":"services array"}},"title":"get_universe_stations_station_id_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"Station not found","schema":{"type":"object","title":"get_universe_stations_station_id_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"get_universe_stations_station_id_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_universe_stations_station_id","x-cached-seconds":300,"x-alternate-versions":["dev","v2"]}},"/universe/structures/{structure_id}/":{"get":{"description":"Returns information on requested structure, if you are on the ACL. Otherwise, returns \"Forbidden\" for all inputs.\n\n---\nAlternate route: `/dev/universe/structures/{structure_id}/`\n\nAlternate route: `/legacy/universe/structures/{structure_id}/`\n\nAlternate route: `/v1/universe/structures/{structure_id}/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get structure information","tags":["Universe"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"structure_id","in":"path","description":"An Eve structure ID","required":true,"type":"integer","format":"int64"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Data about a structure","examples":{"application/json":{"name":"V-3YG7 VI - The Capital","solar_system_id":30000142}},"schema":{"type":"object","required":["name","solar_system_id"],"properties":{"name":{"type":"string","description":"The full name of the structure","title":"get_universe_structures_structure_id_name"},"solar_system_id":{"type":"integer","format":"int32","title":"get_universe_structures_structure_id_solar_system_id","description":"solar_system_id integer"},"type_id":{"type":"integer","format":"int32","title":"get_universe_structures_structure_id_type_id","description":"type_id integer"},"position":{"type":"object","description":"Coordinates of the structure in Cartesian space relative to the Sun, in metres.\n","required":["x","y","z"],"properties":{"x":{"type":"number","format":"double","title":"get_universe_structures_structure_id_x","description":"x number"},"y":{"type":"number","format":"double","title":"get_universe_structures_structure_id_y","description":"y number"},"z":{"type":"number","format":"double","title":"get_universe_structures_structure_id_z","description":"z number"}},"title":"get_universe_structures_structure_id_position"}},"title":"get_universe_structures_structure_id_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"Structure not found","schema":{"type":"object","title":"get_universe_structures_structure_id_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"get_universe_structures_structure_id_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-universe.read_structures.v1"]}],"operationId":"get_universe_structures_structure_id","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/universe/systems/{system_id}/":{"get":{"description":"Get information on a solar system\n\n---\nAlternate route: `/dev/universe/systems/{system_id}/`\n\nAlternate route: `/v3/universe/systems/{system_id}/`\n\n---\nThis route expires daily at 11:05","summary":"Get solar system information","tags":["Universe"],"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/language"},{"name":"system_id","in":"path","required":true,"type":"integer","format":"int32","description":"system_id integer"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Information about a solar system","examples":{"application/json":{"system_id":30000003,"name":"Akpivem","position":{"x":-91174141133075340,"y":43938227486247170,"z":-56482824383339900},"security_status":0.8462923765182495,"constellation_id":20000001,"planets":[{"planet_id":40000041,"moons":[40000042]},{"planet_id":40000043}],"stargates":[50000342],"star_id":40000040,"security_class":"B"}},"schema":{"type":"object","required":["star_id","system_id","name","position","security_status","constellation_id","planets"],"properties":{"star_id":{"type":"integer","format":"int32","title":"get_universe_systems_system_id_star_id","description":"star_id integer"},"system_id":{"type":"integer","format":"int32","title":"get_universe_systems_system_id_system_id","description":"system_id integer"},"name":{"type":"string","title":"get_universe_systems_system_id_name","description":"name string"},"position":{"type":"object","required":["x","y","z"],"properties":{"x":{"type":"number","format":"double","title":"get_universe_systems_system_id_x","description":"x number"},"y":{"type":"number","format":"double","title":"get_universe_systems_system_id_y","description":"y number"},"z":{"type":"number","format":"double","title":"get_universe_systems_system_id_z","description":"z number"}},"title":"get_universe_systems_system_id_position","description":"position object"},"security_status":{"type":"number","format":"float","title":"get_universe_systems_system_id_security_status","description":"security_status number"},"security_class":{"type":"string","title":"get_universe_systems_system_id_security_class","description":"security_class string"},"constellation_id":{"type":"integer","format":"int32","description":"The constellation this solar system is in","title":"get_universe_systems_system_id_constellation_id"},"planets":{"type":"array","maxItems":1000,"items":{"type":"object","required":["planet_id"],"properties":{"planet_id":{"type":"integer","format":"int32","title":"get_universe_systems_system_id_planet_id","description":"planet_id integer"},"moons":{"type":"array","maxItems":1000,"items":{"type":"integer","format":"int32","title":"get_universe_systems_system_id_moon","description":"moon integer"},"title":"get_universe_systems_system_id_moons","description":"moons array"}},"title":"get_universe_systems_system_id_planet","description":"planet object"},"title":"get_universe_systems_system_id_planets","description":"planets array"},"stargates":{"type":"array","maxItems":25,"items":{"type":"integer","format":"int32","title":"get_universe_systems_system_id_stargate","description":"stargate integer"},"title":"get_universe_systems_system_id_stargates","description":"stargates array"},"stations":{"type":"array","maxItems":25,"items":{"type":"integer","format":"int32","title":"get_universe_systems_system_id_station","description":"station integer"},"title":"get_universe_systems_system_id_stations","description":"stations array"}},"title":"get_universe_systems_system_id_ok","description":"200 ok object"},"headers":{"Content-Language":{"description":"The language used in the response","type":"string","enum":["de","en-us","fr","ja","ru","zh"]},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"Solar system not found","schema":{"type":"object","title":"get_universe_systems_system_id_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"get_universe_systems_system_id_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_universe_systems_system_id","x-alternate-versions":["dev","v3"]}},"/universe/systems/":{"get":{"description":"Get a list of solar systems\n\n---\nAlternate route: `/dev/universe/systems/`\n\nAlternate route: `/legacy/universe/systems/`\n\nAlternate route: `/v1/universe/systems/`\n\n---\nThis route expires daily at 11:05","summary":"Get solar systems","tags":["Universe"],"responses":{"200":{"description":"A list of solar system ids","examples":{"application/json":[30000001,30000002]},"schema":{"type":"array","maxItems":10000,"items":{"type":"integer","format":"int32","title":"get_universe_systems_200_ok","description":"200 ok integer"},"title":"get_universe_systems_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_universe_systems","x-alternate-versions":["dev","legacy","v1"]}},"/universe/types/{type_id}/":{"get":{"description":"Get information on a type\n\n---\nAlternate route: `/dev/universe/types/{type_id}/`\n\nAlternate route: `/v3/universe/types/{type_id}/`\n\n---\nThis route expires daily at 11:05","summary":"Get type information","tags":["Universe"],"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/language"},{"name":"type_id","in":"path","description":"An Eve item type ID","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Information about a type","examples":{"application/json":{"type_id":587,"name":"Rifter","description":"The Rifter is a...","published":true,"group_id":25}},"schema":{"type":"object","required":["type_id","name","description","published","group_id"],"properties":{"type_id":{"type":"integer","format":"int32","title":"get_universe_types_type_id_type_id","description":"type_id integer"},"name":{"type":"string","title":"get_universe_types_type_id_name","description":"name string"},"description":{"type":"string","title":"get_universe_types_type_id_description","description":"description string"},"published":{"type":"boolean","title":"get_universe_types_type_id_published","description":"published boolean"},"group_id":{"type":"integer","format":"int32","title":"get_universe_types_type_id_group_id","description":"group_id integer"},"market_group_id":{"type":"integer","format":"int32","description":"This only exists for types that can be put on the market","title":"get_universe_types_type_id_market_group_id"},"radius":{"type":"number","format":"float","title":"get_universe_types_type_id_radius","description":"radius number"},"volume":{"type":"number","format":"float","title":"get_universe_types_type_id_volume","description":"volume number"},"packaged_volume":{"type":"number","format":"float","title":"get_universe_types_type_id_packaged_volume","description":"packaged_volume number"},"icon_id":{"type":"integer","format":"int32","title":"get_universe_types_type_id_icon_id","description":"icon_id integer"},"capacity":{"type":"number","format":"float","title":"get_universe_types_type_id_capacity","description":"capacity number"},"portion_size":{"type":"integer","format":"int32","title":"get_universe_types_type_id_portion_size","description":"portion_size integer"},"mass":{"type":"number","format":"float","title":"get_universe_types_type_id_mass","description":"mass number"},"graphic_id":{"type":"integer","format":"int32","title":"get_universe_types_type_id_graphic_id","description":"graphic_id integer"},"dogma_attributes":{"type":"array","maxItems":1000,"items":{"type":"object","required":["attribute_id","value"],"properties":{"attribute_id":{"type":"integer","format":"int32","title":"get_universe_types_type_id_attribute_id","description":"attribute_id integer"},"value":{"type":"number","format":"float","title":"get_universe_types_type_id_value","description":"value number"}},"title":"get_universe_types_type_id_dogma_attribute","description":"dogma_attribute object"},"title":"get_universe_types_type_id_dogma_attributes","description":"dogma_attributes array"},"dogma_effects":{"type":"array","maxItems":1000,"items":{"type":"object","required":["effect_id","is_default"],"properties":{"effect_id":{"type":"integer","format":"int32","title":"get_universe_types_type_id_effect_id","description":"effect_id integer"},"is_default":{"type":"boolean","title":"get_universe_types_type_id_is_default","description":"is_default boolean"}},"title":"get_universe_types_type_id_dogma_effect","description":"dogma_effect object"},"title":"get_universe_types_type_id_dogma_effects","description":"dogma_effects array"}},"title":"get_universe_types_type_id_ok","description":"200 ok object"},"headers":{"Content-Language":{"description":"The language used in the response","type":"string","enum":["de","en-us","fr","ja","ru","zh"]},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"Type not found","schema":{"type":"object","title":"get_universe_types_type_id_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"get_universe_types_type_id_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_universe_types_type_id","x-alternate-versions":["dev","v3"]}},"/universe/types/":{"get":{"description":"Get a list of type ids\n\n---\nAlternate route: `/dev/universe/types/`\n\nAlternate route: `/legacy/universe/types/`\n\nAlternate route: `/v1/universe/types/`\n\n---\nThis route expires daily at 11:05","summary":"Get types","tags":["Universe"],"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of type ids","examples":{"application/json":[1,2,3]},"schema":{"type":"array","maxItems":1000,"items":{"type":"integer","format":"int32","title":"get_universe_types_200_ok","description":"200 ok integer"},"title":"get_universe_types_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_universe_types","x-alternate-versions":["dev","legacy","v1"]}},"/universe/groups/":{"get":{"description":"Get a list of item groups\n\n---\nAlternate route: `/dev/universe/groups/`\n\nAlternate route: `/legacy/universe/groups/`\n\nAlternate route: `/v1/universe/groups/`\n\n---\nThis route expires daily at 11:05","summary":"Get item groups","tags":["Universe"],"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of item group ids","examples":{"application/json":[1,2,3]},"schema":{"type":"array","maxItems":1000,"items":{"type":"integer","format":"int32","title":"get_universe_groups_200_ok","description":"200 ok integer"},"title":"get_universe_groups_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_universe_groups","x-alternate-versions":["dev","legacy","v1"]}},"/universe/groups/{group_id}/":{"get":{"description":"Get information on an item group\n\n---\nAlternate route: `/dev/universe/groups/{group_id}/`\n\nAlternate route: `/legacy/universe/groups/{group_id}/`\n\nAlternate route: `/v1/universe/groups/{group_id}/`\n\n---\nThis route expires daily at 11:05","summary":"Get item group information","tags":["Universe"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"group_id","in":"path","description":"An Eve item group ID","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/language"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Information about an item group","examples":{"application/json":{"group_id":25,"name":"Frigate","published":true,"category_id":6,"types":[587,586,585]}},"schema":{"type":"object","required":["group_id","name","published","category_id","types"],"properties":{"group_id":{"type":"integer","format":"int32","title":"get_universe_groups_group_id_group_id","description":"group_id integer"},"name":{"type":"string","title":"get_universe_groups_group_id_name","description":"name string"},"published":{"type":"boolean","title":"get_universe_groups_group_id_published","description":"published boolean"},"category_id":{"type":"integer","format":"int32","title":"get_universe_groups_group_id_category_id","description":"category_id integer"},"types":{"type":"array","maxItems":10000,"items":{"type":"integer","format":"int32","title":"get_universe_groups_group_id_type","description":"type integer"},"title":"get_universe_groups_group_id_types","description":"types array"}},"title":"get_universe_groups_group_id_ok","description":"200 ok object"},"headers":{"Content-Language":{"description":"The language used in the response","type":"string","enum":["de","en-us","fr","ja","ru","zh"]},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"Group not found","schema":{"type":"object","title":"get_universe_groups_group_id_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"get_universe_groups_group_id_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_universe_groups_group_id","x-alternate-versions":["dev","legacy","v1"]}},"/universe/categories/":{"get":{"description":"Get a list of item categories\n\n---\nAlternate route: `/dev/universe/categories/`\n\nAlternate route: `/legacy/universe/categories/`\n\nAlternate route: `/v1/universe/categories/`\n\n---\nThis route expires daily at 11:05","summary":"Get item categories","tags":["Universe"],"responses":{"200":{"description":"A list of item category ids","examples":{"application/json":[1,2,3]},"schema":{"type":"array","maxItems":10000,"items":{"type":"integer","format":"int32","title":"get_universe_categories_200_ok","description":"200 ok integer"},"title":"get_universe_categories_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_universe_categories","x-alternate-versions":["dev","legacy","v1"]}},"/universe/categories/{category_id}/":{"get":{"description":"Get information of an item category\n\n---\nAlternate route: `/dev/universe/categories/{category_id}/`\n\nAlternate route: `/legacy/universe/categories/{category_id}/`\n\nAlternate route: `/v1/universe/categories/{category_id}/`\n\n---\nThis route expires daily at 11:05","summary":"Get item category information","tags":["Universe"],"parameters":[{"name":"category_id","in":"path","description":"An Eve item category ID","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/language"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Information about an item category","examples":{"application/json":{"category_id":6,"name":"Ship","published":true,"groups":[25,26,27]}},"schema":{"type":"object","required":["category_id","name","published","groups"],"properties":{"category_id":{"type":"integer","format":"int32","title":"get_universe_categories_category_id_category_id","description":"category_id integer"},"name":{"type":"string","title":"get_universe_categories_category_id_name","description":"name string"},"published":{"type":"boolean","title":"get_universe_categories_category_id_published","description":"published boolean"},"groups":{"type":"array","maxItems":10000,"items":{"type":"integer","format":"int32","title":"get_universe_categories_category_id_group","description":"group integer"},"title":"get_universe_categories_category_id_groups","description":"groups array"}},"title":"get_universe_categories_category_id_ok","description":"200 ok object"},"headers":{"Content-Language":{"description":"The language used in the response","type":"string","enum":["de","en-us","fr","ja","ru","zh"]},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"Category not found","schema":{"type":"object","title":"get_universe_categories_category_id_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"get_universe_categories_category_id_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_universe_categories_category_id","x-alternate-versions":["dev","legacy","v1"]}},"/universe/names/":{"post":{"description":"Resolve a set of IDs to names and categories. Supported ID's for resolving are: Characters, Corporations, Alliances, Stations, Solar Systems, Constellations, Regions, Types.\n\n---\nAlternate route: `/dev/universe/names/`\n\nAlternate route: `/v2/universe/names/`\n","summary":"Get names and categories for a set of ID's","tags":["Universe"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"ids","in":"body","description":"The ids to resolve","required":true,"schema":{"type":"array","minItems":1,"maxItems":1000,"uniqueItems":true,"items":{"type":"integer","format":"int32","title":"post_universe_names_id","description":"id integer"},"example":[95465499,30000142],"title":"post_universe_names_ids","description":"ids array"}},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"List of id/name associations for a set of ID's. All ID's must resolve to a name, or nothing will be returned.","examples":{"application/json":[{"id":95465499,"name":"CCP Bartender","category":"character"},{"id":30000142,"name":"Jita","category":"solar_system"}]},"schema":{"type":"array","maxItems":1000,"items":{"type":"object","required":["id","name","category"],"properties":{"id":{"type":"integer","format":"int32","title":"post_universe_names_id","description":"id integer"},"name":{"type":"string","title":"post_universe_names_name","description":"name string"},"category":{"type":"string","enum":["alliance","character","constellation","corporation","inventory_type","region","solar_system","station"],"title":"post_universe_names_category","description":"category string"}},"title":"post_universe_names_200_ok","description":"200 ok object"},"title":"post_universe_names_ok","description":"200 ok array"}},"404":{"description":"Ensure all IDs are valid before resolving.","schema":{"type":"object","title":"post_universe_names_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"post_universe_names_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"post_universe_names","x-alternate-versions":["dev","v2"]}},"/universe/structures/":{"get":{"description":"List all public structures\n\n---\nAlternate route: `/dev/universe/structures/`\n\nAlternate route: `/legacy/universe/structures/`\n\nAlternate route: `/v1/universe/structures/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"List all public structures","tags":["Universe"],"responses":{"200":{"description":"List of public structure IDs","examples":{"application/json":[1020988381992,1020988381991]},"schema":{"type":"array","maxItems":10000,"uniqueItems":true,"items":{"type":"integer","format":"int64","minimum":0,"title":"get_universe_structures_200_ok","description":"200 ok integer"},"title":"get_universe_structures_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_universe_structures","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/universe/races/":{"get":{"description":"Get a list of character races\n\n---\nAlternate route: `/dev/universe/races/`\n\nAlternate route: `/legacy/universe/races/`\n\nAlternate route: `/v1/universe/races/`\n\n---\nThis route expires daily at 11:05","summary":"Get character races","tags":["Universe"],"responses":{"200":{"description":"A list of character races","examples":{"application/json":[{"race_id":1,"name":"Caldari","description":"Founded on the tenets of patriotism and hard work...","alliance_id":500001}]},"schema":{"type":"array","maxItems":4,"items":{"type":"object","required":["race_id","name","description","alliance_id"],"properties":{"race_id":{"type":"integer","format":"int32","title":"get_universe_races_race_id","description":"race_id integer"},"name":{"type":"string","title":"get_universe_races_name","description":"name string"},"description":{"type":"string","title":"get_universe_races_description","description":"description string"},"alliance_id":{"type":"integer","format":"int32","description":"The alliance generally associated with this race","title":"get_universe_races_alliance_id"}},"title":"get_universe_races_200_ok","description":"200 ok object"},"title":"get_universe_races_ok","description":"200 ok array"},"headers":{"Content-Language":{"description":"The language used in the response","type":"string","enum":["de","en-us","fr","ja","ru","zh"]},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/language"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_universe_races","x-alternate-versions":["dev","legacy","v1"]}},"/universe/factions/":{"get":{"description":"Get a list of factions\n\n---\nAlternate route: `/dev/universe/factions/`\n\nAlternate route: `/v2/universe/factions/`\n\n---\nThis route expires daily at 11:05","summary":"Get factions","tags":["Universe"],"responses":{"200":{"description":"A list of factions","examples":{"application/json":[{"faction_id":1,"name":"Faction","description":"blah blah","solar_system_id":123,"corporation_id":456,"size_factor":1.0,"station_count":1000,"station_system_count":100,"is_unique":true}]},"schema":{"type":"array","maxItems":10000,"items":{"type":"object","required":["faction_id","name","description","size_factor","station_count","station_system_count","is_unique"],"properties":{"faction_id":{"type":"integer","format":"int32","title":"get_universe_factions_faction_id","description":"faction_id integer"},"name":{"type":"string","title":"get_universe_factions_name","description":"name string"},"description":{"type":"string","title":"get_universe_factions_description","description":"description string"},"solar_system_id":{"type":"integer","format":"int32","title":"get_universe_factions_solar_system_id","description":"solar_system_id integer"},"corporation_id":{"type":"integer","format":"int32","title":"get_universe_factions_corporation_id","description":"corporation_id integer"},"militia_corporation_id":{"type":"integer","format":"int32","title":"get_universe_factions_militia_corporation_id","description":"militia_corporation_id integer"},"size_factor":{"type":"number","format":"float","title":"get_universe_factions_size_factor","description":"size_factor number"},"station_count":{"type":"integer","format":"int32","title":"get_universe_factions_station_count","description":"station_count integer"},"station_system_count":{"type":"integer","format":"int32","title":"get_universe_factions_station_system_count","description":"station_system_count integer"},"is_unique":{"type":"boolean","title":"get_universe_factions_is_unique","description":"is_unique boolean"}},"title":"get_universe_factions_200_ok","description":"200 ok object"},"title":"get_universe_factions_ok","description":"200 ok array"},"headers":{"Content-Language":{"description":"The language used in the response","type":"string","enum":["de","en-us","fr","ja","ru","zh"]},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/language"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_universe_factions","x-alternate-versions":["dev","v2"]}},"/universe/bloodlines/":{"get":{"description":"Get a list of bloodlines\n\n---\nAlternate route: `/dev/universe/bloodlines/`\n\nAlternate route: `/legacy/universe/bloodlines/`\n\nAlternate route: `/v1/universe/bloodlines/`\n\n---\nThis route expires daily at 11:05","summary":"Get bloodlines","tags":["Universe"],"responses":{"200":{"description":"A list of bloodlines","examples":{"application/json":[{"bloodline_id":1,"name":"Deteis","description":"The Deteis are regarded as ...","race_id":1,"ship_type_id":601,"corporation_id":1000006,"perception":5,"willpower":5,"charisma":6,"memory":7,"intelligence":7}]},"schema":{"type":"array","maxItems":100,"items":{"type":"object","required":["bloodline_id","name","description","race_id","ship_type_id","corporation_id","perception","willpower","charisma","memory","intelligence"],"properties":{"bloodline_id":{"type":"integer","format":"int32","title":"get_universe_bloodlines_bloodline_id","description":"bloodline_id integer"},"name":{"type":"string","title":"get_universe_bloodlines_name","description":"name string"},"description":{"type":"string","title":"get_universe_bloodlines_description","description":"description string"},"race_id":{"type":"integer","format":"int32","title":"get_universe_bloodlines_race_id","description":"race_id integer"},"ship_type_id":{"type":"integer","format":"int32","title":"get_universe_bloodlines_ship_type_id","description":"ship_type_id integer"},"corporation_id":{"type":"integer","format":"int32","title":"get_universe_bloodlines_corporation_id","description":"corporation_id integer"},"perception":{"type":"integer","format":"int32","title":"get_universe_bloodlines_perception","description":"perception integer"},"willpower":{"type":"integer","format":"int32","title":"get_universe_bloodlines_willpower","description":"willpower integer"},"charisma":{"type":"integer","format":"int32","title":"get_universe_bloodlines_charisma","description":"charisma integer"},"memory":{"type":"integer","format":"int32","title":"get_universe_bloodlines_memory","description":"memory integer"},"intelligence":{"type":"integer","format":"int32","title":"get_universe_bloodlines_intelligence","description":"intelligence integer"}},"title":"get_universe_bloodlines_200_ok","description":"200 ok object"},"title":"get_universe_bloodlines_ok","description":"200 ok array"},"headers":{"Content-Language":{"description":"The language used in the response","type":"string","enum":["de","en-us","fr","ja","ru","zh"]},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/language"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_universe_bloodlines","x-alternate-versions":["dev","legacy","v1"]}},"/universe/regions/":{"get":{"description":"Get a list of regions\n\n---\nAlternate route: `/dev/universe/regions/`\n\nAlternate route: `/legacy/universe/regions/`\n\nAlternate route: `/v1/universe/regions/`\n\n---\nThis route expires daily at 11:05","summary":"Get regions","tags":["Universe"],"responses":{"200":{"description":"A list of region ids","examples":{"application/json":[11000001,11000002]},"schema":{"type":"array","maxItems":1000,"items":{"type":"integer","format":"int32","title":"get_universe_regions_200_ok","description":"200 ok integer"},"title":"get_universe_regions_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_universe_regions","x-alternate-versions":["dev","legacy","v1"]}},"/universe/regions/{region_id}/":{"get":{"description":"Get information on a region\n\n---\nAlternate route: `/dev/universe/regions/{region_id}/`\n\nAlternate route: `/legacy/universe/regions/{region_id}/`\n\nAlternate route: `/v1/universe/regions/{region_id}/`\n\n---\nThis route expires daily at 11:05","summary":"Get region information","tags":["Universe"],"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/language"},{"name":"region_id","in":"path","required":true,"type":"integer","format":"int32","description":"region_id integer"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Information about a region","examples":{"application/json":{"region_id":10000042,"name":"Metropolis","description":"It has long been an established fact of civilization...","constellations":[20000302,20000303]}},"schema":{"type":"object","required":["region_id","name","constellations"],"properties":{"region_id":{"type":"integer","format":"int32","title":"get_universe_regions_region_id_region_id","description":"region_id integer"},"name":{"type":"string","title":"get_universe_regions_region_id_name","description":"name string"},"description":{"type":"string","title":"get_universe_regions_region_id_description","description":"description string"},"constellations":{"type":"array","maxItems":1000,"items":{"type":"integer","format":"int32","title":"get_universe_regions_region_id_constellation","description":"constellation integer"},"title":"get_universe_regions_region_id_constellations","description":"constellations array"}},"title":"get_universe_regions_region_id_ok","description":"200 ok object"},"headers":{"Content-Language":{"description":"The language used in the response","type":"string","enum":["de","en-us","fr","ja","ru","zh"]},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"Region not found","schema":{"type":"object","title":"get_universe_regions_region_id_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"get_universe_regions_region_id_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_universe_regions_region_id","x-alternate-versions":["dev","legacy","v1"]}},"/universe/constellations/":{"get":{"description":"Get a list of constellations\n\n---\nAlternate route: `/dev/universe/constellations/`\n\nAlternate route: `/legacy/universe/constellations/`\n\nAlternate route: `/v1/universe/constellations/`\n\n---\nThis route expires daily at 11:05","summary":"Get constellations","tags":["Universe"],"responses":{"200":{"description":"A list of constellation ids","examples":{"application/json":[20000001,20000002]},"schema":{"type":"array","maxItems":10000,"items":{"type":"integer","format":"int32","title":"get_universe_constellations_200_ok","description":"200 ok integer"},"title":"get_universe_constellations_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_universe_constellations","x-alternate-versions":["dev","legacy","v1"]}},"/universe/constellations/{constellation_id}/":{"get":{"description":"Get information on a constellation\n\n---\nAlternate route: `/dev/universe/constellations/{constellation_id}/`\n\nAlternate route: `/legacy/universe/constellations/{constellation_id}/`\n\nAlternate route: `/v1/universe/constellations/{constellation_id}/`\n\n---\nThis route expires daily at 11:05","summary":"Get constellation information","tags":["Universe"],"parameters":[{"name":"constellation_id","in":"path","required":true,"type":"integer","format":"int32","description":"constellation_id integer"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/language"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Information about a constellation","examples":{"application/json":{"constellation_id":20000009,"name":"Mekashtad","position":{"x":67796138757472320,"y":-70591121348560960,"z":-59587016159270070},"region_id":10000001,"systems":[20000302,20000303]}},"schema":{"type":"object","required":["constellation_id","name","position","region_id","systems"],"properties":{"constellation_id":{"type":"integer","format":"int32","title":"get_universe_constellations_constellation_id_constellation_id","description":"constellation_id integer"},"name":{"type":"string","title":"get_universe_constellations_constellation_id_name","description":"name string"},"position":{"type":"object","required":["x","y","z"],"properties":{"x":{"type":"number","format":"double","title":"get_universe_constellations_constellation_id_x","description":"x number"},"y":{"type":"number","format":"double","title":"get_universe_constellations_constellation_id_y","description":"y number"},"z":{"type":"number","format":"double","title":"get_universe_constellations_constellation_id_z","description":"z number"}},"title":"get_universe_constellations_constellation_id_position","description":"position object"},"region_id":{"type":"integer","format":"int32","description":"The region this constellation is in","title":"get_universe_constellations_constellation_id_region_id"},"systems":{"type":"array","maxItems":10000,"items":{"type":"integer","format":"int32","title":"get_universe_constellations_constellation_id_system","description":"system integer"},"title":"get_universe_constellations_constellation_id_systems","description":"systems array"}},"title":"get_universe_constellations_constellation_id_ok","description":"200 ok object"},"headers":{"Content-Language":{"description":"The language used in the response","type":"string","enum":["de","en-us","fr","ja","ru","zh"]},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"Constellation not found","schema":{"type":"object","title":"get_universe_constellations_constellation_id_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"get_universe_constellations_constellation_id_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_universe_constellations_constellation_id","x-alternate-versions":["dev","legacy","v1"]}},"/universe/moons/{moon_id}/":{"get":{"description":"Get information on a moon\n\n---\nAlternate route: `/dev/universe/moons/{moon_id}/`\n\nAlternate route: `/legacy/universe/moons/{moon_id}/`\n\nAlternate route: `/v1/universe/moons/{moon_id}/`\n\n---\nThis route expires daily at 11:05","summary":"Get moon information","tags":["Universe"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"moon_id","in":"path","required":true,"type":"integer","format":"int32","description":"moon_id integer"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Information about a moon","examples":{"application/json":{"moon_id":40000042,"name":"Akpivem I - Moon 1","position":{"x":58605102008,"y":-3066616285,"z":-55193617920},"system_id":30000003}},"schema":{"type":"object","required":["moon_id","name","position","system_id"],"properties":{"moon_id":{"type":"integer","format":"int32","title":"get_universe_moons_moon_id_moon_id","description":"moon_id integer"},"name":{"type":"string","title":"get_universe_moons_moon_id_name","description":"name string"},"position":{"type":"object","required":["x","y","z"],"properties":{"x":{"type":"number","format":"double","title":"get_universe_moons_moon_id_x","description":"x number"},"y":{"type":"number","format":"double","title":"get_universe_moons_moon_id_y","description":"y number"},"z":{"type":"number","format":"double","title":"get_universe_moons_moon_id_z","description":"z number"}},"title":"get_universe_moons_moon_id_position","description":"position object"},"system_id":{"type":"integer","format":"int32","description":"The solar system this moon is in","title":"get_universe_moons_moon_id_system_id"}},"title":"get_universe_moons_moon_id_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"Moon not found","schema":{"type":"object","title":"get_universe_moons_moon_id_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"get_universe_moons_moon_id_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_universe_moons_moon_id","x-alternate-versions":["dev","legacy","v1"]}},"/universe/stargates/{stargate_id}/":{"get":{"description":"Get information on a stargate\n\n---\nAlternate route: `/dev/universe/stargates/{stargate_id}/`\n\nAlternate route: `/legacy/universe/stargates/{stargate_id}/`\n\nAlternate route: `/v1/universe/stargates/{stargate_id}/`\n\n---\nThis route expires daily at 11:05","summary":"Get stargate information","tags":["Universe"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"stargate_id","in":"path","required":true,"type":"integer","format":"int32","description":"stargate_id integer"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Information about a stargate","examples":{"application/json":{"stargate_id":50000342,"name":"Stargate (Tanoo)","type_id":29624,"position":{"x":-101092761600,"y":5279539200,"z":1550503403520},"system_id":30000003,"destination":{"system_id":30000001,"stargate_id":50000056}}},"schema":{"type":"object","required":["stargate_id","name","type_id","position","system_id","destination"],"properties":{"stargate_id":{"type":"integer","format":"int32","title":"get_universe_stargates_stargate_id_stargate_id","description":"stargate_id integer"},"name":{"type":"string","title":"get_universe_stargates_stargate_id_name","description":"name string"},"type_id":{"type":"integer","format":"int32","title":"get_universe_stargates_stargate_id_type_id","description":"type_id integer"},"position":{"type":"object","required":["x","y","z"],"properties":{"x":{"type":"number","format":"double","title":"get_universe_stargates_stargate_id_x","description":"x number"},"y":{"type":"number","format":"double","title":"get_universe_stargates_stargate_id_y","description":"y number"},"z":{"type":"number","format":"double","title":"get_universe_stargates_stargate_id_z","description":"z number"}},"title":"get_universe_stargates_stargate_id_position","description":"position object"},"system_id":{"type":"integer","format":"int32","description":"The solar system this stargate is in","title":"get_universe_stargates_stargate_id_system_id"},"destination":{"type":"object","required":["system_id","stargate_id"],"properties":{"system_id":{"type":"integer","format":"int32","description":"The solar system this stargate connects to","title":"get_universe_stargates_stargate_id_system_id"},"stargate_id":{"type":"integer","format":"int32","description":"The stargate this stargate connects to","title":"get_universe_stargates_stargate_id_stargate_id"}},"title":"get_universe_stargates_stargate_id_destination","description":"destination object"}},"title":"get_universe_stargates_stargate_id_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"Stargate not found","schema":{"type":"object","title":"get_universe_stargates_stargate_id_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"get_universe_stargates_stargate_id_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_universe_stargates_stargate_id","x-alternate-versions":["dev","legacy","v1"]}},"/universe/graphics/":{"get":{"description":"Get a list of graphics\n\n---\nAlternate route: `/dev/universe/graphics/`\n\nAlternate route: `/legacy/universe/graphics/`\n\nAlternate route: `/v1/universe/graphics/`\n\n---\nThis route expires daily at 11:05","summary":"Get graphics","tags":["Universe"],"responses":{"200":{"description":"A list of graphic ids","examples":{"application/json":[10,4106]},"schema":{"type":"array","maxItems":10000,"items":{"type":"integer","format":"int32","title":"get_universe_graphics_200_ok","description":"200 ok integer"},"title":"get_universe_graphics_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_universe_graphics","x-alternate-versions":["dev","legacy","v1"]}},"/universe/graphics/{graphic_id}/":{"get":{"description":"Get information on a graphic\n\n---\nAlternate route: `/dev/universe/graphics/{graphic_id}/`\n\nAlternate route: `/legacy/universe/graphics/{graphic_id}/`\n\nAlternate route: `/v1/universe/graphics/{graphic_id}/`\n\n---\nThis route expires daily at 11:05","summary":"Get graphic information","tags":["Universe"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"graphic_id","in":"path","required":true,"type":"integer","format":"int32","description":"graphic_id integer"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Information about a graphic","examples":{"application/json":{"graphic_id":10,"graphic_file":"res:/dx9/model/worldobject/planet/moon.red"}},"schema":{"type":"object","required":["graphic_id"],"properties":{"graphic_id":{"type":"integer","format":"int32","title":"get_universe_graphics_graphic_id_graphic_id","description":"graphic_id integer"},"graphic_file":{"type":"string","title":"get_universe_graphics_graphic_id_graphic_file","description":"graphic_file string"},"sof_race_name":{"type":"string","title":"get_universe_graphics_graphic_id_sof_race_name","description":"sof_race_name string"},"sof_fation_name":{"type":"string","title":"get_universe_graphics_graphic_id_sof_fation_name","description":"sof_fation_name string"},"sof_dna":{"type":"string","title":"get_universe_graphics_graphic_id_sof_dna","description":"sof_dna string"},"sof_hull_name":{"type":"string","title":"get_universe_graphics_graphic_id_sof_hull_name","description":"sof_hull_name string"},"collision_file":{"type":"string","title":"get_universe_graphics_graphic_id_collision_file","description":"collision_file string"},"icon_folder":{"type":"string","title":"get_universe_graphics_graphic_id_icon_folder","description":"icon_folder string"}},"title":"get_universe_graphics_graphic_id_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"404":{"description":"Graphic not found","schema":{"type":"object","title":"get_universe_graphics_graphic_id_not_found","description":"Not found","properties":{"error":{"type":"string","description":"Not found message","title":"get_universe_graphics_graphic_id_404_not_found"}}},"examples":{"application/json":{"error":"Not found message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_universe_graphics_graphic_id","x-alternate-versions":["dev","legacy","v1"]}},"/universe/system_jumps/":{"get":{"description":"Get the number of jumps in solar systems within the last hour ending at the timestamp of the Last-Modified header, excluding wormhole space. Only systems with jumps will be listed\n\n---\nAlternate route: `/dev/universe/system_jumps/`\n\nAlternate route: `/legacy/universe/system_jumps/`\n\nAlternate route: `/v1/universe/system_jumps/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get system jumps","tags":["Universe"],"responses":{"200":{"description":"A list of systems and number of jumps","examples":{"application/json":[{"system_id":30002410,"ship_jumps":42}]},"schema":{"type":"array","maxItems":10000,"items":{"type":"object","required":["system_id","ship_jumps"],"properties":{"system_id":{"type":"integer","format":"int32","title":"get_universe_system_jumps_system_id","description":"system_id integer"},"ship_jumps":{"type":"integer","format":"int32","title":"get_universe_system_jumps_ship_jumps","description":"ship_jumps integer"}},"title":"get_universe_system_jumps_200_ok","description":"200 ok object"},"title":"get_universe_system_jumps_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_universe_system_jumps","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/universe/system_kills/":{"get":{"description":"Get the number of ship, pod and NPC kills per solar system within the last hour ending at the timestamp of the Last-Modified header, excluding wormhole space. Only systems with kills will be listed\n\n---\nAlternate route: `/dev/universe/system_kills/`\n\nAlternate route: `/v2/universe/system_kills/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get system kills","tags":["Universe"],"responses":{"200":{"description":"A list of systems and number of ship, pod and NPC kills","examples":{"application/json":[{"system_id":30002410,"ship_kills":42,"npc_kills":0,"pod_kills":24}]},"schema":{"type":"array","maxItems":10000,"items":{"type":"object","required":["system_id","ship_kills","npc_kills","pod_kills"],"properties":{"system_id":{"type":"integer","format":"int32","title":"get_universe_system_kills_system_id","description":"system_id integer"},"ship_kills":{"type":"integer","format":"int32","description":"Number of player ships killed in this system","title":"get_universe_system_kills_ship_kills"},"npc_kills":{"type":"integer","format":"int32","description":"Number of NPC ships killed in this system","title":"get_universe_system_kills_npc_kills"},"pod_kills":{"type":"integer","format":"int32","description":"Number of pods killed in this system","title":"get_universe_system_kills_pod_kills"}},"title":"get_universe_system_kills_200_ok","description":"200 ok object"},"title":"get_universe_system_kills_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"operationId":"get_universe_system_kills","x-cached-seconds":3600,"x-alternate-versions":["dev","v2"]}},"/universe/stars/{star_id}/":{"get":{"description":"Get information on a star\n\n---\nAlternate route: `/dev/universe/stars/{star_id}/`\n\nAlternate route: `/legacy/universe/stars/{star_id}/`\n\nAlternate route: `/v1/universe/stars/{star_id}/`\n\n---\nThis route expires daily at 11:05","summary":"Get star information","tags":["Universe"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"star_id","in":"path","required":true,"type":"integer","format":"int32","description":"star_id integer"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Information about a star","examples":{"application/json":{"type_id":45033,"age":9398686722,"name":"BKG-Q2 - Star","luminosity":0.06615000218153,"radius":346600000,"spectral_class":"K2 V","temperature":3953,"solar_system_id":30004333}},"schema":{"type":"object","required":["name","type_id","age","luminosity","radius","spectral_class","temperature","solar_system_id"],"properties":{"name":{"type":"string","title":"get_universe_stars_star_id_name","description":"name string"},"solar_system_id":{"type":"integer","format":"int32","title":"get_universe_stars_star_id_solar_system_id","description":"solar_system_id integer"},"type_id":{"type":"integer","format":"int32","title":"get_universe_stars_star_id_type_id","description":"type_id integer"},"age":{"type":"integer","format":"int64","description":"Age of star in years","title":"get_universe_stars_star_id_age"},"luminosity":{"type":"number","format":"float","title":"get_universe_stars_star_id_luminosity","description":"luminosity number"},"radius":{"type":"integer","format":"int64","title":"get_universe_stars_star_id_radius","description":"radius integer"},"spectral_class":{"type":"string","enum":["K2 V","K4 V","G2 V","G8 V","M7 V","K7 V","M2 V","K5 V","M3 V","G0 V","G7 V","G3 V","F9 V","G5 V","F6 V","K8 V","K9 V","K6 V","G9 V","G6 V","G4 VI","G4 V","F8 V","F2 V","F1 V","K3 V","F0 VI","G1 VI","G0 VI","K1 V","M4 V","M1 V","M6 V","M0 V","K2 IV","G2 VI","K0 V","K5 IV","F5 VI","G6 VI","F6 VI","F2 IV","G3 VI","M8 V","F1 VI","K1 IV","F7 V","G5 VI","M5 V","G7 VI","F5 V","F4 VI","F8 VI","K3 IV","F4 IV","F0 V","G7 IV","G8 VI","F2 VI","F4 V","F7 VI","F3 V","G1 V","G9 VI","F3 IV","F9 VI","M9 V","K0 IV","F1 IV","G4 IV","F3 VI","K4 IV","G5 IV","G3 IV","G1 IV","K7 IV","G0 IV","K6 IV","K9 IV","G2 IV","F9 IV","F0 IV","K8 IV","G8 IV","F6 IV","F5 IV","A0","A0IV","A0IV2"],"title":"get_universe_stars_star_id_spectral_class","description":"spectral_class string"},"temperature":{"type":"integer","format":"int32","title":"get_universe_stars_star_id_temperature","description":"temperature integer"}},"title":"get_universe_stars_star_id_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_universe_stars_star_id","x-alternate-versions":["dev","legacy","v1"]}},"/universe/ids/":{"post":{"description":"Resolve a set of names to IDs in the following categories: agents, alliances, characters, constellations, corporations factions, inventory_types, regions, stations, and systems. Only exact matches will be returned. All names searched for are cached for 12 hours.\n\n---\nAlternate route: `/dev/universe/ids/`\n\nAlternate route: `/legacy/universe/ids/`\n\nAlternate route: `/v1/universe/ids/`\n","summary":"Bulk names to IDs","tags":["Universe"],"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/language"},{"name":"names","in":"body","description":"The names to resolve","required":true,"schema":{"type":"array","minItems":1,"maxItems":1000,"uniqueItems":true,"items":{"type":"string","minLength":1,"maxLength":100,"title":"post_universe_ids_name","description":"name string"},"example":["CCP Zoetrope"],"title":"post_universe_ids_names","description":"names array"}},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"List of id/name associations for a set of names divided by category. Any name passed in that did not have a match will be ommitted.","examples":{"application/json":{"characters":[{"id":95465499,"name":"CCP Bartender"},{"id":2112625428,"name":"CCP Zoetrope"}],"systems":[{"id":30000142,"name":"Jita"}]}},"schema":{"type":"object","properties":{"agents":{"type":"array","maxItems":1000,"items":{"type":"object","properties":{"id":{"type":"integer","format":"int32","title":"post_universe_ids_id","description":"id integer"},"name":{"type":"string","title":"post_universe_ids_name","description":"name string"}},"title":"post_universe_ids_agent","description":"agent object"},"title":"post_universe_ids_agents","description":"agents array"},"alliances":{"type":"array","maxItems":1000,"items":{"type":"object","properties":{"id":{"type":"integer","format":"int32","title":"post_universe_ids_id","description":"id integer"},"name":{"type":"string","title":"post_universe_ids_name","description":"name string"}},"title":"post_universe_ids_alliance","description":"alliance object"},"title":"post_universe_ids_alliances","description":"alliances array"},"characters":{"type":"array","maxItems":1000,"items":{"type":"object","properties":{"id":{"type":"integer","format":"int32","title":"post_universe_ids_id","description":"id integer"},"name":{"type":"string","title":"post_universe_ids_name","description":"name string"}},"title":"post_universe_ids_character","description":"character object"},"title":"post_universe_ids_characters","description":"characters array"},"constellations":{"type":"array","maxItems":1000,"items":{"type":"object","properties":{"id":{"type":"integer","format":"int32","title":"post_universe_ids_id","description":"id integer"},"name":{"type":"string","title":"post_universe_ids_name","description":"name string"}},"title":"post_universe_ids_constellation","description":"constellation object"},"title":"post_universe_ids_constellations","description":"constellations array"},"corporations":{"type":"array","maxItems":1000,"items":{"type":"object","properties":{"id":{"type":"integer","format":"int32","title":"post_universe_ids_id","description":"id integer"},"name":{"type":"string","title":"post_universe_ids_name","description":"name string"}},"title":"post_universe_ids_corporation","description":"corporation object"},"title":"post_universe_ids_corporations","description":"corporations array"},"factions":{"type":"array","maxItems":1000,"items":{"type":"object","properties":{"id":{"type":"integer","format":"int32","title":"post_universe_ids_id","description":"id integer"},"name":{"type":"string","title":"post_universe_ids_name","description":"name string"}},"title":"post_universe_ids_faction","description":"faction object"},"title":"post_universe_ids_factions","description":"factions array"},"inventory_types":{"type":"array","maxItems":1000,"items":{"type":"object","properties":{"id":{"type":"integer","format":"int32","title":"post_universe_ids_id","description":"id integer"},"name":{"type":"string","title":"post_universe_ids_name","description":"name string"}},"title":"post_universe_ids_inventory_type","description":"inventory_type object"},"title":"post_universe_ids_inventory_types","description":"inventory_types array"},"regions":{"type":"array","maxItems":1000,"items":{"type":"object","properties":{"id":{"type":"integer","format":"int32","title":"post_universe_ids_id","description":"id integer"},"name":{"type":"string","title":"post_universe_ids_name","description":"name string"}},"title":"post_universe_ids_region","description":"region object"},"title":"post_universe_ids_regions","description":"regions array"},"systems":{"type":"array","maxItems":1000,"items":{"type":"object","properties":{"id":{"type":"integer","format":"int32","title":"post_universe_ids_id","description":"id integer"},"name":{"type":"string","title":"post_universe_ids_name","description":"name string"}},"title":"post_universe_ids_system","description":"system object"},"title":"post_universe_ids_systems","description":"systems array"},"stations":{"type":"array","maxItems":1000,"items":{"type":"object","properties":{"id":{"type":"integer","format":"int32","title":"post_universe_ids_id","description":"id integer"},"name":{"type":"string","title":"post_universe_ids_name","description":"name string"}},"title":"post_universe_ids_station","description":"station object"},"title":"post_universe_ids_stations","description":"stations array"}},"title":"post_universe_ids_ok","description":"200 ok object"},"headers":{"Content-Language":{"description":"The language used in the response","type":"string","enum":["de","en-us","fr","ja","ru","zh"]}}},"503":{"description":"Search backend is unreachable, try again later.","schema":{"type":"object","title":"post_universe_ids_service_unavailable","description":"Service unavailable","properties":{"error":{"type":"string","description":"Service unavailable message","title":"post_universe_ids_503_service_unavailable"}}},"examples":{"application/json":{"error":"Service unavailable message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"post_universe_ids","x-alternate-versions":["dev","legacy","v1"]}},"/ui/openwindow/marketdetails/":{"post":{"description":"Open the market details window for a specific typeID inside the client\n\n---\nAlternate route: `/dev/ui/openwindow/marketdetails/`\n\nAlternate route: `/legacy/ui/openwindow/marketdetails/`\n\nAlternate route: `/v1/ui/openwindow/marketdetails/`\n","summary":"Open Market Details","tags":["User Interface"],"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"name":"type_id","in":"query","description":"The item type to open in market window","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"204":{"description":"Open window request received"},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-ui.open_window.v1"]}],"operationId":"post_ui_openwindow_marketdetails","x-alternate-versions":["dev","legacy","v1"]}},"/ui/openwindow/contract/":{"post":{"description":"Open the contract window inside the client\n\n---\nAlternate route: `/dev/ui/openwindow/contract/`\n\nAlternate route: `/legacy/ui/openwindow/contract/`\n\nAlternate route: `/v1/ui/openwindow/contract/`\n","summary":"Open Contract Window","tags":["User Interface"],"parameters":[{"name":"contract_id","in":"query","description":"The contract to open","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"204":{"description":"Open window request received"},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-ui.open_window.v1"]}],"operationId":"post_ui_openwindow_contract","x-alternate-versions":["dev","legacy","v1"]}},"/ui/openwindow/information/":{"post":{"description":"Open the information window for a character, corporation or alliance inside the client\n\n---\nAlternate route: `/dev/ui/openwindow/information/`\n\nAlternate route: `/legacy/ui/openwindow/information/`\n\nAlternate route: `/v1/ui/openwindow/information/`\n","summary":"Open Information Window","tags":["User Interface"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"target_id","in":"query","description":"The target to open","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"204":{"description":"Open window request received"},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-ui.open_window.v1"]}],"operationId":"post_ui_openwindow_information","x-alternate-versions":["dev","legacy","v1"]}},"/ui/autopilot/waypoint/":{"post":{"description":"Set a solar system as autopilot waypoint\n\n---\nAlternate route: `/dev/ui/autopilot/waypoint/`\n\nAlternate route: `/v2/ui/autopilot/waypoint/`\n","summary":"Set Autopilot Waypoint","tags":["User Interface"],"parameters":[{"name":"add_to_beginning","in":"query","description":"Whether this solar system should be added to the beginning of all waypoints","required":true,"type":"boolean","default":false},{"name":"clear_other_waypoints","in":"query","description":"Whether clean other waypoints beforing adding this one","required":true,"type":"boolean","default":false},{"$ref":"#/parameters/datasource"},{"name":"destination_id","in":"query","description":"The destination to travel to, can be solar system, station or structure's id","required":true,"type":"integer","format":"int64"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"204":{"description":"Open window request received"},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-ui.write_waypoint.v1"]}],"operationId":"post_ui_autopilot_waypoint","x-alternate-versions":["dev","v2"]}},"/ui/openwindow/newmail/":{"post":{"description":"Open the New Mail window, according to settings from the request if applicable\n\n---\nAlternate route: `/dev/ui/openwindow/newmail/`\n\nAlternate route: `/legacy/ui/openwindow/newmail/`\n\nAlternate route: `/v1/ui/openwindow/newmail/`\n","summary":"Open New Mail Window","tags":["User Interface"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"new_mail","in":"body","description":"The details of mail to create","required":true,"schema":{"type":"object","required":["subject","body","recipients"],"properties":{"subject":{"type":"string","maxLength":1000,"title":"post_ui_openwindow_newmail_subject","description":"subject string"},"body":{"type":"string","maxLength":10000,"title":"post_ui_openwindow_newmail_body","description":"body string"},"recipients":{"type":"array","minItems":1,"maxItems":50,"items":{"type":"integer","format":"int32","title":"post_ui_openwindow_newmail_recipient","description":"recipient integer"},"title":"post_ui_openwindow_newmail_recipients","description":"recipients array"},"to_mailing_list_id":{"type":"integer","format":"int32","description":"Corporations, alliances and mailing lists are all types of mailing groups. You may only send to one mailing group, at a time, so you may fill out either this field or the to_corp_or_alliance_ids field","title":"post_ui_openwindow_newmail_to_mailing_list_id"},"to_corp_or_alliance_id":{"type":"integer","format":"int32","title":"post_ui_openwindow_newmail_to_corp_or_alliance_id","description":"to_corp_or_alliance_id integer"}},"title":"post_ui_openwindow_newmail_new_mail","description":"new_mail object"}},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"204":{"description":"Open window request received"},"422":{"description":"Invalid request","schema":{"type":"object","title":"post_ui_openwindow_newmail_unprocessable_entity","description":"Unprocessable entity","properties":{"error":{"type":"string","description":"Unprocessable entity message","title":"post_ui_openwindow_newmail_422_unprocessable_entity"}}},"examples":{"application/json":{"error":"Unprocessable entity message"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-ui.open_window.v1"]}],"operationId":"post_ui_openwindow_newmail","x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/wallet/":{"get":{"summary":"Get a character's wallet balance","description":"Returns a character's wallet balance\n\n---\nAlternate route: `/dev/characters/{character_id}/wallet/`\n\nAlternate route: `/legacy/characters/{character_id}/wallet/`\n\nAlternate route: `/v1/characters/{character_id}/wallet/`\n\n---\nThis route is cached for up to 120 seconds","tags":["Wallet"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Wallet balance","examples":{"application/json":29500.01},"schema":{"type":"number","format":"double","description":"Wallet balance","title":"get_characters_character_id_wallet_ok"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}}},"security":[{"evesso":["esi-wallet.read_character_wallet.v1"]}],"operationId":"get_characters_character_id_wallet","x-cached-seconds":120,"x-alternate-versions":["dev","legacy","v1"]}},"/characters/{character_id}/wallet/journal/":{"get":{"description":"Retrieve character wallet journal\n\n---\nAlternate route: `/v2/characters/{character_id}/wallet/journal/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\n[This route has an available update](https://esi.tech.ccp.is/diff/latest/dev/#GET-/characters/{character_id}/wallet/journal/)","summary":"Get character wallet journal","tags":["Wallet"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"name":"from_id","in":"query","description":"Only show journal entries happened before the transaction referenced by this id","required":false,"type":"integer","format":"int64"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Journal entries","examples":{"application/json":[{"date":"2016-10-24T09:00:00Z","ref_id":1234567890,"ref_type":"player_trading"}]},"schema":{"type":"array","description":"Journal entries","maxItems":2500,"items":{"type":"object","required":["date","ref_id","ref_type"],"properties":{"date":{"type":"string","format":"date-time","description":"Date and time of transaction","title":"get_characters_character_id_wallet_journal_date"},"ref_id":{"type":"integer","format":"int64","description":"Unique journal reference ID","title":"get_characters_character_id_wallet_journal_ref_id"},"ref_type":{"type":"string","description":"Transaction type, different type of transaction will populate different fields in `extra_info` Note: If you have an existing XML API application that is using ref_types, you will need to know which string ESI ref_type maps to which integer. You can use the following gist to see string->int mappings: https://gist.github.com/ccp-zoetrope/c03db66d90c2148724c06171bc52e0ec","enum":["acceleration_gate_fee","advertisement_listing_fee","agent_donation","agent_location_services","agent_miscellaneous","agent_mission_collateral_paid","agent_mission_collateral_refunded","agent_mission_reward","agent_mission_reward_corporation_tax","agent_mission_time_bonus_reward","agent_mission_time_bonus_reward_corporation_tax","agent_security_services","agent_services_rendered","agents_preward","alliance_maintainance_fee","alliance_registration_fee","asset_safety_recovery_tax","bounty","bounty_prize","bounty_prize_corporation_tax","bounty_prizes","bounty_reimbursement","bounty_surcharge","brokers_fee","clone_activation","clone_transfer","contraband_fine","contract_auction_bid","contract_auction_bid_corp","contract_auction_bid_refund","contract_auction_sold","contract_brokers_fee","contract_brokers_fee_corp","contract_collateral","contract_collateral_deposited_corp","contract_collateral_payout","contract_collateral_refund","contract_deposit","contract_deposit_corp","contract_deposit_refund","contract_deposit_sales_tax","contract_price","contract_price_payment_corp","contract_reversal","contract_reward","contract_reward_deposited","contract_reward_deposited_corp","contract_reward_refund","contract_sales_tax","copying","corporate_reward_payout","corporate_reward_tax","corporation_account_withdrawal","corporation_bulk_payment","corporation_dividend_payment","corporation_liquidation","corporation_logo_change_cost","corporation_payment","corporation_registration_fee","courier_mission_escrow","cspa","cspaofflinerefund","datacore_fee","dna_modification_fee","docking_fee","duel_wager_escrow","duel_wager_payment","duel_wager_refund","factory_slot_rental_fee","gm_cash_transfer","industry_job_tax","infrastructure_hub_maintenance","inheritance","insurance","jump_clone_activation_fee","jump_clone_installation_fee","kill_right_fee","lp_store","manufacturing","market_escrow","market_fine_paid","market_transaction","medal_creation","medal_issued","mission_completion","mission_cost","mission_expiration","mission_reward","office_rental_fee","operation_bonus","opportunity_reward","planetary_construction","planetary_export_tax","planetary_import_tax","player_donation","player_trading","project_discovery_reward","project_discovery_tax","reaction","release_of_impounded_property","repair_bill","reprocessing_tax","researching_material_productivity","researching_technology","researching_time_productivity","resource_wars_reward","reverse_engineering","security_processing_fee","shares","sovereignity_bill","store_purchase","store_purchase_refund","transaction_tax","upkeep_adjustment_fee","war_ally_contract","war_fee","war_fee_surrender"],"title":"get_characters_character_id_wallet_journal_ref_type"},"first_party_id":{"type":"integer","format":"int32","title":"get_characters_character_id_wallet_journal_first_party_id","description":"first_party_id integer"},"first_party_type":{"type":"string","enum":["character","corporation","alliance","faction","system"],"title":"get_characters_character_id_wallet_journal_first_party_type","description":"first_party_type string"},"second_party_id":{"type":"integer","format":"int32","title":"get_characters_character_id_wallet_journal_second_party_id","description":"second_party_id integer"},"second_party_type":{"type":"string","enum":["character","corporation","alliance","faction","system"],"title":"get_characters_character_id_wallet_journal_second_party_type","description":"second_party_type string"},"amount":{"type":"number","format":"double","description":"Transaction amount. Positive when value transferred to the first party. Negative otherwise","title":"get_characters_character_id_wallet_journal_amount"},"balance":{"type":"number","format":"double","description":"Wallet balance after transaction occurred","title":"get_characters_character_id_wallet_journal_balance"},"reason":{"type":"string","title":"get_characters_character_id_wallet_journal_reason","description":"reason string"},"tax_reciever_id":{"type":"integer","format":"int32","description":"the corporation ID receiving any tax paid","title":"get_characters_character_id_wallet_journal_tax_reciever_id"},"tax":{"type":"number","format":"double","description":"Tax amount received for tax related transactions","title":"get_characters_character_id_wallet_journal_tax"},"extra_info":{"type":"object","description":"Extra information for different type of transaction","properties":{"location_id":{"type":"integer","format":"int64","title":"get_characters_character_id_wallet_journal_location_id","description":"location_id integer"},"transaction_id":{"type":"integer","format":"int64","title":"get_characters_character_id_wallet_journal_transaction_id","description":"transaction_id integer"},"npc_name":{"type":"string","title":"get_characters_character_id_wallet_journal_npc_name","description":"npc_name string"},"npc_id":{"type":"integer","format":"int32","title":"get_characters_character_id_wallet_journal_npc_id","description":"npc_id integer"},"destroyed_ship_type_id":{"type":"integer","format":"int32","title":"get_characters_character_id_wallet_journal_destroyed_ship_type_id","description":"destroyed_ship_type_id integer"},"character_id":{"type":"integer","format":"int32","title":"get_characters_character_id_wallet_journal_character_id","description":"character_id integer"},"corporation_id":{"type":"integer","format":"int32","title":"get_characters_character_id_wallet_journal_corporation_id","description":"corporation_id integer"},"alliance_id":{"type":"integer","format":"int32","title":"get_characters_character_id_wallet_journal_alliance_id","description":"alliance_id integer"},"job_id":{"type":"integer","format":"int32","title":"get_characters_character_id_wallet_journal_job_id","description":"job_id integer"},"contract_id":{"type":"integer","format":"int32","title":"get_characters_character_id_wallet_journal_contract_id","description":"contract_id integer"},"system_id":{"type":"integer","format":"int32","title":"get_characters_character_id_wallet_journal_system_id","description":"system_id integer"},"planet_id":{"type":"integer","format":"int32","title":"get_characters_character_id_wallet_journal_planet_id","description":"planet_id integer"}},"title":"get_characters_character_id_wallet_journal_extra_info"}},"title":"get_characters_character_id_wallet_journal_200_ok","description":"200 ok object"},"title":"get_characters_character_id_wallet_journal_ok"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-wallet.read_character_wallet.v1"]}],"operationId":"get_characters_character_id_wallet_journal","x-cached-seconds":3600,"x-alternate-versions":["v2"]}},"/characters/{character_id}/wallet/transactions/":{"get":{"description":"Get wallet transactions of a character\n\n---\nAlternate route: `/dev/characters/{character_id}/wallet/transactions/`\n\nAlternate route: `/legacy/characters/{character_id}/wallet/transactions/`\n\nAlternate route: `/v1/characters/{character_id}/wallet/transactions/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get wallet transactions","tags":["Wallet"],"parameters":[{"$ref":"#/parameters/character_id"},{"$ref":"#/parameters/datasource"},{"name":"from_id","in":"query","description":"Only show transactions happened before the one referenced by this id","required":false,"type":"integer","format":"int64"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Wallet transactions","examples":{"application/json":[{"transaction_id":1234567890,"date":"2016-10-24T09:00:00Z","location_id":60014719,"type_id":587,"unit_price":1,"quantity":1,"client_id":54321,"is_buy":true,"is_personal":true,"journal_ref_id":67890}]},"schema":{"type":"array","description":"Wallet transactions","maxItems":2500,"items":{"type":"object","description":"wallet transaction","required":["transaction_id","date","location_id","type_id","unit_price","quantity","client_id","is_buy","is_personal","journal_ref_id"],"properties":{"transaction_id":{"type":"integer","format":"int64","description":"Unique transaction ID","title":"get_characters_character_id_wallet_transactions_transaction_id"},"date":{"type":"string","format":"date-time","description":"Date and time of transaction","title":"get_characters_character_id_wallet_transactions_date"},"type_id":{"type":"integer","format":"int32","title":"get_characters_character_id_wallet_transactions_type_id","description":"type_id integer"},"location_id":{"type":"integer","format":"int64","title":"get_characters_character_id_wallet_transactions_location_id","description":"location_id integer"},"unit_price":{"type":"number","format":"double","description":"Amount paid per unit","title":"get_characters_character_id_wallet_transactions_unit_price"},"quantity":{"type":"integer","format":"int32","title":"get_characters_character_id_wallet_transactions_quantity","description":"quantity integer"},"client_id":{"type":"integer","format":"int32","title":"get_characters_character_id_wallet_transactions_client_id","description":"client_id integer"},"is_buy":{"type":"boolean","title":"get_characters_character_id_wallet_transactions_is_buy","description":"is_buy boolean"},"is_personal":{"type":"boolean","title":"get_characters_character_id_wallet_transactions_is_personal","description":"is_personal boolean"},"journal_ref_id":{"type":"integer","format":"int64","title":"get_characters_character_id_wallet_transactions_journal_ref_id","description":"journal_ref_id integer"}},"title":"get_characters_character_id_wallet_transactions_200_ok"},"title":"get_characters_character_id_wallet_transactions_ok"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"security":[{"evesso":["esi-wallet.read_character_wallet.v1"]}],"operationId":"get_characters_character_id_wallet_transactions","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/wallets/":{"get":{"description":"Get a corporation's wallets\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/wallets/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/wallets/`\n\nAlternate route: `/v1/corporations/{corporation_id}/wallets/`\n\n---\nThis route is cached for up to 300 seconds\n\n---\nRequires one of the following EVE corporation role(s): Accountant, Junior_Accountant\n","summary":"Returns a corporation's wallet balance","tags":["Wallet"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"List of corporation wallets","examples":{"application/json":[{"division":1,"balance":123.45},{"division":2,"balance":123.45},{"division":3,"balance":123.45},{"division":4,"balance":123.45},{"division":5,"balance":123.45},{"division":6,"balance":123.45},{"division":7,"balance":123.45}]},"schema":{"type":"array","maxItems":7,"items":{"type":"object","required":["division","balance"],"properties":{"division":{"type":"integer","format":"int32","minimum":1,"maximum":7,"title":"get_corporations_corporation_id_wallets_division","description":"division integer"},"balance":{"type":"number","format":"double","title":"get_corporations_corporation_id_wallets_balance","description":"balance number"}},"title":"get_corporations_corporation_id_wallets_200_ok","description":"200 ok object"},"title":"get_corporations_corporation_id_wallets_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":["Accountant","Junior_Accountant"],"security":[{"evesso":["esi-wallet.read_corporation_wallets.v1"]}],"operationId":"get_corporations_corporation_id_wallets","x-cached-seconds":300,"x-alternate-versions":["dev","legacy","v1"]}},"/corporations/{corporation_id}/wallets/{division}/journal/":{"get":{"description":"Retrieve corporation wallet journal\n\n---\nAlternate route: `/legacy/corporations/{corporation_id}/wallets/{division}/journal/`\n\nAlternate route: `/v1/corporations/{corporation_id}/wallets/{division}/journal/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\nRequires one of the following EVE corporation role(s): Accountant, Junior_Accountant\n\n\n---\n[This route has an available update](https://esi.tech.ccp.is/diff/latest/dev/#GET-/corporations/{corporation_id}/wallets/{division}/journal/)","summary":"Get corporation wallet journal","tags":["Wallet"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"name":"division","in":"path","description":"Wallet key of the division to fetch journals from","required":true,"type":"integer","format":"int32","minimum":1,"maximum":7},{"name":"from_id","in":"query","description":"Only show journal entries happened before the transaction referenced by this id","required":false,"type":"integer","format":"int64"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Journal entries","examples":{"application/json":[{"date":"2016-10-24T09:00:00Z","ref_id":1234567890,"ref_type":"player_trading"}]},"schema":{"type":"array","description":"Journal entries","maxItems":2500,"items":{"type":"object","required":["date","ref_id","ref_type"],"properties":{"date":{"type":"string","format":"date-time","description":"Date and time of transaction","title":"get_corporations_corporation_id_wallets_division_journal_date"},"ref_id":{"type":"integer","format":"int64","description":"Unique journal reference ID","title":"get_corporations_corporation_id_wallets_division_journal_ref_id"},"ref_type":{"type":"string","description":"Transaction type, different type of transaction will populate different fields in `extra_info` Note: If you have an existing XML API application that is using ref_types, you will need to know which string ESI ref_type maps to which integer. You can use the following gist to see string->int mappings: https://gist.github.com/ccp-zoetrope/c03db66d90c2148724c06171bc52e0ec","enum":["acceleration_gate_fee","advertisement_listing_fee","agent_donation","agent_location_services","agent_miscellaneous","agent_mission_collateral_paid","agent_mission_collateral_refunded","agent_mission_reward","agent_mission_reward_corporation_tax","agent_mission_time_bonus_reward","agent_mission_time_bonus_reward_corporation_tax","agent_security_services","agent_services_rendered","agents_preward","alliance_maintainance_fee","alliance_registration_fee","asset_safety_recovery_tax","bounty","bounty_prize","bounty_prize_corporation_tax","bounty_prizes","bounty_reimbursement","bounty_surcharge","brokers_fee","clone_activation","clone_transfer","contraband_fine","contract_auction_bid","contract_auction_bid_corp","contract_auction_bid_refund","contract_auction_sold","contract_brokers_fee","contract_brokers_fee_corp","contract_collateral","contract_collateral_deposited_corp","contract_collateral_payout","contract_collateral_refund","contract_deposit","contract_deposit_corp","contract_deposit_refund","contract_deposit_sales_tax","contract_price","contract_price_payment_corp","contract_reversal","contract_reward","contract_reward_deposited","contract_reward_deposited_corp","contract_reward_refund","contract_sales_tax","copying","corporate_reward_payout","corporate_reward_tax","corporation_account_withdrawal","corporation_bulk_payment","corporation_dividend_payment","corporation_liquidation","corporation_logo_change_cost","corporation_payment","corporation_registration_fee","courier_mission_escrow","cspa","cspaofflinerefund","datacore_fee","dna_modification_fee","docking_fee","duel_wager_escrow","duel_wager_payment","duel_wager_refund","factory_slot_rental_fee","gm_cash_transfer","industry_job_tax","infrastructure_hub_maintenance","inheritance","insurance","jump_clone_activation_fee","jump_clone_installation_fee","kill_right_fee","lp_store","manufacturing","market_escrow","market_fine_paid","market_transaction","medal_creation","medal_issued","mission_completion","mission_cost","mission_expiration","mission_reward","office_rental_fee","operation_bonus","opportunity_reward","planetary_construction","planetary_export_tax","planetary_import_tax","player_donation","player_trading","project_discovery_reward","project_discovery_tax","reaction","release_of_impounded_property","repair_bill","reprocessing_tax","researching_material_productivity","researching_technology","researching_time_productivity","resource_wars_reward","reverse_engineering","security_processing_fee","shares","sovereignity_bill","store_purchase","store_purchase_refund","transaction_tax","upkeep_adjustment_fee","war_ally_contract","war_fee","war_fee_surrender"],"title":"get_corporations_corporation_id_wallets_division_journal_ref_type"},"first_party_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_wallets_division_journal_first_party_id","description":"first_party_id integer"},"first_party_type":{"type":"string","enum":["character","corporation","alliance","faction","system"],"title":"get_corporations_corporation_id_wallets_division_journal_first_party_type","description":"first_party_type string"},"second_party_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_wallets_division_journal_second_party_id","description":"second_party_id integer"},"second_party_type":{"type":"string","enum":["character","corporation","alliance","faction","system"],"title":"get_corporations_corporation_id_wallets_division_journal_second_party_type","description":"second_party_type string"},"amount":{"type":"number","format":"double","description":"Transaction amount. Positive when value transferred to the first party. Negative otherwise","title":"get_corporations_corporation_id_wallets_division_journal_amount"},"balance":{"type":"number","format":"double","description":"Wallet balance after transaction occurred","title":"get_corporations_corporation_id_wallets_division_journal_balance"},"reason":{"type":"string","title":"get_corporations_corporation_id_wallets_division_journal_reason","description":"reason string"},"tax_reciever_id":{"type":"integer","format":"int32","description":"the corporation ID receiving any tax paid","title":"get_corporations_corporation_id_wallets_division_journal_tax_reciever_id"},"tax":{"type":"number","format":"double","description":"Tax amount received for tax related transactions","title":"get_corporations_corporation_id_wallets_division_journal_tax"},"extra_info":{"type":"object","description":"Extra information for different type of transaction","properties":{"location_id":{"type":"integer","format":"int64","title":"get_corporations_corporation_id_wallets_division_journal_location_id","description":"location_id integer"},"transaction_id":{"type":"integer","format":"int64","title":"get_corporations_corporation_id_wallets_division_journal_transaction_id","description":"transaction_id integer"},"npc_name":{"type":"string","title":"get_corporations_corporation_id_wallets_division_journal_npc_name","description":"npc_name string"},"npc_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_wallets_division_journal_npc_id","description":"npc_id integer"},"destroyed_ship_type_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_wallets_division_journal_destroyed_ship_type_id","description":"destroyed_ship_type_id integer"},"character_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_wallets_division_journal_character_id","description":"character_id integer"},"corporation_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_wallets_division_journal_corporation_id","description":"corporation_id integer"},"alliance_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_wallets_division_journal_alliance_id","description":"alliance_id integer"},"job_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_wallets_division_journal_job_id","description":"job_id integer"},"contract_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_wallets_division_journal_contract_id","description":"contract_id integer"},"system_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_wallets_division_journal_system_id","description":"system_id integer"},"planet_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_wallets_division_journal_planet_id","description":"planet_id integer"}},"title":"get_corporations_corporation_id_wallets_division_journal_extra_info"}},"title":"get_corporations_corporation_id_wallets_division_journal_200_ok","description":"200 ok object"},"title":"get_corporations_corporation_id_wallets_division_journal_ok"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":["Accountant","Junior_Accountant"],"security":[{"evesso":["esi-wallet.read_corporation_wallets.v1"]}],"operationId":"get_corporations_corporation_id_wallets_division_journal","x-cached-seconds":3600,"x-alternate-versions":["legacy","v1"]}},"/corporations/{corporation_id}/wallets/{division}/transactions/":{"get":{"description":"Get wallet transactions of a corporation\n\n---\nAlternate route: `/dev/corporations/{corporation_id}/wallets/{division}/transactions/`\n\nAlternate route: `/legacy/corporations/{corporation_id}/wallets/{division}/transactions/`\n\nAlternate route: `/v1/corporations/{corporation_id}/wallets/{division}/transactions/`\n\n---\nThis route is cached for up to 3600 seconds\n\n---\nRequires one of the following EVE corporation role(s): Accountant, Junior_Accountant\n","summary":"Get corporation wallet transactions","tags":["Wallet"],"parameters":[{"$ref":"#/parameters/corporation_id"},{"$ref":"#/parameters/datasource"},{"name":"division","in":"path","description":"Wallet key of the division to fetch journals from","required":true,"type":"integer","format":"int32","minimum":1,"maximum":7},{"name":"from_id","in":"query","description":"Only show journal entries happened before the transaction referenced by this id","required":false,"type":"integer","format":"int64"},{"$ref":"#/parameters/token"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Wallet transactions","examples":{"application/json":[{"transaction_id":1234567890,"date":"2016-10-24T09:00:00Z","location_id":60014719,"type_id":587,"unit_price":1,"quantity":1,"client_id":54321,"is_buy":true,"journal_ref_id":67890}]},"schema":{"type":"array","description":"Wallet transactions","maxItems":2500,"items":{"type":"object","description":"wallet transaction","required":["transaction_id","date","location_id","type_id","unit_price","quantity","client_id","is_buy","journal_ref_id"],"properties":{"transaction_id":{"type":"integer","format":"int64","description":"Unique transaction ID","title":"get_corporations_corporation_id_wallets_division_transactions_transaction_id"},"date":{"type":"string","format":"date-time","description":"Date and time of transaction","title":"get_corporations_corporation_id_wallets_division_transactions_date"},"type_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_wallets_division_transactions_type_id","description":"type_id integer"},"location_id":{"type":"integer","format":"int64","title":"get_corporations_corporation_id_wallets_division_transactions_location_id","description":"location_id integer"},"unit_price":{"type":"number","format":"double","description":"Amount paid per unit","title":"get_corporations_corporation_id_wallets_division_transactions_unit_price"},"quantity":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_wallets_division_transactions_quantity","description":"quantity integer"},"client_id":{"type":"integer","format":"int32","title":"get_corporations_corporation_id_wallets_division_transactions_client_id","description":"client_id integer"},"is_buy":{"type":"boolean","title":"get_corporations_corporation_id_wallets_division_transactions_is_buy","description":"is_buy boolean"},"journal_ref_id":{"type":"integer","format":"int64","title":"get_corporations_corporation_id_wallets_division_transactions_journal_ref_id","description":"journal_ref_id integer"}},"title":"get_corporations_corporation_id_wallets_division_transactions_200_ok"},"title":"get_corporations_corporation_id_wallets_division_transactions_ok"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"403":{"description":"Forbidden","schema":{"$ref":"#/definitions/forbidden"},"examples":{"application/json":{"error":"Forbidden message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"x-required-roles":["Accountant","Junior_Accountant"],"security":[{"evesso":["esi-wallet.read_corporation_wallets.v1"]}],"operationId":"get_corporations_corporation_id_wallets_division_transactions","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/wars/":{"get":{"description":"Return a list of wars\n\n---\nAlternate route: `/dev/wars/`\n\nAlternate route: `/legacy/wars/`\n\nAlternate route: `/v1/wars/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"List wars","tags":["Wars"],"parameters":[{"$ref":"#/parameters/datasource"},{"name":"max_war_id","in":"query","description":"Only return wars with ID smaller than this.","required":false,"type":"integer","format":"int32"},{"$ref":"#/parameters/user_agent"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of war IDs, in decending order by war_id.","examples":{"application/json":[3,2,1]},"schema":{"type":"array","maxItems":2000,"items":{"type":"integer","format":"int32","title":"get_wars_200_ok","description":"200 ok integer"},"title":"get_wars_ok","description":"200 ok array"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_wars","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/wars/{war_id}/":{"get":{"description":"Return details about a war\n\n---\nAlternate route: `/dev/wars/{war_id}/`\n\nAlternate route: `/legacy/wars/{war_id}/`\n\nAlternate route: `/v1/wars/{war_id}/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"Get war information","tags":["Wars"],"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/user_agent"},{"name":"war_id","in":"path","description":"ID for a war","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"Details about a war","examples":{"application/json":{"id":1941,"declared":"2004-05-22T05:20:00Z","mutual":false,"open_for_allies":false,"aggressor":{"corporation_id":986665792,"ships_killed":0,"isk_destroyed":0},"defender":{"corporation_id":1001562011,"ships_killed":0,"isk_destroyed":0}}},"schema":{"type":"object","required":["id","declared","mutual","open_for_allies","aggressor","defender"],"properties":{"id":{"type":"integer","format":"int32","description":"ID of the specified war","title":"get_wars_war_id_id"},"declared":{"type":"string","format":"date-time","description":"Time that the war was declared","title":"get_wars_war_id_declared"},"started":{"type":"string","format":"date-time","description":"Time when the war started and both sides could shoot each other","title":"get_wars_war_id_started"},"retracted":{"type":"string","format":"date-time","description":"Time the war was retracted but both sides could still shoot each other","title":"get_wars_war_id_retracted"},"finished":{"type":"string","format":"date-time","description":"Time the war ended and shooting was no longer allowed","title":"get_wars_war_id_finished"},"mutual":{"type":"boolean","description":"Was the war declared mutual by both parties","title":"get_wars_war_id_mutual"},"open_for_allies":{"type":"boolean","description":"Is the war currently open for allies or not","title":"get_wars_war_id_open_for_allies"},"aggressor":{"type":"object","description":"The aggressor corporation or alliance that declared this war, only contains either corporation_id or alliance_id","required":["ships_killed","isk_destroyed"],"properties":{"corporation_id":{"type":"integer","format":"int32","description":"Corporation ID if and only if the aggressor is a corporation","title":"get_wars_war_id_corporation_id"},"alliance_id":{"type":"integer","format":"int32","description":"Alliance ID if and only if the aggressor is an alliance","title":"get_wars_war_id_alliance_id"},"ships_killed":{"type":"integer","format":"int32","description":"The number of ships the aggressor has killed","title":"get_wars_war_id_ships_killed"},"isk_destroyed":{"type":"number","format":"float","description":"ISK value of ships the aggressor has destroyed","title":"get_wars_war_id_isk_destroyed"}},"title":"get_wars_war_id_aggressor"},"defender":{"type":"object","description":"The defending corporation or alliance that declared this war, only contains either corporation_id or alliance_id","required":["ships_killed","isk_destroyed"],"properties":{"corporation_id":{"type":"integer","format":"int32","description":"Corporation ID if and only if the defender is a corporation","title":"get_wars_war_id_corporation_id"},"alliance_id":{"type":"integer","format":"int32","description":"Alliance ID if and only if the defender is an alliance","title":"get_wars_war_id_alliance_id"},"ships_killed":{"type":"integer","format":"int32","description":"The number of ships the defender has killed","title":"get_wars_war_id_ships_killed"},"isk_destroyed":{"type":"number","format":"float","description":"ISK value of ships the defender has killed","title":"get_wars_war_id_isk_destroyed"}},"title":"get_wars_war_id_defender"},"allies":{"type":"array","description":"allied corporations or alliances, each object contains either corporation_id or alliance_id","maxItems":10000,"items":{"type":"object","properties":{"corporation_id":{"type":"integer","format":"int32","description":"Corporation ID if and only if this ally is a corporation","title":"get_wars_war_id_corporation_id"},"alliance_id":{"type":"integer","format":"int32","description":"Alliance ID if and only if this ally is an alliance","title":"get_wars_war_id_alliance_id"}},"title":"get_wars_war_id_ally","description":"ally object"},"title":"get_wars_war_id_allies"}},"title":"get_wars_war_id_ok","description":"200 ok object"},"headers":{"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"422":{"description":"War not found","schema":{"type":"object","title":"get_wars_war_id_unprocessable_entity","description":"Unprocessable entity","properties":{"error":{"type":"string","description":"Unprocessable entity message","title":"get_wars_war_id_422_unprocessable_entity"}}},"examples":{"application/json":{"error":"Unprocessable entity message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_wars_war_id","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}},"/wars/{war_id}/killmails/":{"get":{"description":"Return a list of kills related to a war\n\n---\nAlternate route: `/dev/wars/{war_id}/killmails/`\n\nAlternate route: `/legacy/wars/{war_id}/killmails/`\n\nAlternate route: `/v1/wars/{war_id}/killmails/`\n\n---\nThis route is cached for up to 3600 seconds","summary":"List kills for a war","tags":["Wars"],"parameters":[{"$ref":"#/parameters/datasource"},{"$ref":"#/parameters/page"},{"$ref":"#/parameters/user_agent"},{"name":"war_id","in":"path","description":"A valid war ID","required":true,"type":"integer","format":"int32"},{"$ref":"#/parameters/X-User-Agent"}],"responses":{"200":{"description":"A list of killmail IDs and hashes","examples":{"application/json":[{"killmail_id":2,"killmail_hash":"8eef5e8fb6b88fe3407c489df33822b2e3b57a5e"},{"killmail_id":1,"killmail_hash":"b41ccb498ece33d64019f64c0db392aa3aa701fb"}]},"schema":{"type":"array","maxItems":2000,"items":{"type":"object","required":["killmail_id","killmail_hash"],"properties":{"killmail_id":{"type":"integer","format":"int32","description":"ID of this killmail","title":"get_wars_war_id_killmails_killmail_id"},"killmail_hash":{"type":"string","description":"A hash of this killmail","title":"get_wars_war_id_killmails_killmail_hash"}},"title":"get_wars_war_id_killmails_200_ok","description":"200 ok object"},"title":"get_wars_war_id_killmails_ok","description":"200 ok array"},"headers":{"X-Pages":{"description":"Maximum page number","type":"integer","format":"int32","default":1},"Cache-Control":{"description":"The caching mechanism used","type":"string"},"Last-Modified":{"description":"RFC7231 formatted datetime string","type":"string"},"Expires":{"description":"RFC7231 formatted datetime string","type":"string"}}},"422":{"description":"War not found","schema":{"type":"object","title":"get_wars_war_id_killmails_unprocessable_entity","description":"Unprocessable entity","properties":{"error":{"type":"string","description":"Unprocessable entity message","title":"get_wars_war_id_killmails_422_unprocessable_entity"}}},"examples":{"application/json":{"error":"Unprocessable entity message"}}},"500":{"description":"Internal server error","schema":{"$ref":"#/definitions/internal_server_error"},"examples":{"application/json":{"error":"Internal server error message"}}}},"operationId":"get_wars_war_id_killmails","x-cached-seconds":3600,"x-alternate-versions":["dev","legacy","v1"]}}},"securityDefinitions":{"evesso":{"type":"oauth2","authorizationUrl":"https://login.eveonline.com/oauth/authorize","flow":"implicit","scopes":{"esi-alliances.read_contacts.v1":"EVE SSO scope esi-alliances.read_contacts.v1","esi-assets.read_assets.v1":"EVE SSO scope esi-assets.read_assets.v1","esi-assets.read_corporation_assets.v1":"EVE SSO scope esi-assets.read_corporation_assets.v1","esi-bookmarks.read_character_bookmarks.v1":"EVE SSO scope esi-bookmarks.read_character_bookmarks.v1","esi-bookmarks.read_corporation_bookmarks.v1":"EVE SSO scope esi-bookmarks.read_corporation_bookmarks.v1","esi-calendar.read_calendar_events.v1":"EVE SSO scope esi-calendar.read_calendar_events.v1","esi-calendar.respond_calendar_events.v1":"EVE SSO scope esi-calendar.respond_calendar_events.v1","esi-characters.read_agents_research.v1":"EVE SSO scope esi-characters.read_agents_research.v1","esi-characters.read_blueprints.v1":"EVE SSO scope esi-characters.read_blueprints.v1","esi-characters.read_chat_channels.v1":"EVE SSO scope esi-characters.read_chat_channels.v1","esi-characters.read_contacts.v1":"EVE SSO scope esi-characters.read_contacts.v1","esi-characters.read_corporation_roles.v1":"EVE SSO scope esi-characters.read_corporation_roles.v1","esi-characters.read_fatigue.v1":"EVE SSO scope esi-characters.read_fatigue.v1","esi-characters.read_fw_stats.v1":"EVE SSO scope esi-characters.read_fw_stats.v1","esi-characters.read_loyalty.v1":"EVE SSO scope esi-characters.read_loyalty.v1","esi-characters.read_medals.v1":"EVE SSO scope esi-characters.read_medals.v1","esi-characters.read_notifications.v1":"EVE SSO scope esi-characters.read_notifications.v1","esi-characters.read_opportunities.v1":"EVE SSO scope esi-characters.read_opportunities.v1","esi-characters.read_standings.v1":"EVE SSO scope esi-characters.read_standings.v1","esi-characters.read_titles.v1":"EVE SSO scope esi-characters.read_titles.v1","esi-characters.write_contacts.v1":"EVE SSO scope esi-characters.write_contacts.v1","esi-characterstats.read.v1":"EVE SSO scope esi-characterstats.read.v1","esi-clones.read_clones.v1":"EVE SSO scope esi-clones.read_clones.v1","esi-clones.read_implants.v1":"EVE SSO scope esi-clones.read_implants.v1","esi-contracts.read_character_contracts.v1":"EVE SSO scope esi-contracts.read_character_contracts.v1","esi-contracts.read_corporation_contracts.v1":"EVE SSO scope esi-contracts.read_corporation_contracts.v1","esi-corporations.read_blueprints.v1":"EVE SSO scope esi-corporations.read_blueprints.v1","esi-corporations.read_contacts.v1":"EVE SSO scope esi-corporations.read_contacts.v1","esi-corporations.read_container_logs.v1":"EVE SSO scope esi-corporations.read_container_logs.v1","esi-corporations.read_corporation_membership.v1":"EVE SSO scope esi-corporations.read_corporation_membership.v1","esi-corporations.read_divisions.v1":"EVE SSO scope esi-corporations.read_divisions.v1","esi-corporations.read_facilities.v1":"EVE SSO scope esi-corporations.read_facilities.v1","esi-corporations.read_fw_stats.v1":"EVE SSO scope esi-corporations.read_fw_stats.v1","esi-corporations.read_medals.v1":"EVE SSO scope esi-corporations.read_medals.v1","esi-corporations.read_outposts.v1":"EVE SSO scope esi-corporations.read_outposts.v1","esi-corporations.read_standings.v1":"EVE SSO scope esi-corporations.read_standings.v1","esi-corporations.read_starbases.v1":"EVE SSO scope esi-corporations.read_starbases.v1","esi-corporations.read_structures.v1":"EVE SSO scope esi-corporations.read_structures.v1","esi-corporations.read_titles.v1":"EVE SSO scope esi-corporations.read_titles.v1","esi-corporations.track_members.v1":"EVE SSO scope esi-corporations.track_members.v1","esi-corporations.write_structures.v1":"EVE SSO scope esi-corporations.write_structures.v1","esi-fittings.read_fittings.v1":"EVE SSO scope esi-fittings.read_fittings.v1","esi-fittings.write_fittings.v1":"EVE SSO scope esi-fittings.write_fittings.v1","esi-fleets.read_fleet.v1":"EVE SSO scope esi-fleets.read_fleet.v1","esi-fleets.write_fleet.v1":"EVE SSO scope esi-fleets.write_fleet.v1","esi-industry.read_character_jobs.v1":"EVE SSO scope esi-industry.read_character_jobs.v1","esi-industry.read_character_mining.v1":"EVE SSO scope esi-industry.read_character_mining.v1","esi-industry.read_corporation_jobs.v1":"EVE SSO scope esi-industry.read_corporation_jobs.v1","esi-industry.read_corporation_mining.v1":"EVE SSO scope esi-industry.read_corporation_mining.v1","esi-killmails.read_corporation_killmails.v1":"EVE SSO scope esi-killmails.read_corporation_killmails.v1","esi-killmails.read_killmails.v1":"EVE SSO scope esi-killmails.read_killmails.v1","esi-location.read_location.v1":"EVE SSO scope esi-location.read_location.v1","esi-location.read_online.v1":"EVE SSO scope esi-location.read_online.v1","esi-location.read_ship_type.v1":"EVE SSO scope esi-location.read_ship_type.v1","esi-mail.organize_mail.v1":"EVE SSO scope esi-mail.organize_mail.v1","esi-mail.read_mail.v1":"EVE SSO scope esi-mail.read_mail.v1","esi-mail.send_mail.v1":"EVE SSO scope esi-mail.send_mail.v1","esi-markets.read_character_orders.v1":"EVE SSO scope esi-markets.read_character_orders.v1","esi-markets.read_corporation_orders.v1":"EVE SSO scope esi-markets.read_corporation_orders.v1","esi-markets.structure_markets.v1":"EVE SSO scope esi-markets.structure_markets.v1","esi-planets.manage_planets.v1":"EVE SSO scope esi-planets.manage_planets.v1","esi-planets.read_customs_offices.v1":"EVE SSO scope esi-planets.read_customs_offices.v1","esi-search.search_structures.v1":"EVE SSO scope esi-search.search_structures.v1","esi-skills.read_skillqueue.v1":"EVE SSO scope esi-skills.read_skillqueue.v1","esi-skills.read_skills.v1":"EVE SSO scope esi-skills.read_skills.v1","esi-ui.open_window.v1":"EVE SSO scope esi-ui.open_window.v1","esi-ui.write_waypoint.v1":"EVE SSO scope esi-ui.write_waypoint.v1","esi-universe.read_structures.v1":"EVE SSO scope esi-universe.read_structures.v1","esi-wallet.read_character_wallet.v1":"EVE SSO scope esi-wallet.read_character_wallet.v1","esi-wallet.read_corporation_wallets.v1":"EVE SSO scope esi-wallet.read_corporation_wallets.v1"}}},"parameters":{"datasource":{"name":"datasource","description":"The server name you would like data from","in":"query","type":"string","default":"tranquility","enum":["tranquility","singularity"]},"user_agent":{"name":"user_agent","description":"Client identifier, takes precedence over headers","in":"query","type":"string"},"X-User-Agent":{"name":"X-User-Agent","description":"Client identifier, takes precedence over User-Agent","in":"header","type":"string"},"page":{"name":"page","description":"Which page of results to return","in":"query","type":"integer","format":"int32","default":1},"token":{"name":"token","description":"Access token to use if unable to set a header","in":"query","type":"string"},"character_id":{"description":"An EVE character ID","format":"int32","in":"path","name":"character_id","required":true,"type":"integer"},"corporation_id":{"description":"An EVE corporation ID","format":"int32","in":"path","name":"corporation_id","required":true,"type":"integer"},"language":{"name":"language","description":"Language to use in the response","in":"query","type":"string","default":"en-us","enum":["de","en-us","fr","ja","ru","zh"]},"alliance_id":{"description":"An EVE alliance ID","format":"int32","in":"path","name":"alliance_id","required":true,"type":"integer"}},"definitions":{"internal_server_error":{"type":"object","description":"Internal server error model","title":"Internal server error","required":["error"],"properties":{"error":{"type":"string","description":"Internal server error message"}}},"forbidden":{"type":"object","description":"Forbidden model","title":"Forbidden","required":["error"],"properties":{"error":{"type":"string","description":"Forbidden message"},"sso_status":{"type":"integer","description":"Status code received from SSO"}}}}} \ No newline at end of file diff --git a/vendor/eveseat/eseye/tools/get_endpoints_and_scopes.php b/vendor/eveseat/eseye/tools/get_endpoints_and_scopes.php new file mode 100644 index 000000000..6809d1970 --- /dev/null +++ b/vendor/eveseat/eseye/tools/get_endpoints_and_scopes.php @@ -0,0 +1,57 @@ + [], + 'post' => [], + 'put' => [], + 'delete' => [], + 'patch' => [], +]; + +echo 'Mapping Scopes to endpoints and methods ..' . PHP_EOL; +foreach ($esi['paths'] as $path => $description) { + + foreach ($description as $method => $data) { + + if (isset($data['security'])) + $scope = $data['security'][0]['evesso'][0]; + else + $scope = 'public'; + + // Update the scope map! + $scope_map[$method][$path] = $scope; + echo $method . ' | ' . $path . ' | ' . $scope . PHP_EOL; + } +} + +// Output the scopes in a way that we can +// copy/pasta in code! +echo 'Var Export of the scope map:' . PHP_EOL; +echo PHP_EOL; +var_export($scope_map); +echo PHP_EOL; diff --git a/vendor/eveseat/eseye/tools/scopes.json b/vendor/eveseat/eseye/tools/scopes.json new file mode 100644 index 000000000..737051c6b --- /dev/null +++ b/vendor/eveseat/eseye/tools/scopes.json @@ -0,0 +1 @@ +{"get":{"\/alliances\/":"public","\/alliances\/names\/":"public","\/alliances\/{alliance_id}\/":"public","\/alliances\/{alliance_id}\/corporations\/":"public","\/alliances\/{alliance_id}\/icons\/":"public","\/characters\/names\/":"public","\/characters\/{character_id}\/":"public","\/characters\/{character_id}\/assets\/":"esi-assets.read_assets.v1","\/characters\/{character_id}\/bookmarks\/":"esi-bookmarks.read_character_bookmarks.v1","\/characters\/{character_id}\/bookmarks\/folders\/":"esi-bookmarks.read_character_bookmarks.v1","\/characters\/{character_id}\/calendar\/":"esi-calendar.read_calendar_events.v1","\/characters\/{character_id}\/calendar\/{event_id}\/":"esi-calendar.read_calendar_events.v1","\/characters\/{character_id}\/clones\/":"esi-clones.read_clones.v1","\/characters\/{character_id}\/contacts\/":"esi-characters.read_contacts.v1","\/characters\/{character_id}\/contacts\/labels\/":"esi-characters.read_contacts.v1","\/characters\/{character_id}\/corporationhistory\/":"public","\/characters\/{character_id}\/killmails\/recent\/":"esi-killmails.read_killmails.v1","\/characters\/{character_id}\/location\/":"esi-location.read_location.v1","\/characters\/{character_id}\/mail\/":"esi-mail.read_mail.v1","\/characters\/{character_id}\/mail\/labels\/":"esi-mail.read_mail.v1","\/characters\/{character_id}\/mail\/lists\/":"esi-mail.read_mail.v1","\/characters\/{character_id}\/mail\/{mail_id}\/":"esi-mail.read_mail.v1","\/characters\/{character_id}\/planets\/":"esi-planets.manage_planets.v1","\/characters\/{character_id}\/planets\/{planet_id}\/":"esi-planets.manage_planets.v1","\/characters\/{character_id}\/portrait\/":"public","\/characters\/{character_id}\/search\/":"esi-search.search_structures.v1","\/characters\/{character_id}\/ship\/":"esi-location.read_ship_type.v1","\/characters\/{character_id}\/skillqueue\/":"esi-skills.read_skillqueue.v1","\/characters\/{character_id}\/skills\/":"esi-skills.read_skills.v1","\/characters\/{character_id}\/wallets\/":"esi-wallet.read_character_wallet.v1","\/corporations\/names\/":"public","\/corporations\/{corporation_id}\/":"public","\/corporations\/{corporation_id}\/alliancehistory\/":"public","\/corporations\/{corporation_id}\/icons\/":"public","\/corporations\/{corporation_id}\/members\/":"esi-corporations.read_corporation_membership.v1","\/corporations\/{corporation_id}\/roles\/":"esi-corporations.read_corporation_membership.v1","\/fleets\/{fleet_id}\/":"esi-fleets.read_fleet.v1","\/fleets\/{fleet_id}\/members\/":"esi-fleets.read_fleet.v1","\/fleets\/{fleet_id}\/wings\/":"esi-fleets.read_fleet.v1","\/incursions\/":"public","\/industry\/facilities\/":"public","\/industry\/systems\/":"public","\/insurance\/prices\/":"public","\/killmails\/{killmail_id}\/{killmail_hash}\/":"public","\/markets\/prices\/":"public","\/markets\/{region_id}\/history\/":"public","\/markets\/{region_id}\/orders\/":"public","\/search\/":"public","\/sovereignty\/campaigns\/":"public","\/sovereignty\/structures\/":"public","\/universe\/schematics\/{schematic_id}\/":"public","\/universe\/stations\/{station_id}\/":"public","\/universe\/structures\/":"public","\/universe\/structures\/{structure_id}\/":"esi-universe.read_structures.v1","\/universe\/systems\/{system_id}\/":"public","\/universe\/types\/{type_id}\/":"public","\/wars\/":"public","\/wars\/{war_id}\/":"public","\/wars\/{war_id}\/killmails\/":"public"},"post":{"\/characters\/{character_id}\/contacts\/":"esi-characters.write_contacts.v1","\/characters\/{character_id}\/cspa\/":"esi-characters.read_contacts.v1","\/characters\/{character_id}\/mail\/":"esi-mail.send_mail.v1","\/characters\/{character_id}\/mail\/labels\/":"esi-mail.organize_mail.v1","\/fleets\/{fleet_id}\/members\/":"esi-fleets.write_fleet.v1","\/fleets\/{fleet_id}\/wings\/":"esi-fleets.write_fleet.v1","\/fleets\/{fleet_id}\/wings\/{wing_id}\/squads\/":"esi-fleets.write_fleet.v1","\/ui\/autopilot\/waypoint\/":"esi-ui.write_waypoint.v1","\/ui\/openwindow\/contract\/":"esi-ui.open_window.v1","\/ui\/openwindow\/information\/":"esi-ui.open_window.v1","\/ui\/openwindow\/marketdetails\/":"esi-ui.open_window.v1","\/ui\/openwindow\/newmail\/":"esi-ui.open_window.v1","\/universe\/names\/":"public"},"put":{"\/characters\/{character_id}\/calendar\/{event_id}\/":"esi-calendar.respond_calendar_events.v1","\/characters\/{character_id}\/contacts\/":"esi-characters.write_contacts.v1","\/characters\/{character_id}\/mail\/{mail_id}\/":"esi-mail.organize_mail.v1","\/fleets\/{fleet_id}\/":"esi-fleets.write_fleet.v1","\/fleets\/{fleet_id}\/members\/{member_id}\/":"esi-fleets.write_fleet.v1","\/fleets\/{fleet_id}\/squads\/{squad_id}\/":"esi-fleets.write_fleet.v1","\/fleets\/{fleet_id}\/wings\/{wing_id}\/":"esi-fleets.write_fleet.v1"},"delete":{"\/characters\/{character_id}\/contacts\/":"esi-characters.write_contacts.v1","\/characters\/{character_id}\/mail\/labels\/{label_id}\/":"esi-mail.organize_mail.v1","\/characters\/{character_id}\/mail\/{mail_id}\/":"esi-mail.organize_mail.v1","\/fleets\/{fleet_id}\/members\/{member_id}\/":"esi-fleets.write_fleet.v1","\/fleets\/{fleet_id}\/squads\/{squad_id}\/":"esi-fleets.write_fleet.v1","\/fleets\/{fleet_id}\/wings\/{wing_id}\/":"esi-fleets.write_fleet.v1"},"patch":[]} \ No newline at end of file diff --git a/vendor/eveseat/eseye/tools/swagger_download.php b/vendor/eveseat/eseye/tools/swagger_download.php new file mode 100644 index 000000000..8313ae511 --- /dev/null +++ b/vendor/eveseat/eseye/tools/swagger_download.php @@ -0,0 +1,28 @@ +request('get', 'https://esi.evetech.net/latest/swagger.json?datasource=tranquility', [ + 'sink' => 'esi.json', +]); diff --git a/vendor/fgrosse/phpasn1/CHANGELOG.md b/vendor/fgrosse/phpasn1/CHANGELOG.md new file mode 100644 index 000000000..25426e4c4 --- /dev/null +++ b/vendor/fgrosse/phpasn1/CHANGELOG.md @@ -0,0 +1,47 @@ +#### v2.2.0 (2020-08) +* support polyfills for bcmath and gmp, and add a composer.json + suggestion for the `phpseclib/bcmath_polyfill` for servers unable + to install PHP the gmp or bcmath extensions. + +#### v.2.1.1 & &v.2.0.2 (2018-12) +* add stricter validation around some structures, highlighed + by wycheproof test suite + +#### v.2.1.0 (2018-03) +* add support for `bcmath` extension (making `gmp` optional)
+ https://github.com/fgrosse/PHPASN1/pull/68 + +#### v.2.0.1 & v.1.5.3 (2017-12) +* add .gitattributes file to prevent examples and tests to be installed via composer when --prefer-dist was set + +#### v.2.0.0 (2017-08) +* rename `FG\ASN1\Object` to `FG\ASN1\ASNObject` because `Object` is a special class name in the next major PHP release + - when you upgrade you have to adapt all corresponding `use` and `extends` statements as well as type hints and all + usages of `Object::fromBinary(…)`. +* generally drop PHP 5.6 support + +#### v.1.5.2 (2016-10-29) +* allow empty octet strings + +#### v.1.5.1 (2015-10-02) +* add keywords to composer.json (this is a version on its own so the keywords are found on a stable version at packagist.org) + +#### v.1.5.0 (2015-10-30) +* fix a bug that would prevent you from decoding context specific tags on multiple objects [#57](https://github.com/fgrosse/PHPASN1/issues/57) + - `ExplicitlyTaggedObject::__construct` does now accept multiple objects to be tagged with a single tag + - `ExplicitlyTaggedObject::getContent` will now always return an array (even if only one object is tagged) + +#### v.1.4.2 (2015-09-29) +* fix a bug that would prevent you from decoding empty tagged objects [#57](https://github.com/fgrosse/PHPASN1/issues/57) + +#### v.1.4.1 +* improve exception messages and general error handling [#55](https://github.com/fgrosse/PHPASN1/pull/55) + +#### v.1.4.0 +* **require PHP 5.6** +* support big integers (closes #1 and #37) +* enforce one code style via [styleci.io][9] +* track code coverage via [coveralls.io][10] +* replace obsolete `FG\ASN1\Exception\GeneralException` with `\Exception` +* `Construct` (`Sequence`, `Set`) does now implement `ArrayAccess`, `Countable` and `Iterator` so its easier to use +* add [`TemplateParser`][11] diff --git a/vendor/fgrosse/phpasn1/LICENSE b/vendor/fgrosse/phpasn1/LICENSE new file mode 100644 index 000000000..1e17eb03e --- /dev/null +++ b/vendor/fgrosse/phpasn1/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2012-2015 Friedrich Große + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/fgrosse/phpasn1/README.md b/vendor/fgrosse/phpasn1/README.md new file mode 100644 index 000000000..3931010ab --- /dev/null +++ b/vendor/fgrosse/phpasn1/README.md @@ -0,0 +1,167 @@ +PHPASN1 +======= + +[![Build Status](https://secure.travis-ci.org/fgrosse/PHPASN1.png?branch=master)](http://travis-ci.org/fgrosse/PHPASN1) +[![PHP 7 ready](http://php7ready.timesplinter.ch/fgrosse/PHPASN1/badge.svg)](https://travis-ci.org/fgrosse/PHPASN1) +[![Coverage Status](https://coveralls.io/repos/fgrosse/PHPASN1/badge.svg?branch=master&service=github)](https://coveralls.io/github/fgrosse/PHPASN1?branch=master) + +[![Latest Stable Version](https://poser.pugx.org/fgrosse/phpasn1/v/stable.png)](https://packagist.org/packages/fgrosse/phpasn1) +[![Total Downloads](https://poser.pugx.org/fgrosse/phpasn1/downloads.png)](https://packagist.org/packages/fgrosse/phpasn1) +[![Latest Unstable Version](https://poser.pugx.org/fgrosse/phpasn1/v/unstable.png)](https://packagist.org/packages/fgrosse/phpasn1) +[![License](https://poser.pugx.org/fgrosse/phpasn1/license.png)](https://packagist.org/packages/fgrosse/phpasn1) + +A PHP Framework that allows you to encode and decode arbitrary [ASN.1][3] structures +using the [ITU-T X.690 Encoding Rules][4]. +This encoding is very frequently used in [X.509 PKI environments][5] or the communication between heterogeneous computer systems. + +The API allows you to encode ASN.1 structures to create binary data such as certificate +signing requests (CSR), X.509 certificates or certificate revocation lists (CRL). +PHPASN1 can also read [BER encoded][6] binary data into separate PHP objects that can be manipulated by the user and reencoded afterwards. + +The **changelog** can now be found at [CHANGELOG.md](CHANGELOG.md). + +## Dependencies + +PHPASN1 requires at least `PHP 7.0` and either the `gmp` or `bcmath` extension. +Support for older PHP versions (i.e. PHP 5.6) was dropped starting with `v2.0`. +If you must use an outdated PHP version consider using [PHPASN v1.5][13]. + +For the loading of object identifier names directly from the web [curl][7] is used. + +## Installation + +The preferred way to install this library is to rely on [Composer][2]: + +```bash +$ composer require fgrosse/phpasn1 +``` + +## Usage + +### Encoding ASN.1 Structures + +PHPASN1 offers you a class for each of the implemented ASN.1 universal types. +The constructors should be pretty self explanatory so you should have no big trouble getting started. +All data will be encoded using [DER encoding][8] + +```php +use FG\ASN1\OID; +use FG\ASN1\Universal\Integer; +use FG\ASN1\Universal\Boolean; +use FG\ASN1\Universal\Enumerated; +use FG\ASN1\Universal\IA5String; +use FG\ASN1\Universal\ObjectIdentifier; +use FG\ASN1\Universal\PrintableString; +use FG\ASN1\Universal\Sequence; +use FG\ASN1\Universal\Set; +use FG\ASN1\Universal\NullObject; + +$integer = new Integer(123456); +$boolean = new Boolean(true); +$enum = new Enumerated(1); +$ia5String = new IA5String('Hello world'); + +$asnNull = new NullObject(); +$objectIdentifier1 = new ObjectIdentifier('1.2.250.1.16.9'); +$objectIdentifier2 = new ObjectIdentifier(OID::RSA_ENCRYPTION); +$printableString = new PrintableString('Foo bar'); + +$sequence = new Sequence($integer, $boolean, $enum, $ia5String); +$set = new Set($sequence, $asnNull, $objectIdentifier1, $objectIdentifier2, $printableString); + +$myBinary = $sequence->getBinary(); +$myBinary .= $set->getBinary(); + +echo base64_encode($myBinary); +``` + + +### Decoding binary data + +Decoding BER encoded binary data is just as easy as encoding it: + +```php +use FG\ASN1\ASNObject; + +$base64String = ... +$binaryData = base64_decode($base64String); +$asnObject = ASNObject::fromBinary($binaryData); + + +// do stuff +``` + +If you already know exactly how your expected data should look like you can use the `FG\ASN1\TemplateParser`: + +```php +use FG\ASN1\TemplateParser; + +// first define your template +$template = [ + Identifier::SEQUENCE => [ + Identifier::SET => [ + Identifier::OBJECT_IDENTIFIER, + Identifier::SEQUENCE => [ + Identifier::INTEGER, + Identifier::BITSTRING, + ] + ] + ] +]; + +// if your binary data is not matching the template you provided this will throw an `\Exception`: +$parser = new TemplateParser(); +$object = $parser->parseBinary($data, $template); + +// there is also a convenience function if you parse binary data from base64: +$object = $parser->parseBase64($data, $template); +``` + +You can use this function to make sure your data has exactly the format you are expecting. + +### Navigating decoded data + +All constructed classes (i.e. `Sequence` and `Set`) can be navigated by array access or using an iterator. +You can find examples +[here](https://github.com/fgrosse/PHPASN1/blob/f6442cadda9d36f3518c737e32f28300a588b777/tests/ASN1/Universal/SequenceTest.php#L148-148), +[here](https://github.com/fgrosse/PHPASN1/blob/f6442cadda9d36f3518c737e32f28300a588b777/tests/ASN1/Universal/SequenceTest.php#L121) and +[here](https://github.com/fgrosse/PHPASN1/blob/f6442cadda9d36f3518c737e32f28300a588b777/tests/ASN1/TemplateParserTest.php#L45). + + +### Give me more examples! + +To see some example usage of the API classes or some generated output check out the [examples](https://github.com/fgrosse/PHPASN1/tree/master/examples). + + +### How do I contribute? + +If you found an issue or have a question submit a github issue with detailed information. + +In case you already know what caused the issue and feel in the mood to fix it, your code contributions are always welcome. Just fork the repository, implement your changes and make sure that you covered everything with tests. +Afterwards submit a pull request via github and be a little patient :) I usually try to comment and/or merge as soon as possible. + +#### Mailing list + +New features or questions can be discussed in [this google group/mailing list][12]. + +### Thanks + +To [all contributors][1] so far! + +## License + +This library is distributed under the [MIT License](LICENSE). + +[1]: https://github.com/fgrosse/PHPASN1/graphs/contributors +[2]: https://getcomposer.org/ +[3]: http://www.itu.int/ITU-T/asn1/ +[4]: http://www.itu.int/ITU-T/recommendations/rec.aspx?rec=x.690 +[5]: http://en.wikipedia.org/wiki/X.509 +[6]: http://en.wikipedia.org/wiki/X.690#BER_encoding +[7]: http://php.net/manual/en/book.curl.php +[8]: http://en.wikipedia.org/wiki/X.690#DER_encoding +[9]: https://styleci.io +[10]: https://coveralls.io/github/fgrosse/PHPASN1 +[11]: https://github.com/fgrosse/PHPASN1/blob/master/tests/ASN1/TemplateParserTest.php#L16 +[12]: https://groups.google.com/d/forum/phpasn1 +[13]: https://packagist.org/packages/fgrosse/phpasn1#1.5.2 diff --git a/vendor/fgrosse/phpasn1/composer.json b/vendor/fgrosse/phpasn1/composer.json new file mode 100644 index 000000000..01ffadaed --- /dev/null +++ b/vendor/fgrosse/phpasn1/composer.json @@ -0,0 +1,49 @@ +{ + "name": "fgrosse/phpasn1", + "description": "A PHP Framework that allows you to encode and decode arbitrary ASN.1 structures using the ITU-T X.690 Encoding Rules.", + "type": "library", + "homepage": "https://github.com/FGrosse/PHPASN1", + "license": "MIT", + "authors": [ + { + "name": "Friedrich Große", + "email": "friedrich.grosse@gmail.com", + "homepage": "https://github.com/FGrosse", + "role": "Author" + }, + { + "name": "All contributors", + "homepage": "https://github.com/FGrosse/PHPASN1/contributors" + } + ], + "keywords": [ "x690", "x.690", "x.509", "x509", "asn1", "asn.1", "ber", "der", "binary", "encoding", "decoding" ], + + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": "~6.3", + "satooshi/php-coveralls": "~2.0" + }, + "suggest": { + "ext-gmp": "GMP is the preferred extension for big integer calculations", + "ext-bcmath": "BCmath is the fallback extension for big integer calculations", + "phpseclib/bcmath_compat": "BCmath polyfill for servers where neither GMP nor BCmath is available", + "ext-curl": "For loading OID information from the web if they have not bee defined statically" + }, + "autoload": { + "psr-4": { + "FG\\": "lib/" + } + }, + "autoload-dev": { + "psr-4": { + "FG\\Test\\": "tests/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/ASNObject.php b/vendor/fgrosse/phpasn1/lib/ASN1/ASNObject.php new file mode 100644 index 000000000..3b7f16215 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/ASNObject.php @@ -0,0 +1,355 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1; + +use FG\ASN1\Exception\ParserException; +use FG\ASN1\Universal\BitString; +use FG\ASN1\Universal\Boolean; +use FG\ASN1\Universal\Enumerated; +use FG\ASN1\Universal\GeneralizedTime; +use FG\ASN1\Universal\Integer; +use FG\ASN1\Universal\NullObject; +use FG\ASN1\Universal\ObjectIdentifier; +use FG\ASN1\Universal\RelativeObjectIdentifier; +use FG\ASN1\Universal\OctetString; +use FG\ASN1\Universal\Sequence; +use FG\ASN1\Universal\Set; +use FG\ASN1\Universal\UTCTime; +use FG\ASN1\Universal\IA5String; +use FG\ASN1\Universal\PrintableString; +use FG\ASN1\Universal\NumericString; +use FG\ASN1\Universal\UTF8String; +use FG\ASN1\Universal\UniversalString; +use FG\ASN1\Universal\CharacterString; +use FG\ASN1\Universal\GeneralString; +use FG\ASN1\Universal\VisibleString; +use FG\ASN1\Universal\GraphicString; +use FG\ASN1\Universal\BMPString; +use FG\ASN1\Universal\T61String; +use FG\ASN1\Universal\ObjectDescriptor; +use FG\Utility\BigInteger; +use LogicException; + +/** + * Class ASNObject is the base class for all concrete ASN.1 objects. + */ +abstract class ASNObject implements Parsable +{ + private $contentLength; + private $nrOfLengthOctets; + + /** + * Must return the number of octets of the content part. + * + * @return int + */ + abstract protected function calculateContentLength(); + + /** + * Encode the object using DER encoding. + * + * @see http://en.wikipedia.org/wiki/X.690#DER_encoding + * + * @return string the binary representation of an objects value + */ + abstract protected function getEncodedValue(); + + /** + * Return the content of this object in a non encoded form. + * This can be used to print the value in human readable form. + * + * @return mixed + */ + abstract public function getContent(); + + /** + * Return the object type octet. + * This should use the class constants of Identifier. + * + * @see Identifier + * + * @return int + */ + abstract public function getType(); + + /** + * Returns all identifier octets. If an inheriting class models a tag with + * the long form identifier format, it MUST reimplement this method to + * return all octets of the identifier. + * + * @throws LogicException If the identifier format is long form + * + * @return string Identifier as a set of octets + */ + public function getIdentifier() + { + $firstOctet = $this->getType(); + + if (Identifier::isLongForm($firstOctet)) { + throw new LogicException(sprintf('Identifier of %s uses the long form and must therefor override "ASNObject::getIdentifier()".', get_class($this))); + } + + return chr($firstOctet); + } + + /** + * Encode this object using DER encoding. + * + * @return string the full binary representation of the complete object + */ + public function getBinary() + { + $result = $this->getIdentifier(); + $result .= $this->createLengthPart(); + $result .= $this->getEncodedValue(); + + return $result; + } + + private function createLengthPart() + { + $contentLength = $this->getContentLength(); + $nrOfLengthOctets = $this->getNumberOfLengthOctets($contentLength); + + if ($nrOfLengthOctets == 1) { + return chr($contentLength); + } else { + // the first length octet determines the number subsequent length octets + $lengthOctets = chr(0x80 | ($nrOfLengthOctets - 1)); + for ($shiftLength = 8 * ($nrOfLengthOctets - 2); $shiftLength >= 0; $shiftLength -= 8) { + $lengthOctets .= chr($contentLength >> $shiftLength); + } + + return $lengthOctets; + } + } + + protected function getNumberOfLengthOctets($contentLength = null) + { + if (!isset($this->nrOfLengthOctets)) { + if ($contentLength == null) { + $contentLength = $this->getContentLength(); + } + + $this->nrOfLengthOctets = 1; + if ($contentLength > 127) { + do { // long form + $this->nrOfLengthOctets++; + $contentLength = $contentLength >> 8; + } while ($contentLength > 0); + } + } + + return $this->nrOfLengthOctets; + } + + protected function getContentLength() + { + if (!isset($this->contentLength)) { + $this->contentLength = $this->calculateContentLength(); + } + + return $this->contentLength; + } + + protected function setContentLength($newContentLength) + { + $this->contentLength = $newContentLength; + $this->getNumberOfLengthOctets($newContentLength); + } + + /** + * Returns the length of the whole object (including the identifier and length octets). + */ + public function getObjectLength() + { + $nrOfIdentifierOctets = strlen($this->getIdentifier()); + $contentLength = $this->getContentLength(); + $nrOfLengthOctets = $this->getNumberOfLengthOctets($contentLength); + + return $nrOfIdentifierOctets + $nrOfLengthOctets + $contentLength; + } + + public function __toString() + { + return $this->getContent(); + } + + /** + * Returns the name of the ASN.1 Type of this object. + * + * @see Identifier::getName() + */ + public function getTypeName() + { + return Identifier::getName($this->getType()); + } + + /** + * @param string $binaryData + * @param int $offsetIndex + * + * @throws ParserException + * + * @return \FG\ASN1\ASNObject + */ + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + if (strlen($binaryData) <= $offsetIndex) { + throw new ParserException('Can not parse binary from data: Offset index larger than input size', $offsetIndex); + } + + $identifierOctet = ord($binaryData[$offsetIndex]); + if (Identifier::isContextSpecificClass($identifierOctet) && Identifier::isConstructed($identifierOctet)) { + return ExplicitlyTaggedObject::fromBinary($binaryData, $offsetIndex); + } + + switch ($identifierOctet) { + case Identifier::BITSTRING: + return BitString::fromBinary($binaryData, $offsetIndex); + case Identifier::BOOLEAN: + return Boolean::fromBinary($binaryData, $offsetIndex); + case Identifier::ENUMERATED: + return Enumerated::fromBinary($binaryData, $offsetIndex); + case Identifier::INTEGER: + return Integer::fromBinary($binaryData, $offsetIndex); + case Identifier::NULL: + return NullObject::fromBinary($binaryData, $offsetIndex); + case Identifier::OBJECT_IDENTIFIER: + return ObjectIdentifier::fromBinary($binaryData, $offsetIndex); + case Identifier::RELATIVE_OID: + return RelativeObjectIdentifier::fromBinary($binaryData, $offsetIndex); + case Identifier::OCTETSTRING: + return OctetString::fromBinary($binaryData, $offsetIndex); + case Identifier::SEQUENCE: + return Sequence::fromBinary($binaryData, $offsetIndex); + case Identifier::SET: + return Set::fromBinary($binaryData, $offsetIndex); + case Identifier::UTC_TIME: + return UTCTime::fromBinary($binaryData, $offsetIndex); + case Identifier::GENERALIZED_TIME: + return GeneralizedTime::fromBinary($binaryData, $offsetIndex); + case Identifier::IA5_STRING: + return IA5String::fromBinary($binaryData, $offsetIndex); + case Identifier::PRINTABLE_STRING: + return PrintableString::fromBinary($binaryData, $offsetIndex); + case Identifier::NUMERIC_STRING: + return NumericString::fromBinary($binaryData, $offsetIndex); + case Identifier::UTF8_STRING: + return UTF8String::fromBinary($binaryData, $offsetIndex); + case Identifier::UNIVERSAL_STRING: + return UniversalString::fromBinary($binaryData, $offsetIndex); + case Identifier::CHARACTER_STRING: + return CharacterString::fromBinary($binaryData, $offsetIndex); + case Identifier::GENERAL_STRING: + return GeneralString::fromBinary($binaryData, $offsetIndex); + case Identifier::VISIBLE_STRING: + return VisibleString::fromBinary($binaryData, $offsetIndex); + case Identifier::GRAPHIC_STRING: + return GraphicString::fromBinary($binaryData, $offsetIndex); + case Identifier::BMP_STRING: + return BMPString::fromBinary($binaryData, $offsetIndex); + case Identifier::T61_STRING: + return T61String::fromBinary($binaryData, $offsetIndex); + case Identifier::OBJECT_DESCRIPTOR: + return ObjectDescriptor::fromBinary($binaryData, $offsetIndex); + default: + // At this point the identifier may be >1 byte. + if (Identifier::isConstructed($identifierOctet)) { + return new UnknownConstructedObject($binaryData, $offsetIndex); + } else { + $identifier = self::parseBinaryIdentifier($binaryData, $offsetIndex); + $lengthOfUnknownObject = self::parseContentLength($binaryData, $offsetIndex); + $offsetIndex += $lengthOfUnknownObject; + + return new UnknownObject($identifier, $lengthOfUnknownObject); + } + } + } + + protected static function parseIdentifier($identifierOctet, $expectedIdentifier, $offsetForExceptionHandling) + { + if (is_string($identifierOctet) || is_numeric($identifierOctet) == false) { + $identifierOctet = ord($identifierOctet); + } + + if ($identifierOctet != $expectedIdentifier) { + $message = 'Can not create an '.Identifier::getName($expectedIdentifier).' from an '.Identifier::getName($identifierOctet); + throw new ParserException($message, $offsetForExceptionHandling); + } + } + + protected static function parseBinaryIdentifier($binaryData, &$offsetIndex) + { + if (strlen($binaryData) <= $offsetIndex) { + throw new ParserException('Can not parse identifier from data: Offset index larger than input size', $offsetIndex); + } + + $identifier = $binaryData[$offsetIndex++]; + + if (Identifier::isLongForm(ord($identifier)) == false) { + return $identifier; + } + + while (true) { + if (strlen($binaryData) <= $offsetIndex) { + throw new ParserException('Can not parse identifier (long form) from data: Offset index larger than input size', $offsetIndex); + } + $nextOctet = $binaryData[$offsetIndex++]; + $identifier .= $nextOctet; + + if ((ord($nextOctet) & 0x80) === 0) { + // the most significant bit is 0 to we have reached the end of the identifier + break; + } + } + + return $identifier; + } + + protected static function parseContentLength(&$binaryData, &$offsetIndex, $minimumLength = 0) + { + if (strlen($binaryData) <= $offsetIndex) { + throw new ParserException('Can not parse content length from data: Offset index larger than input size', $offsetIndex); + } + + $contentLength = ord($binaryData[$offsetIndex++]); + if (($contentLength & 0x80) != 0) { + // bit 8 is set -> this is the long form + $nrOfLengthOctets = $contentLength & 0x7F; + $contentLength = BigInteger::create(0x00); + for ($i = 0; $i < $nrOfLengthOctets; $i++) { + if (strlen($binaryData) <= $offsetIndex) { + throw new ParserException('Can not parse content length (long form) from data: Offset index larger than input size', $offsetIndex); + } + $contentLength = $contentLength->shiftLeft(8)->add(ord($binaryData[$offsetIndex++])); + } + + if ($contentLength->compare(PHP_INT_MAX) > 0) { + throw new ParserException("Can not parse content length from data: length > maximum integer", $offsetIndex); + } + + $contentLength = $contentLength->toInteger(); + } + + if ($contentLength < $minimumLength) { + throw new ParserException('A '.get_called_class()." should have a content length of at least {$minimumLength}. Extracted length was {$contentLength}", $offsetIndex); + } + + $lenDataRemaining = strlen($binaryData) - $offsetIndex; + + if ($lenDataRemaining < $contentLength) { + throw new ParserException("Content length {$contentLength} exceeds remaining data length {$lenDataRemaining}", $offsetIndex); + } + + return $contentLength; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/AbstractString.php b/vendor/fgrosse/phpasn1/lib/ASN1/AbstractString.php new file mode 100644 index 000000000..7e0d7ddbb --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/AbstractString.php @@ -0,0 +1,136 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1; + +use Exception; + +abstract class AbstractString extends ASNObject implements Parsable +{ + /** @var string */ + protected $value; + private $checkStringForIllegalChars = true; + private $allowedCharacters = []; + + /** + * The abstract base class for ASN.1 classes which represent some string of character. + * + * @param string $string + */ + public function __construct($string) + { + $this->value = $string; + } + + public function getContent() + { + return $this->value; + } + + protected function allowCharacter($character) + { + $this->allowedCharacters[] = $character; + } + + protected function allowCharacters(...$characters) + { + foreach ($characters as $character) { + $this->allowedCharacters[] = $character; + } + } + + protected function allowNumbers() + { + foreach (range('0', '9') as $char) { + $this->allowedCharacters[] = (string) $char; + } + } + + protected function allowAllLetters() + { + $this->allowSmallLetters(); + $this->allowCapitalLetters(); + } + + protected function allowSmallLetters() + { + foreach (range('a', 'z') as $char) { + $this->allowedCharacters[] = $char; + } + } + + protected function allowCapitalLetters() + { + foreach (range('A', 'Z') as $char) { + $this->allowedCharacters[] = $char; + } + } + + protected function allowSpaces() + { + $this->allowedCharacters[] = ' '; + } + + protected function allowAll() + { + $this->checkStringForIllegalChars = false; + } + + protected function calculateContentLength() + { + return strlen($this->value); + } + + protected function getEncodedValue() + { + if ($this->checkStringForIllegalChars) { + $this->checkString(); + } + + return $this->value; + } + + protected function checkString() + { + $stringLength = $this->getContentLength(); + for ($i = 0; $i < $stringLength; $i++) { + if (in_array($this->value[$i], $this->allowedCharacters) == false) { + $typeName = Identifier::getName($this->getType()); + throw new Exception("Could not create a {$typeName} from the character sequence '{$this->value}'."); + } + } + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + $parsedObject = new static(''); + + self::parseIdentifier($binaryData[$offsetIndex], $parsedObject->getType(), $offsetIndex++); + $contentLength = self::parseContentLength($binaryData, $offsetIndex); + $string = substr($binaryData, $offsetIndex, $contentLength); + $offsetIndex += $contentLength; + + $parsedObject->value = $string; + $parsedObject->setContentLength($contentLength); + return $parsedObject; + } + + public static function isValid($string) + { + $testObject = new static($string); + try { + $testObject->checkString(); + + return true; + } catch (Exception $exception) { + return false; + } + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/AbstractTime.php b/vendor/fgrosse/phpasn1/lib/ASN1/AbstractTime.php new file mode 100644 index 000000000..9fad50516 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/AbstractTime.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1; + +use DateInterval; +use DateTime; +use DateTimeZone; +use Exception; + +abstract class AbstractTime extends ASNObject +{ + /** @var DateTime */ + protected $value; + + public function __construct($dateTime = null, $dateTimeZone = 'UTC') + { + if ($dateTime == null || is_string($dateTime)) { + $timeZone = new DateTimeZone($dateTimeZone); + $dateTimeObject = new DateTime($dateTime, $timeZone); + if ($dateTimeObject == false) { + $errorMessage = $this->getLastDateTimeErrors(); + $className = Identifier::getName($this->getType()); + throw new Exception(sprintf("Could not create %s from date time string '%s': %s", $className, $dateTime, $errorMessage)); + } + $dateTime = $dateTimeObject; + } elseif (!$dateTime instanceof DateTime) { + throw new Exception('Invalid first argument for some instance of AbstractTime constructor'); + } + + $this->value = $dateTime; + } + + public function getContent() + { + return $this->value; + } + + protected function getLastDateTimeErrors() + { + $messages = ''; + $lastErrors = DateTime::getLastErrors(); + foreach ($lastErrors['errors'] as $errorMessage) { + $messages .= "{$errorMessage}, "; + } + + return substr($messages, 0, -2); + } + + public function __toString() + { + return $this->value->format("Y-m-d\tH:i:s"); + } + + protected static function extractTimeZoneData(&$binaryData, &$offsetIndex, DateTime $dateTime) + { + $sign = $binaryData[$offsetIndex++]; + $timeOffsetHours = intval(substr($binaryData, $offsetIndex, 2)); + $timeOffsetMinutes = intval(substr($binaryData, $offsetIndex + 2, 2)); + $offsetIndex += 4; + + $interval = new DateInterval("PT{$timeOffsetHours}H{$timeOffsetMinutes}M"); + if ($sign == '+') { + $dateTime->sub($interval); + } else { + $dateTime->add($interval); + } + + return $dateTime; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Base128.php b/vendor/fgrosse/phpasn1/lib/ASN1/Base128.php new file mode 100644 index 000000000..119ee7b9c --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Base128.php @@ -0,0 +1,63 @@ +modulus(0x80)->toInteger()); + + $value = $value->shiftRight(7); + while ($value->compare(0) > 0) { + $octets .= chr(0x80 | $value->modulus(0x80)->toInteger()); + $value = $value->shiftRight(7); + } + + return strrev($octets); + } + + /** + * @param string $octets + * + * @throws InvalidArgumentException if the given octets represent a malformed base-128 value or the decoded value would exceed the the maximum integer length + * + * @return int + */ + public static function decode($octets) + { + $bitsPerOctet = 7; + $value = BigInteger::create(0); + $i = 0; + + while (true) { + if (!isset($octets[$i])) { + throw new InvalidArgumentException(sprintf('Malformed base-128 encoded value (0x%s).', strtoupper(bin2hex($octets)) ?: '0')); + } + + $octet = ord($octets[$i++]); + + $l1 = $value->shiftLeft($bitsPerOctet); + $r1 = $octet & 0x7f; + $value = $l1->add($r1); + + if (0 === ($octet & 0x80)) { + break; + } + } + + return (string)$value; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Composite/AttributeTypeAndValue.php b/vendor/fgrosse/phpasn1/lib/ASN1/Composite/AttributeTypeAndValue.php new file mode 100644 index 000000000..3f4027c25 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Composite/AttributeTypeAndValue.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Composite; + +use FG\ASN1\ASNObject; +use FG\ASN1\Universal\Sequence; +use FG\ASN1\Universal\ObjectIdentifier; + +class AttributeTypeAndValue extends Sequence +{ + /** + * @param ObjectIdentifier|string $objIdentifier + * @param \FG\ASN1\ASNObject $value + */ + public function __construct($objIdentifier, ASNObject $value) + { + if ($objIdentifier instanceof ObjectIdentifier == false) { + $objIdentifier = new ObjectIdentifier($objIdentifier); + } + parent::__construct($objIdentifier, $value); + } + + public function __toString() + { + return $this->children[0].': '.$this->children[1]; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Composite/RDNString.php b/vendor/fgrosse/phpasn1/lib/ASN1/Composite/RDNString.php new file mode 100644 index 000000000..e95e7acd3 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Composite/RDNString.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Composite; + +use FG\ASN1\Universal\PrintableString; +use FG\ASN1\Universal\IA5String; +use FG\ASN1\Universal\UTF8String; + +class RDNString extends RelativeDistinguishedName +{ + /** + * @param string|\FG\ASN1\Universal\ObjectIdentifier $objectIdentifierString + * @param string|\FG\ASN1\ASNObject $value + */ + public function __construct($objectIdentifierString, $value) + { + if (PrintableString::isValid($value)) { + $value = new PrintableString($value); + } else { + if (IA5String::isValid($value)) { + $value = new IA5String($value); + } else { + $value = new UTF8String($value); + } + } + + parent::__construct($objectIdentifierString, $value); + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Composite/RelativeDistinguishedName.php b/vendor/fgrosse/phpasn1/lib/ASN1/Composite/RelativeDistinguishedName.php new file mode 100644 index 000000000..4185f41a1 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Composite/RelativeDistinguishedName.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Composite; + +use FG\ASN1\Exception\NotImplementedException; +use FG\ASN1\ASNObject; +use FG\ASN1\Universal\Set; + +class RelativeDistinguishedName extends Set +{ + /** + * @param string|\FG\ASN1\Universal\ObjectIdentifier $objIdentifierString + * @param \FG\ASN1\ASNObject $value + */ + public function __construct($objIdentifierString, ASNObject $value) + { + // TODO: This does only support one element in the RelativeDistinguishedName Set but it it is defined as follows: + // RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue + parent::__construct(new AttributeTypeAndValue($objIdentifierString, $value)); + } + + public function getContent() + { + /** @var \FG\ASN1\ASNObject $firstObject */ + $firstObject = $this->children[0]; + return $firstObject->__toString(); + } + + /** + * At the current version this code can not work since the implementation of Construct requires + * the class to support a constructor without arguments. + * + * @deprecated this function is not yet implemented! Feel free to submit a pull request on github + * @param string $binaryData + * @param int $offsetIndex + * @throws NotImplementedException + */ + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + throw new NotImplementedException(); + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Construct.php b/vendor/fgrosse/phpasn1/lib/ASN1/Construct.php new file mode 100644 index 000000000..58f961375 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Construct.php @@ -0,0 +1,191 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1; + +use ArrayAccess; +use ArrayIterator; +use Countable; +use FG\ASN1\Exception\ParserException; +use Iterator; + +abstract class Construct extends ASNObject implements Countable, ArrayAccess, Iterator, Parsable +{ + /** @var \FG\ASN1\ASNObject[] */ + protected $children; + private $iteratorPosition; + + /** + * @param \FG\ASN1\ASNObject[] $children the variadic type hint is commented due to https://github.com/facebook/hhvm/issues/4858 + */ + public function __construct(/* HH_FIXME[4858]: variadic + strict */ ...$children) + { + $this->children = $children; + $this->iteratorPosition = 0; + } + + public function getContent() + { + return $this->children; + } + + public function rewind() + { + $this->iteratorPosition = 0; + } + + public function current() + { + return $this->children[$this->iteratorPosition]; + } + + public function key() + { + return $this->iteratorPosition; + } + + public function next() + { + $this->iteratorPosition++; + } + + public function valid() + { + return isset($this->children[$this->iteratorPosition]); + } + + public function offsetExists($offset) + { + return array_key_exists($offset, $this->children); + } + + public function offsetGet($offset) + { + return $this->children[$offset]; + } + + public function offsetSet($offset, $value) + { + if ($offset === null) { + $offset = count($this->children); + } + + $this->children[$offset] = $value; + } + + public function offsetUnset($offset) + { + unset($this->children[$offset]); + } + + protected function calculateContentLength() + { + $length = 0; + foreach ($this->children as $component) { + $length += $component->getObjectLength(); + } + + return $length; + } + + protected function getEncodedValue() + { + $result = ''; + foreach ($this->children as $component) { + $result .= $component->getBinary(); + } + + return $result; + } + + public function addChild(ASNObject $child) + { + $this->children[] = $child; + } + + public function addChildren(array $children) + { + foreach ($children as $child) { + $this->addChild($child); + } + } + + public function __toString() + { + $nrOfChildren = $this->getNumberOfChildren(); + $childString = $nrOfChildren == 1 ? 'child' : 'children'; + + return "[{$nrOfChildren} {$childString}]"; + } + + public function getNumberOfChildren() + { + return count($this->children); + } + + /** + * @return \FG\ASN1\ASNObject[] + */ + public function getChildren() + { + return $this->children; + } + + /** + * @return \FG\ASN1\ASNObject + */ + public function getFirstChild() + { + return $this->children[0]; + } + + /** + * @param string $binaryData + * @param int $offsetIndex + * + * @throws Exception\ParserException + * + * @return Construct|static + */ + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + $parsedObject = new static(); + self::parseIdentifier($binaryData[$offsetIndex], $parsedObject->getType(), $offsetIndex++); + $contentLength = self::parseContentLength($binaryData, $offsetIndex); + $startIndex = $offsetIndex; + + $children = []; + $octetsToRead = $contentLength; + while ($octetsToRead > 0) { + $newChild = ASNObject::fromBinary($binaryData, $offsetIndex); + $octetsToRead -= $newChild->getObjectLength(); + $children[] = $newChild; + } + + if ($octetsToRead !== 0) { + throw new ParserException("Sequence length incorrect", $startIndex); + } + + $parsedObject->addChildren($children); + $parsedObject->setContentLength($contentLength); + + return $parsedObject; + } + + public function count($mode = COUNT_NORMAL) + { + return count($this->children, $mode); + } + + public function getIterator() + { + return new ArrayIterator($this->children); + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Exception/NotImplementedException.php b/vendor/fgrosse/phpasn1/lib/ASN1/Exception/NotImplementedException.php new file mode 100644 index 000000000..c9f8e82e6 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Exception/NotImplementedException.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Exception; + +class NotImplementedException extends \Exception +{ +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Exception/ParserException.php b/vendor/fgrosse/phpasn1/lib/ASN1/Exception/ParserException.php new file mode 100644 index 000000000..4bda4e872 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Exception/ParserException.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Exception; + +class ParserException extends \Exception +{ + private $errorMessage; + private $offset; + + public function __construct($errorMessage, $offset) + { + $this->errorMessage = $errorMessage; + $this->offset = $offset; + parent::__construct("ASN.1 Parser Exception at offset {$this->offset}: {$this->errorMessage}"); + } + + public function getOffset() + { + return $this->offset; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/ExplicitlyTaggedObject.php b/vendor/fgrosse/phpasn1/lib/ASN1/ExplicitlyTaggedObject.php new file mode 100644 index 000000000..b947a9599 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/ExplicitlyTaggedObject.php @@ -0,0 +1,131 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1; + +use FG\ASN1\Exception\ParserException; + +/** + * Class ExplicitlyTaggedObject decorate an inner object with an additional tag that gives information about + * its context specific meaning. + * + * Explanation taken from A Layman's Guide to a Subset of ASN.1, BER, and DER: + * >>> An RSA Laboratories Technical Note + * >>> Burton S. Kaliski Jr. + * >>> Revised November 1, 1993 + * + * [...] + * Explicitly tagged types are derived from other types by adding an outer tag to the underlying type. + * In effect, explicitly tagged types are structured types consisting of one component, the underlying type. + * Explicit tagging is denoted by the ASN.1 keywords [class number] EXPLICIT (see Section 5.2). + * [...] + * + * @see http://luca.ntop.org/Teaching/Appunti/asn1.html + */ +class ExplicitlyTaggedObject extends ASNObject +{ + /** @var \FG\ASN1\ASNObject[] */ + private $decoratedObjects; + private $tag; + + /** + * @param int $tag + * @param \FG\ASN1\ASNObject $objects,... + */ + public function __construct($tag, /* HH_FIXME[4858]: variadic + strict */ ...$objects) + { + $this->tag = $tag; + $this->decoratedObjects = $objects; + } + + protected function calculateContentLength() + { + $length = 0; + foreach ($this->decoratedObjects as $object) { + $length += $object->getObjectLength(); + } + + return $length; + } + + protected function getEncodedValue() + { + $encoded = ''; + foreach ($this->decoratedObjects as $object) { + $encoded .= $object->getBinary(); + } + + return $encoded; + } + + public function getContent() + { + return $this->decoratedObjects; + } + + public function __toString() + { + switch ($length = count($this->decoratedObjects)) { + case 0: + return "Context specific empty object with tag [{$this->tag}]"; + case 1: + $decoratedType = Identifier::getShortName($this->decoratedObjects[0]->getType()); + return "Context specific $decoratedType with tag [{$this->tag}]"; + default: + return "$length context specific objects with tag [{$this->tag}]"; + } + } + + public function getType() + { + return ord($this->getIdentifier()); + } + + public function getIdentifier() + { + $identifier = Identifier::create(Identifier::CLASS_CONTEXT_SPECIFIC, true, $this->tag); + + return is_int($identifier) ? chr($identifier) : $identifier; + } + + public function getTag() + { + return $this->tag; + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + $identifier = self::parseBinaryIdentifier($binaryData, $offsetIndex); + $firstIdentifierOctet = ord($identifier); + assert(Identifier::isContextSpecificClass($firstIdentifierOctet), 'identifier octet should indicate context specific class'); + assert(Identifier::isConstructed($firstIdentifierOctet), 'identifier octet should indicate constructed object'); + $tag = Identifier::getTagNumber($identifier); + + $totalContentLength = self::parseContentLength($binaryData, $offsetIndex); + $remainingContentLength = $totalContentLength; + + $offsetIndexOfDecoratedObject = $offsetIndex; + $decoratedObjects = []; + + while ($remainingContentLength > 0) { + $nextObject = ASNObject::fromBinary($binaryData, $offsetIndex); + $remainingContentLength -= $nextObject->getObjectLength(); + $decoratedObjects[] = $nextObject; + } + + if ($remainingContentLength != 0) { + throw new ParserException("Context-Specific explicitly tagged object [$tag] starting at offset $offsetIndexOfDecoratedObject specifies a length of $totalContentLength octets but $remainingContentLength remain after parsing the content", $offsetIndexOfDecoratedObject); + } + + $parsedObject = new self($tag, ...$decoratedObjects); + $parsedObject->setContentLength($totalContentLength); + return $parsedObject; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Identifier.php b/vendor/fgrosse/phpasn1/lib/ASN1/Identifier.php new file mode 100644 index 000000000..b21caa34c --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Identifier.php @@ -0,0 +1,339 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1; + +use Exception; + +/** + * The Identifier encodes the ASN.1 tag (class and number) of the type of a data value. + * + * Every identifier whose number is in the range 0 to 30 has the following structure: + * + * Bits: 8 7 6 5 4 3 2 1 + * | Class | P/C | Tag number | + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Bits 8 and 7 define the class of this type ( Universal, Application, Context-specific or Private). + * Bit 6 encoded whether this type is primitive or constructed + * The remaining bits 5 - 1 encode the tag number + */ +class Identifier +{ + const CLASS_UNIVERSAL = 0x00; + const CLASS_APPLICATION = 0x01; + const CLASS_CONTEXT_SPECIFIC = 0x02; + const CLASS_PRIVATE = 0x03; + + const EOC = 0x00; // unsupported for now + const BOOLEAN = 0x01; + const INTEGER = 0x02; + const BITSTRING = 0x03; + const OCTETSTRING = 0x04; + const NULL = 0x05; + const OBJECT_IDENTIFIER = 0x06; + const OBJECT_DESCRIPTOR = 0x07; + const EXTERNAL = 0x08; // unsupported for now + const REAL = 0x09; // unsupported for now + const ENUMERATED = 0x0A; + const EMBEDDED_PDV = 0x0B; // unsupported for now + const UTF8_STRING = 0x0C; + const RELATIVE_OID = 0x0D; + // value 0x0E and 0x0F are reserved for future use + + const SEQUENCE = 0x30; + const SET = 0x31; + const NUMERIC_STRING = 0x12; + const PRINTABLE_STRING = 0x13; + const T61_STRING = 0x14; // sometimes referred to as TeletextString + const VIDEOTEXT_STRING = 0x15; + const IA5_STRING = 0x16; + const UTC_TIME = 0x17; + const GENERALIZED_TIME = 0x18; + const GRAPHIC_STRING = 0x19; + const VISIBLE_STRING = 0x1A; + const GENERAL_STRING = 0x1B; + const UNIVERSAL_STRING = 0x1C; + const CHARACTER_STRING = 0x1D; // Unrestricted character type + const BMP_STRING = 0x1E; + + const LONG_FORM = 0x1F; + const IS_CONSTRUCTED = 0x20; + + /** + * Creates an identifier. Short form identifiers are returned as integers + * for BC, long form identifiers will be returned as a string of octets. + * + * @param int $class + * @param bool $isConstructed + * @param int $tagNumber + * + * @throws Exception if the given arguments are invalid + * + * @return int|string + */ + public static function create($class, $isConstructed, $tagNumber) + { + if (!is_numeric($class) || $class < self::CLASS_UNIVERSAL || $class > self::CLASS_PRIVATE) { + throw new Exception(sprintf('Invalid class %d given', $class)); + } + + if (!is_bool($isConstructed)) { + throw new Exception("\$isConstructed must be a boolean value ($isConstructed given)"); + } + + $tagNumber = self::makeNumeric($tagNumber); + if ($tagNumber < 0) { + throw new Exception(sprintf('Invalid $tagNumber %d given. You can only use positive integers.', $tagNumber)); + } + + if ($tagNumber < self::LONG_FORM) { + return ($class << 6) | ($isConstructed << 5) | $tagNumber; + } + + $firstOctet = ($class << 6) | ($isConstructed << 5) | self::LONG_FORM; + + // Tag numbers formatted in long form are base-128 encoded. See X.609#8.1.2.4 + return chr($firstOctet).Base128::encode($tagNumber); + } + + public static function isConstructed($identifierOctet) + { + return ($identifierOctet & self::IS_CONSTRUCTED) === self::IS_CONSTRUCTED; + } + + public static function isLongForm($identifierOctet) + { + return ($identifierOctet & self::LONG_FORM) === self::LONG_FORM; + } + + /** + * Return the name of the mapped ASN.1 type with a preceding "ASN.1 ". + * + * Example: ASN.1 Octet String + * + * @see Identifier::getShortName() + * + * @param int|string $identifier + * + * @return string + */ + public static function getName($identifier) + { + $identifierOctet = self::makeNumeric($identifier); + + $typeName = static::getShortName($identifier); + + if (($identifierOctet & self::LONG_FORM) < self::LONG_FORM) { + $typeName = "ASN.1 {$typeName}"; + } + + return $typeName; + } + + /** + * Return the short version of the type name. + * + * If the given identifier octet can be mapped to a known universal type this will + * return its name. Else Identifier::getClassDescription() is used to retrieve + * information about the identifier. + * + * @see Identifier::getName() + * @see Identifier::getClassDescription() + * + * @param int|string $identifier + * + * @return string + */ + public static function getShortName($identifier) + { + $identifierOctet = self::makeNumeric($identifier); + + switch ($identifierOctet) { + case self::EOC: + return 'End-of-contents octet'; + case self::BOOLEAN: + return 'Boolean'; + case self::INTEGER: + return 'Integer'; + case self::BITSTRING: + return 'Bit String'; + case self::OCTETSTRING: + return 'Octet String'; + case self::NULL: + return 'NULL'; + case self::OBJECT_IDENTIFIER: + return 'Object Identifier'; + case self::OBJECT_DESCRIPTOR: + return 'Object Descriptor'; + case self::EXTERNAL: + return 'External Type'; + case self::REAL: + return 'Real'; + case self::ENUMERATED: + return 'Enumerated'; + case self::EMBEDDED_PDV: + return 'Embedded PDV'; + case self::UTF8_STRING: + return 'UTF8 String'; + case self::RELATIVE_OID: + return 'Relative OID'; + case self::SEQUENCE: + return 'Sequence'; + case self::SET: + return 'Set'; + case self::NUMERIC_STRING: + return 'Numeric String'; + case self::PRINTABLE_STRING: + return 'Printable String'; + case self::T61_STRING: + return 'T61 String'; + case self::VIDEOTEXT_STRING: + return 'Videotext String'; + case self::IA5_STRING: + return 'IA5 String'; + case self::UTC_TIME: + return 'UTC Time'; + case self::GENERALIZED_TIME: + return 'Generalized Time'; + case self::GRAPHIC_STRING: + return 'Graphic String'; + case self::VISIBLE_STRING: + return 'Visible String'; + case self::GENERAL_STRING: + return 'General String'; + case self::UNIVERSAL_STRING: + return 'Universal String'; + case self::CHARACTER_STRING: + return 'Character String'; + case self::BMP_STRING: + return 'BMP String'; + + case 0x0E: + return 'RESERVED (0x0E)'; + case 0x0F: + return 'RESERVED (0x0F)'; + + case self::LONG_FORM: + default: + $classDescription = self::getClassDescription($identifier); + + if (is_int($identifier)) { + $identifier = chr($identifier); + } + + return "$classDescription (0x".strtoupper(bin2hex($identifier)).')'; + } + } + + /** + * Returns a textual description of the information encoded in a given identifier octet. + * + * The first three (most significant) bytes are evaluated to determine if this is a + * constructed or primitive type and if it is either universal, application, context-specific or + * private. + * + * Example: + * Constructed context-specific + * Primitive universal + * + * @param int|string $identifier + * + * @return string + */ + public static function getClassDescription($identifier) + { + $identifierOctet = self::makeNumeric($identifier); + + if (self::isConstructed($identifierOctet)) { + $classDescription = 'Constructed '; + } else { + $classDescription = 'Primitive '; + } + $classBits = $identifierOctet >> 6; + switch ($classBits) { + case self::CLASS_UNIVERSAL: + $classDescription .= 'universal'; + break; + case self::CLASS_APPLICATION: + $classDescription .= 'application'; + break; + case self::CLASS_CONTEXT_SPECIFIC: + $tagNumber = self::getTagNumber($identifier); + $classDescription = "[$tagNumber] Context-specific"; + break; + case self::CLASS_PRIVATE: + $classDescription .= 'private'; + break; + + default: + return "INVALID IDENTIFIER OCTET: {$identifierOctet}"; + } + + return $classDescription; + } + + /** + * @param int|string $identifier + * + * @return int + */ + public static function getTagNumber($identifier) + { + $firstOctet = self::makeNumeric($identifier); + $tagNumber = $firstOctet & self::LONG_FORM; + + if ($tagNumber < self::LONG_FORM) { + return $tagNumber; + } + + if (is_numeric($identifier)) { + $identifier = chr($identifier); + } + return Base128::decode(substr($identifier, 1)); + } + + public static function isUniversalClass($identifier) + { + $identifier = self::makeNumeric($identifier); + + return $identifier >> 6 == self::CLASS_UNIVERSAL; + } + + public static function isApplicationClass($identifier) + { + $identifier = self::makeNumeric($identifier); + + return $identifier >> 6 == self::CLASS_APPLICATION; + } + + public static function isContextSpecificClass($identifier) + { + $identifier = self::makeNumeric($identifier); + + return $identifier >> 6 == self::CLASS_CONTEXT_SPECIFIC; + } + + public static function isPrivateClass($identifier) + { + $identifier = self::makeNumeric($identifier); + + return $identifier >> 6 == self::CLASS_PRIVATE; + } + + private static function makeNumeric($identifierOctet) + { + if (!is_numeric($identifierOctet)) { + return ord($identifierOctet); + } else { + return $identifierOctet; + } + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/OID.php b/vendor/fgrosse/phpasn1/lib/ASN1/OID.php new file mode 100644 index 000000000..61ab01e15 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/OID.php @@ -0,0 +1,198 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1; + +class OID +{ + const RSA_ENCRYPTION = '1.2.840.113549.1.1.1'; + const MD5_WITH_RSA_ENCRYPTION = '1.2.840.113549.1.1.4'; + const SHA1_WITH_RSA_SIGNATURE = '1.2.840.113549.1.1.5'; + const PKCS9_EMAIL = '1.2.840.113549.1.9.1'; + const PKCS9_UNSTRUCTURED_NAME = '1.2.840.113549.1.9.2'; + const PKCS9_CONTENT_TYPE = '1.2.840.113549.1.9.3'; + const PKCS9_MESSAGE_DIGEST = '1.2.840.113549.1.9.4'; + const PKCS9_SIGNING_TIME = '1.2.840.113549.1.9.5'; + const PKCS9_EXTENSION_REQUEST = '1.2.840.113549.1.9.14'; + + // certificate extension identifier + const CERT_EXT_SUBJECT_DIRECTORY_ATTR = '2.5.29.9'; + const CERT_EXT_SUBJECT_KEY_IDENTIFIER = '2.5.29.14'; + const CERT_EXT_KEY_USAGE = '2.5.29.15'; + const CERT_EXT_PRIVATE_KEY_USAGE_PERIOD = '2.5.29.16'; + const CERT_EXT_SUBJECT_ALT_NAME = '2.5.29.17'; + const CERT_EXT_ISSUER_ALT_NAME = '2.5.29.18'; + const CERT_EXT_BASIC_CONSTRAINTS = '2.5.29.19'; + const CERT_EXT_CRL_NUMBER = '2.5.29.20'; + const CERT_EXT_REASON_CODE = '2.5.29.21'; + const CERT_EXT_INVALIDITY_DATE = '2.5.29.24'; + const CERT_EXT_DELTA_CRL_INDICATOR = '2.5.29.27'; + const CERT_EXT_ISSUING_DIST_POINT = '2.5.29.28'; + const CERT_EXT_CERT_ISSUER = '2.5.29.29'; + const CERT_EXT_NAME_CONSTRAINTS = '2.5.29.30'; + const CERT_EXT_CRL_DISTRIBUTION_POINTS = '2.5.29.31'; + const CERT_EXT_CERT_POLICIES = '2.5.29.32'; + const CERT_EXT_AUTHORITY_KEY_IDENTIFIER = '2.5.29.35'; + const CERT_EXT_EXTENDED_KEY_USAGE = '2.5.29.37'; + + // standard certificate files + const COMMON_NAME = '2.5.4.3'; + const SURNAME = '2.5.4.4'; + const SERIAL_NUMBER = '2.5.4.5'; + const COUNTRY_NAME = '2.5.4.6'; + const LOCALITY_NAME = '2.5.4.7'; + const STATE_OR_PROVINCE_NAME = '2.5.4.8'; + const STREET_ADDRESS = '2.5.4.9'; + const ORGANIZATION_NAME = '2.5.4.10'; + const OU_NAME = '2.5.4.11'; + const TITLE = '2.5.4.12'; + const DESCRIPTION = '2.5.4.13'; + const POSTAL_ADDRESS = '2.5.4.16'; + const POSTAL_CODE = '2.5.4.17'; + const AUTHORITY_REVOCATION_LIST = '2.5.4.38'; + + const AUTHORITY_INFORMATION_ACCESS = '1.3.6.1.5.5.7.1.1'; + + /** + * Returns the name of the given object identifier. + * + * Some OIDs are saved as class constants in this class. + * If the wanted oidString is not among them, this method will + * query http://oid-info.com for the right name. + * This behavior can be suppressed by setting the second method parameter to false. + * + * @param string $oidString + * @param bool $loadFromWeb + * + * @see self::loadFromWeb($oidString) + * + * @return string + */ + public static function getName($oidString, $loadFromWeb = true) + { + switch ($oidString) { + case self::RSA_ENCRYPTION: + return 'RSA Encryption'; + case self::MD5_WITH_RSA_ENCRYPTION: + return 'MD5 with RSA Encryption'; + case self::SHA1_WITH_RSA_SIGNATURE: + return 'SHA-1 with RSA Signature'; + + case self::PKCS9_EMAIL: + return 'PKCS #9 Email Address'; + case self::PKCS9_UNSTRUCTURED_NAME: + return 'PKCS #9 Unstructured Name'; + case self::PKCS9_CONTENT_TYPE: + return 'PKCS #9 Content Type'; + case self::PKCS9_MESSAGE_DIGEST: + return 'PKCS #9 Message Digest'; + case self::PKCS9_SIGNING_TIME: + return 'PKCS #9 Signing Time'; + + case self::COMMON_NAME: + return 'Common Name'; + case self::SURNAME: + return 'Surname'; + case self::SERIAL_NUMBER: + return 'Serial Number'; + case self::COUNTRY_NAME: + return 'Country Name'; + case self::LOCALITY_NAME: + return 'Locality Name'; + case self::STATE_OR_PROVINCE_NAME: + return 'State or Province Name'; + case self::STREET_ADDRESS: + return 'Street Address'; + case self::ORGANIZATION_NAME: + return 'Organization Name'; + case self::OU_NAME: + return 'Organization Unit Name'; + case self::TITLE: + return 'Title'; + case self::DESCRIPTION: + return 'Description'; + case self::POSTAL_ADDRESS: + return 'Postal Address'; + case self::POSTAL_CODE: + return 'Postal Code'; + case self::AUTHORITY_REVOCATION_LIST: + return 'Authority Revocation List'; + + case self::CERT_EXT_SUBJECT_DIRECTORY_ATTR: + return 'Subject directory attributes'; + case self::CERT_EXT_SUBJECT_KEY_IDENTIFIER: + return 'Subject key identifier'; + case self::CERT_EXT_KEY_USAGE: + return 'Key usage certificate extension'; + case self::CERT_EXT_PRIVATE_KEY_USAGE_PERIOD: + return 'Private key usage'; + case self::CERT_EXT_SUBJECT_ALT_NAME: + return 'Subject alternative name (SAN)'; + case self::CERT_EXT_ISSUER_ALT_NAME: + return 'Issuer alternative name'; + case self::CERT_EXT_BASIC_CONSTRAINTS: + return 'Basic constraints'; + case self::CERT_EXT_CRL_NUMBER: + return 'CRL number'; + case self::CERT_EXT_REASON_CODE: + return 'Reason code'; + case self::CERT_EXT_INVALIDITY_DATE: + return 'Invalidity code'; + case self::CERT_EXT_DELTA_CRL_INDICATOR: + return 'Delta CRL indicator'; + case self::CERT_EXT_ISSUING_DIST_POINT: + return 'Issuing distribution point'; + case self::CERT_EXT_CERT_ISSUER: + return 'Certificate issuer'; + case self::CERT_EXT_NAME_CONSTRAINTS: + return 'Name constraints'; + case self::CERT_EXT_CRL_DISTRIBUTION_POINTS: + return 'CRL distribution points'; + case self::CERT_EXT_CERT_POLICIES: + return 'Certificate policies '; + case self::CERT_EXT_AUTHORITY_KEY_IDENTIFIER: + return 'Authority key identifier'; + case self::CERT_EXT_EXTENDED_KEY_USAGE: + return 'Extended key usage'; + case self::AUTHORITY_INFORMATION_ACCESS: + return 'Certificate Authority Information Access (AIA)'; + + default: + if ($loadFromWeb) { + return self::loadFromWeb($oidString); + } else { + return $oidString; + } + } + } + + public static function loadFromWeb($oidString) + { + $ch = curl_init("http://oid-info.com/get/{$oidString}"); + + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_HEADER, 0); + + $contents = curl_exec($ch); + curl_close($ch); + + // This pattern needs to be updated as soon as the website layout of oid-info.com changes + preg_match_all('#(.+)\(\d+\)#si', $contents, $oidName); + + if (empty($oidName[1])) { + return "{$oidString} (unknown)"; + } + + $oidName = ucfirst(strtolower(preg_replace('/([A-Z][a-z])/', ' $1', $oidName[1][0]))); + $oidName = str_replace('-', ' ', $oidName); + + return "{$oidName} ({$oidString})"; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Parsable.php b/vendor/fgrosse/phpasn1/lib/ASN1/Parsable.php new file mode 100644 index 000000000..fa66b558d --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Parsable.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1; + +use FG\ASN1\Exception\ParserException; + +/** + * The Parsable interface describes classes that can be parsed from their binary DER representation. + */ +interface Parsable +{ + /** + * Parse an instance of this class from its binary DER encoded representation. + * + * @param string $binaryData + * @param int $offsetIndex the offset at which parsing of the $binaryData is started. This parameter ill be modified + * to contain the offset index of the next object after this object has been parsed + * + * @throws ParserException if the given binary data is either invalid or not currently supported + * + * @return static + */ + public static function fromBinary(&$binaryData, &$offsetIndex = null); +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/TemplateParser.php b/vendor/fgrosse/phpasn1/lib/ASN1/TemplateParser.php new file mode 100644 index 000000000..90a40b037 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/TemplateParser.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1; + +use Exception; +use FG\ASN1\Exception\ParserException; +use FG\ASN1\Universal\Sequence; + +class TemplateParser +{ + /** + * @param string $data + * @param array $template + * @return \FG\ASN1\ASNObject|Sequence + * @throws ParserException if there was an issue parsing + */ + public function parseBase64($data, array $template) + { + // TODO test with invalid data + return $this->parseBinary(base64_decode($data), $template); + } + + /** + * @param string $binary + * @param array $template + * @return \FG\ASN1\ASNObject|Sequence + * @throws ParserException if there was an issue parsing + */ + public function parseBinary($binary, array $template) + { + $parsedObject = ASNObject::fromBinary($binary); + + foreach ($template as $key => $value) { + $this->validate($parsedObject, $key, $value); + } + + return $parsedObject; + } + + private function validate(ASNObject $object, $key, $value) + { + if (is_array($value)) { + $this->assertTypeId($key, $object); + + /* @var Construct $object */ + foreach ($value as $key => $child) { + $this->validate($object->current(), $key, $child); + $object->next(); + } + } else { + $this->assertTypeId($value, $object); + } + } + + private function assertTypeId($expectedTypeId, ASNObject $object) + { + $actualType = $object->getType(); + if ($expectedTypeId != $actualType) { + throw new Exception("Expected type ($expectedTypeId) does not match actual type ($actualType"); + } + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Universal/BMPString.php b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/BMPString.php new file mode 100644 index 000000000..83ec6a910 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/BMPString.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\AbstractString; +use FG\ASN1\Identifier; + +class BMPString extends AbstractString +{ + /** + * Creates a new ASN.1 BMP String. + * + * BMPString is a subtype of UniversalString that has its own + * unique tag and contains only the characters in the + * Basic Multilingual Plane (those corresponding to the first + * 64K-2 cells, less cells whose encoding is used to address + * characters outside the Basic Multilingual Plane) of ISO/IEC 10646-1. + * + * TODO The encodable characters of this type are not yet checked. + * + * @param string $string + */ + public function __construct($string) + { + $this->value = $string; + $this->allowAll(); + } + + public function getType() + { + return Identifier::BMP_STRING; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Universal/BitString.php b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/BitString.php new file mode 100644 index 000000000..226695c59 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/BitString.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use Exception; +use FG\ASN1\Exception\ParserException; +use FG\ASN1\Parsable; +use FG\ASN1\Identifier; + +class BitString extends OctetString implements Parsable +{ + private $nrOfUnusedBits; + + /** + * Creates a new ASN.1 BitString object. + * + * @param string|int $value Either the hexadecimal value as a string (spaces are allowed - leading 0x is optional) or a numeric value + * @param int $nrOfUnusedBits the number of unused bits in the last octet [optional]. + * + * @throws Exception if the second parameter is no positive numeric value + */ + public function __construct($value, $nrOfUnusedBits = 0) + { + parent::__construct($value); + + if (!is_numeric($nrOfUnusedBits) || $nrOfUnusedBits < 0) { + throw new Exception('BitString: second parameter needs to be a positive number (or zero)!'); + } + + $this->nrOfUnusedBits = $nrOfUnusedBits; + } + + public function getType() + { + return Identifier::BITSTRING; + } + + protected function calculateContentLength() + { + // add one to the length for the first octet which encodes the number of unused bits in the last octet + return parent::calculateContentLength() + 1; + } + + protected function getEncodedValue() + { + // the first octet determines the number of unused bits + $nrOfUnusedBitsOctet = chr($this->nrOfUnusedBits); + $actualContent = parent::getEncodedValue(); + + return $nrOfUnusedBitsOctet.$actualContent; + } + + public function getNumberOfUnusedBits() + { + return $this->nrOfUnusedBits; + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + self::parseIdentifier($binaryData[$offsetIndex], Identifier::BITSTRING, $offsetIndex++); + $contentLength = self::parseContentLength($binaryData, $offsetIndex, 2); + + $nrOfUnusedBits = ord($binaryData[$offsetIndex]); + $value = substr($binaryData, $offsetIndex + 1, $contentLength - 1); + + if ($nrOfUnusedBits > 7 || // no less than 1 used, otherwise non-minimal + ($contentLength - 1) == 1 && $nrOfUnusedBits > 0 || // content length only 1, no + (ord($value[strlen($value)-1])&((1<<$nrOfUnusedBits)-1)) != 0 // unused bits set + ) { + throw new ParserException("Can not parse bit string with invalid padding", $offsetIndex); + } + + $offsetIndex += $contentLength; + + $parsedObject = new self(bin2hex($value), $nrOfUnusedBits); + $parsedObject->setContentLength($contentLength); + + return $parsedObject; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Boolean.php b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Boolean.php new file mode 100644 index 000000000..b73c99f1e --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Boolean.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\ASNObject; +use FG\ASN1\Parsable; +use FG\ASN1\Identifier; +use FG\ASN1\Exception\ParserException; + +class Boolean extends ASNObject implements Parsable +{ + private $value; + + /** + * @param bool $value + */ + public function __construct($value) + { + $this->value = $value; + } + + public function getType() + { + return Identifier::BOOLEAN; + } + + protected function calculateContentLength() + { + return 1; + } + + protected function getEncodedValue() + { + if ($this->value == false) { + return chr(0x00); + } else { + return chr(0xFF); + } + } + + public function getContent() + { + if ($this->value == true) { + return 'TRUE'; + } else { + return 'FALSE'; + } + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + self::parseIdentifier($binaryData[$offsetIndex], Identifier::BOOLEAN, $offsetIndex++); + $contentLength = self::parseContentLength($binaryData, $offsetIndex); + + if ($contentLength != 1) { + throw new ParserException("An ASN.1 Boolean should not have a length other than one. Extracted length was {$contentLength}", $offsetIndex); + } + + $value = ord($binaryData[$offsetIndex++]); + $booleanValue = $value == 0xFF ? true : false; + + $parsedObject = new self($booleanValue); + $parsedObject->setContentLength($contentLength); + + return $parsedObject; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Universal/CharacterString.php b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/CharacterString.php new file mode 100644 index 000000000..bfc170dba --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/CharacterString.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\AbstractString; +use FG\ASN1\Identifier; + +class CharacterString extends AbstractString +{ + public function __construct($string) + { + $this->value = $string; + $this->allowAll(); + } + + public function getType() + { + return Identifier::CHARACTER_STRING; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Enumerated.php b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Enumerated.php new file mode 100644 index 000000000..06d04a3a3 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Enumerated.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\Identifier; + +class Enumerated extends Integer +{ + public function getType() + { + return Identifier::ENUMERATED; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Universal/GeneralString.php b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/GeneralString.php new file mode 100644 index 000000000..fb0346f0a --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/GeneralString.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\AbstractString; +use FG\ASN1\Identifier; + +class GeneralString extends AbstractString +{ + /** + * Creates a new ASN.1 GeneralString. + * TODO The encodable characters of this type are not yet checked. + * + * @param string $string + */ + public function __construct($string) + { + $this->value = $string; + $this->allowAll(); + } + + public function getType() + { + return Identifier::GENERAL_STRING; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Universal/GeneralizedTime.php b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/GeneralizedTime.php new file mode 100644 index 000000000..ca9220972 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/GeneralizedTime.php @@ -0,0 +1,134 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\AbstractTime; +use FG\ASN1\Parsable; +use FG\ASN1\Identifier; +use FG\ASN1\Exception\ParserException; + +/** + * This ASN.1 universal type contains date and time information according to ISO 8601. + * + * The type consists of values representing: + * a) a calendar date, as defined in ISO 8601; and + * b) a time of day, to any of the precisions defined in ISO 8601, except for the hours value 24 which shall not be used; and + * c) the local time differential factor as defined in ISO 8601. + * + * Decoding of this type will accept the Basic Encoding Rules (BER) + * The encoding will comply with the Distinguished Encoding Rules (DER). + */ +class GeneralizedTime extends AbstractTime implements Parsable +{ + private $microseconds; + + public function __construct($dateTime = null, $dateTimeZone = 'UTC') + { + parent::__construct($dateTime, $dateTimeZone); + $this->microseconds = $this->value->format('u'); + if ($this->containsFractionalSecondsElement()) { + // DER requires us to remove trailing zeros + $this->microseconds = preg_replace('/([1-9]+)0+$/', '$1', $this->microseconds); + } + } + + public function getType() + { + return Identifier::GENERALIZED_TIME; + } + + protected function calculateContentLength() + { + $contentSize = 15; // YYYYMMDDHHmmSSZ + + if ($this->containsFractionalSecondsElement()) { + $contentSize += 1 + strlen($this->microseconds); + } + + return $contentSize; + } + + public function containsFractionalSecondsElement() + { + return intval($this->microseconds) > 0; + } + + protected function getEncodedValue() + { + $encodedContent = $this->value->format('YmdHis'); + if ($this->containsFractionalSecondsElement()) { + $encodedContent .= ".{$this->microseconds}"; + } + + return $encodedContent.'Z'; + } + + public function __toString() + { + if ($this->containsFractionalSecondsElement()) { + return $this->value->format("Y-m-d\tH:i:s.uP"); + } else { + return $this->value->format("Y-m-d\tH:i:sP"); + } + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + self::parseIdentifier($binaryData[$offsetIndex], Identifier::GENERALIZED_TIME, $offsetIndex++); + $lengthOfMinimumTimeString = 14; // YYYYMMDDHHmmSS + $contentLength = self::parseContentLength($binaryData, $offsetIndex, $lengthOfMinimumTimeString); + $maximumBytesToRead = $contentLength; + + $format = 'YmdGis'; + $content = substr($binaryData, $offsetIndex, $contentLength); + $dateTimeString = substr($content, 0, $lengthOfMinimumTimeString); + $offsetIndex += $lengthOfMinimumTimeString; + $maximumBytesToRead -= $lengthOfMinimumTimeString; + + if ($contentLength == $lengthOfMinimumTimeString) { + $localTimeZone = new \DateTimeZone(date_default_timezone_get()); + $dateTime = \DateTime::createFromFormat($format, $dateTimeString, $localTimeZone); + } else { + if ($binaryData[$offsetIndex] == '.') { + $maximumBytesToRead--; // account for the '.' + $nrOfFractionalSecondElements = 1; // account for the '.' + + while ($maximumBytesToRead > 0 + && $binaryData[$offsetIndex + $nrOfFractionalSecondElements] != '+' + && $binaryData[$offsetIndex + $nrOfFractionalSecondElements] != '-' + && $binaryData[$offsetIndex + $nrOfFractionalSecondElements] != 'Z') { + $nrOfFractionalSecondElements++; + $maximumBytesToRead--; + } + + $dateTimeString .= substr($binaryData, $offsetIndex, $nrOfFractionalSecondElements); + $offsetIndex += $nrOfFractionalSecondElements; + $format .= '.u'; + } + + $dateTime = \DateTime::createFromFormat($format, $dateTimeString, new \DateTimeZone('UTC')); + + if ($maximumBytesToRead > 0) { + if ($binaryData[$offsetIndex] == '+' + || $binaryData[$offsetIndex] == '-') { + $dateTime = static::extractTimeZoneData($binaryData, $offsetIndex, $dateTime); + } elseif ($binaryData[$offsetIndex++] != 'Z') { + throw new ParserException('Invalid ISO 8601 Time String', $offsetIndex); + } + } + } + + $parsedObject = new self($dateTime); + $parsedObject->setContentLength($contentLength); + + return $parsedObject; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Universal/GraphicString.php b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/GraphicString.php new file mode 100644 index 000000000..4a01d67be --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/GraphicString.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\AbstractString; +use FG\ASN1\Identifier; + +class GraphicString extends AbstractString +{ + /** + * Creates a new ASN.1 Graphic String. + * TODO The encodable characters of this type are not yet checked. + * + * @param string $string + */ + public function __construct($string) + { + $this->value = $string; + $this->allowAll(); + } + + public function getType() + { + return Identifier::GRAPHIC_STRING; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Universal/IA5String.php b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/IA5String.php new file mode 100644 index 000000000..33a806793 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/IA5String.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\AbstractString; +use FG\ASN1\Identifier; + +/** + * The International Alphabet No.5 (IA5) references the encoding of the ASCII characters. + * + * Each character in the data is encoded as 1 byte. + */ +class IA5String extends AbstractString +{ + public function __construct($string) + { + parent::__construct($string); + for ($i = 1; $i < 128; $i++) { + $this->allowCharacter(chr($i)); + } + } + + public function getType() + { + return Identifier::IA5_STRING; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Integer.php b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Integer.php new file mode 100644 index 000000000..fe3806ba7 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Integer.php @@ -0,0 +1,130 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use Exception; +use FG\Utility\BigInteger; +use FG\ASN1\Exception\ParserException; +use FG\ASN1\ASNObject; +use FG\ASN1\Parsable; +use FG\ASN1\Identifier; + +class Integer extends ASNObject implements Parsable +{ + /** @var int */ + private $value; + + /** + * @param int $value + * + * @throws Exception if the value is not numeric + */ + public function __construct($value) + { + if (is_numeric($value) == false) { + throw new Exception("Invalid VALUE [{$value}] for ASN1_INTEGER"); + } + $this->value = $value; + } + + public function getType() + { + return Identifier::INTEGER; + } + + public function getContent() + { + return $this->value; + } + + protected function calculateContentLength() + { + return strlen($this->getEncodedValue()); + } + + protected function getEncodedValue() + { + $value = BigInteger::create($this->value, 10); + $negative = $value->compare(0) < 0; + if ($negative) { + $value = $value->absoluteValue(); + $limit = 0x80; + } else { + $limit = 0x7f; + } + + $mod = 0xff+1; + $values = []; + while($value->compare($limit) > 0) { + $values[] = $value->modulus($mod)->toInteger(); + $value = $value->shiftRight(8); + } + + $values[] = $value->modulus($mod)->toInteger(); + $numValues = count($values); + + if ($negative) { + for ($i = 0; $i < $numValues; $i++) { + $values[$i] = 0xff - $values[$i]; + } + for ($i = 0; $i < $numValues; $i++) { + $values[$i] += 1; + if ($values[$i] <= 0xff) { + break; + } + assert($i != $numValues - 1); + $values[$i] = 0; + } + if ($values[$numValues - 1] == 0x7f) { + $values[] = 0xff; + } + } + $values = array_reverse($values); + $r = pack("C*", ...$values); + return $r; + } + + private static function ensureMinimalEncoding($binaryData, $offsetIndex) + { + // All the first nine bits cannot equal 0 or 1, which would + // be non-minimal encoding for positive and negative integers respectively + if ((ord($binaryData[$offsetIndex]) == 0x00 && (ord($binaryData[$offsetIndex+1]) & 0x80) == 0) || + (ord($binaryData[$offsetIndex]) == 0xff && (ord($binaryData[$offsetIndex+1]) & 0x80) == 0x80)) { + throw new ParserException("Integer not minimally encoded", $offsetIndex); + } + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + $parsedObject = new static(0); + self::parseIdentifier($binaryData[$offsetIndex], $parsedObject->getType(), $offsetIndex++); + $contentLength = self::parseContentLength($binaryData, $offsetIndex, 1); + + if ($contentLength > 1) { + self::ensureMinimalEncoding($binaryData, $offsetIndex); + } + $isNegative = (ord($binaryData[$offsetIndex]) & 0x80) != 0x00; + $number = BigInteger::create(ord($binaryData[$offsetIndex++]) & 0x7F); + + for ($i = 0; $i < $contentLength - 1; $i++) { + $number = $number->multiply(0x100)->add(ord($binaryData[$offsetIndex++])); + } + + if ($isNegative) { + $number = $number->subtract(BigInteger::create(2)->toPower(8 * $contentLength - 1)); + } + + $parsedObject = new static((string)$number); + $parsedObject->setContentLength($contentLength); + + return $parsedObject; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Universal/NullObject.php b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/NullObject.php new file mode 100644 index 000000000..b5293e4ba --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/NullObject.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\ASNObject; +use FG\ASN1\Parsable; +use FG\ASN1\Identifier; +use FG\ASN1\Exception\ParserException; + +class NullObject extends ASNObject implements Parsable +{ + public function getType() + { + return Identifier::NULL; + } + + protected function calculateContentLength() + { + return 0; + } + + protected function getEncodedValue() + { + return null; + } + + public function getContent() + { + return 'NULL'; + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + self::parseIdentifier($binaryData[$offsetIndex], Identifier::NULL, $offsetIndex++); + $contentLength = self::parseContentLength($binaryData, $offsetIndex); + + if ($contentLength != 0) { + throw new ParserException("An ASN.1 Null should not have a length other than zero. Extracted length was {$contentLength}", $offsetIndex); + } + + $parsedObject = new self(); + $parsedObject->setContentLength(0); + + return $parsedObject; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Universal/NumericString.php b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/NumericString.php new file mode 100644 index 000000000..13fb7c347 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/NumericString.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\AbstractString; +use FG\ASN1\Identifier; + +class NumericString extends AbstractString +{ + /** + * Creates a new ASN.1 NumericString. + * + * The following characters are permitted: + * Digits 0,1, ... 9 + * SPACE (space) + * + * @param string $string + */ + public function __construct($string) + { + $this->value = $string; + $this->allowNumbers(); + $this->allowSpaces(); + } + + public function getType() + { + return Identifier::NUMERIC_STRING; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Universal/ObjectDescriptor.php b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/ObjectDescriptor.php new file mode 100644 index 000000000..1c5d3498a --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/ObjectDescriptor.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\Identifier; + +class ObjectDescriptor extends GraphicString +{ + public function __construct($objectDescription) + { + parent::__construct($objectDescription); + } + + public function getType() + { + return Identifier::OBJECT_DESCRIPTOR; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Universal/ObjectIdentifier.php b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/ObjectIdentifier.php new file mode 100644 index 000000000..150ce9c4a --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/ObjectIdentifier.php @@ -0,0 +1,138 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use Exception; +use FG\ASN1\Base128; +use FG\ASN1\OID; +use FG\ASN1\ASNObject; +use FG\ASN1\Parsable; +use FG\ASN1\Identifier; +use FG\ASN1\Exception\ParserException; + +class ObjectIdentifier extends ASNObject implements Parsable +{ + protected $subIdentifiers; + protected $value; + + public function __construct($value) + { + $this->subIdentifiers = explode('.', $value); + $nrOfSubIdentifiers = count($this->subIdentifiers); + + for ($i = 0; $i < $nrOfSubIdentifiers; $i++) { + if (is_numeric($this->subIdentifiers[$i])) { + // enforce the integer type + $this->subIdentifiers[$i] = intval($this->subIdentifiers[$i]); + } else { + throw new Exception("[{$value}] is no valid object identifier (sub identifier ".($i + 1).' is not numeric)!'); + } + } + + // Merge the first to arcs of the OID registration tree (per ASN definition!) + if ($nrOfSubIdentifiers >= 2) { + $this->subIdentifiers[1] = ($this->subIdentifiers[0] * 40) + $this->subIdentifiers[1]; + unset($this->subIdentifiers[0]); + } + + $this->value = $value; + } + + public function getContent() + { + return $this->value; + } + + public function getType() + { + return Identifier::OBJECT_IDENTIFIER; + } + + protected function calculateContentLength() + { + $length = 0; + foreach ($this->subIdentifiers as $subIdentifier) { + do { + $subIdentifier = $subIdentifier >> 7; + $length++; + } while ($subIdentifier > 0); + } + + return $length; + } + + protected function getEncodedValue() + { + $encodedValue = ''; + foreach ($this->subIdentifiers as $subIdentifier) { + $encodedValue .= Base128::encode($subIdentifier); + } + + return $encodedValue; + } + + public function __toString() + { + return OID::getName($this->value); + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + self::parseIdentifier($binaryData[$offsetIndex], Identifier::OBJECT_IDENTIFIER, $offsetIndex++); + $contentLength = self::parseContentLength($binaryData, $offsetIndex, 1); + + $firstOctet = ord($binaryData[$offsetIndex++]); + $oidString = floor($firstOctet / 40).'.'.($firstOctet % 40); + $oidString .= '.'.self::parseOid($binaryData, $offsetIndex, $contentLength - 1); + + $parsedObject = new self($oidString); + $parsedObject->setContentLength($contentLength); + + return $parsedObject; + } + + /** + * Parses an object identifier except for the first octet, which is parsed + * differently. This way relative object identifiers can also be parsed + * using this. + * + * @param $binaryData + * @param $offsetIndex + * @param $octetsToRead + * + * @throws ParserException + * + * @return string + */ + protected static function parseOid(&$binaryData, &$offsetIndex, $octetsToRead) + { + $oid = ''; + + while ($octetsToRead > 0) { + $octets = ''; + + do { + if (0 === $octetsToRead) { + throw new ParserException('Malformed ASN.1 Object Identifier', $offsetIndex - 1); + } + + $octetsToRead--; + $octet = $binaryData[$offsetIndex++]; + $octets .= $octet; + } while (ord($octet) & 0x80); + + $oid .= sprintf('%d.', Base128::decode($octets)); + } + + // Remove trailing '.' + return substr($oid, 0, -1) ?: ''; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Universal/OctetString.php b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/OctetString.php new file mode 100644 index 000000000..5d69ae7bf --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/OctetString.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use Exception; +use FG\ASN1\ASNObject; +use FG\ASN1\Parsable; +use FG\ASN1\Identifier; + +class OctetString extends ASNObject implements Parsable +{ + protected $value; + + public function __construct($value) + { + if (is_string($value)) { + // remove gaps between hex digits + $value = preg_replace('/\s|0x/', '', $value); + } elseif (is_numeric($value)) { + $value = dechex($value); + } elseif ($value === null) { + return; + } else { + throw new Exception('OctetString: unrecognized input type!'); + } + + if (strlen($value) % 2 != 0) { + // transform values like 1F2 to 01F2 + $value = '0'.$value; + } + + $this->value = $value; + } + + public function getType() + { + return Identifier::OCTETSTRING; + } + + protected function calculateContentLength() + { + return strlen($this->value) / 2; + } + + protected function getEncodedValue() + { + $value = $this->value; + $result = ''; + + //Actual content + while (strlen($value) >= 2) { + // get the hex value byte by byte from the string and and add it to binary result + $result .= chr(hexdec(substr($value, 0, 2))); + $value = substr($value, 2); + } + + return $result; + } + + public function getContent() + { + return strtoupper($this->value); + } + + public function getBinaryContent() + { + return $this->getEncodedValue(); + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + self::parseIdentifier($binaryData[$offsetIndex], Identifier::OCTETSTRING, $offsetIndex++); + $contentLength = self::parseContentLength($binaryData, $offsetIndex); + + $value = substr($binaryData, $offsetIndex, $contentLength); + $offsetIndex += $contentLength; + + $parsedObject = new self(bin2hex($value)); + $parsedObject->setContentLength($contentLength); + + return $parsedObject; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Universal/PrintableString.php b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/PrintableString.php new file mode 100644 index 000000000..fe6d4bc02 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/PrintableString.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\AbstractString; +use FG\ASN1\Identifier; + +class PrintableString extends AbstractString +{ + /** + * Creates a new ASN.1 PrintableString. + * + * The ITU-T X.680 Table 8 permits the following characters: + * Latin capital letters A,B, ... Z + * Latin small letters a,b, ... z + * Digits 0,1, ... 9 + * SPACE (space) + * APOSTROPHE ' + * LEFT PARENTHESIS ( + * RIGHT PARENTHESIS ) + * PLUS SIGN + + * COMMA , + * HYPHEN-MINUS - + * FULL STOP . + * SOLIDUS / + * COLON : + * EQUALS SIGN = + * QUESTION MARK ? + * + * @param string $string + */ + public function __construct($string) + { + $this->value = $string; + $this->allowNumbers(); + $this->allowAllLetters(); + $this->allowSpaces(); + $this->allowCharacters("'", '(', ')', '+', '-', '.', ',', '/', ':', '=', '?'); + } + + public function getType() + { + return Identifier::PRINTABLE_STRING; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Universal/RelativeObjectIdentifier.php b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/RelativeObjectIdentifier.php new file mode 100644 index 000000000..2aa9643a1 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/RelativeObjectIdentifier.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use Exception; +use FG\ASN1\Parsable; +use FG\ASN1\Identifier; +use FG\ASN1\Exception\ParserException; + +class RelativeObjectIdentifier extends ObjectIdentifier implements Parsable +{ + public function __construct($subIdentifiers) + { + $this->value = $subIdentifiers; + $this->subIdentifiers = explode('.', $subIdentifiers); + $nrOfSubIdentifiers = count($this->subIdentifiers); + + for ($i = 0; $i < $nrOfSubIdentifiers; $i++) { + if (is_numeric($this->subIdentifiers[$i])) { + // enforce the integer type + $this->subIdentifiers[$i] = intval($this->subIdentifiers[$i]); + } else { + throw new Exception("[{$subIdentifiers}] is no valid object identifier (sub identifier ".($i + 1).' is not numeric)!'); + } + } + } + + public function getType() + { + return Identifier::RELATIVE_OID; + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + self::parseIdentifier($binaryData[$offsetIndex], Identifier::RELATIVE_OID, $offsetIndex++); + $contentLength = self::parseContentLength($binaryData, $offsetIndex, 1); + + try { + $oidString = self::parseOid($binaryData, $offsetIndex, $contentLength); + } catch (ParserException $e) { + throw new ParserException('Malformed ASN.1 Relative Object Identifier', $e->getOffset()); + } + + $parsedObject = new self($oidString); + $parsedObject->setContentLength($contentLength); + + return $parsedObject; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Sequence.php b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Sequence.php new file mode 100644 index 000000000..0397cf126 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Sequence.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\Construct; +use FG\ASN1\Parsable; +use FG\ASN1\Identifier; + +class Sequence extends Construct implements Parsable +{ + public function getType() + { + return Identifier::SEQUENCE; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Set.php b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Set.php new file mode 100644 index 000000000..6e6d346f7 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Set.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\Identifier; + +class Set extends Sequence +{ + public function getType() + { + return Identifier::SET; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Universal/T61String.php b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/T61String.php new file mode 100644 index 000000000..56418645f --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/T61String.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\AbstractString; +use FG\ASN1\Identifier; + +class T61String extends AbstractString +{ + /** + * Creates a new ASN.1 T61 String. + * TODO The encodable characters of this type are not yet checked. + * + * @see http://en.wikipedia.org/wiki/ITU_T.61 + * + * @param string $string + */ + public function __construct($string) + { + $this->value = $string; + $this->allowAll(); + } + + public function getType() + { + return Identifier::T61_STRING; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Universal/UTCTime.php b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/UTCTime.php new file mode 100644 index 000000000..c4d303cb7 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/UTCTime.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\AbstractTime; +use FG\ASN1\Parsable; +use FG\ASN1\Identifier; +use FG\ASN1\Exception\ParserException; + +/** + * This ASN.1 universal type contains the calendar date and time. + * + * The precision is one minute or one second and optionally a + * local time differential from coordinated universal time. + * + * Decoding of this type will accept the Basic Encoding Rules (BER) + * The encoding will comply with the Distinguished Encoding Rules (DER). + */ +class UTCTime extends AbstractTime implements Parsable +{ + public function getType() + { + return Identifier::UTC_TIME; + } + + protected function calculateContentLength() + { + return 13; // Content is a string o the following format: YYMMDDhhmmssZ (13 octets) + } + + protected function getEncodedValue() + { + return $this->value->format('ymdHis').'Z'; + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + self::parseIdentifier($binaryData[$offsetIndex], Identifier::UTC_TIME, $offsetIndex++); + $contentLength = self::parseContentLength($binaryData, $offsetIndex, 11); + + $format = 'ymdGi'; + $dateTimeString = substr($binaryData, $offsetIndex, 10); + $offsetIndex += 10; + + // extract optional seconds part + if ($binaryData[$offsetIndex] != 'Z' + && $binaryData[$offsetIndex] != '+' + && $binaryData[$offsetIndex] != '-') { + $dateTimeString .= substr($binaryData, $offsetIndex, 2); + $offsetIndex += 2; + $format .= 's'; + } + + $dateTime = \DateTime::createFromFormat($format, $dateTimeString, new \DateTimeZone('UTC')); + + // extract time zone settings + if ($binaryData[$offsetIndex] == '+' + || $binaryData[$offsetIndex] == '-') { + $dateTime = static::extractTimeZoneData($binaryData, $offsetIndex, $dateTime); + } elseif ($binaryData[$offsetIndex++] != 'Z') { + throw new ParserException('Invalid UTC String', $offsetIndex); + } + + $parsedObject = new self($dateTime); + $parsedObject->setContentLength($contentLength); + + return $parsedObject; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Universal/UTF8String.php b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/UTF8String.php new file mode 100644 index 000000000..cba568d33 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/UTF8String.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\AbstractString; +use FG\ASN1\Identifier; + +class UTF8String extends AbstractString +{ + /** + * Creates a new ASN.1 Universal String. + * TODO The encodable characters of this type are not yet checked. + * + * @param string $string + */ + public function __construct($string) + { + $this->value = $string; + $this->allowAll(); + } + + public function getType() + { + return Identifier::UTF8_STRING; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Universal/UniversalString.php b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/UniversalString.php new file mode 100644 index 000000000..0c3fe1d01 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/UniversalString.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\AbstractString; +use FG\ASN1\Identifier; + +class UniversalString extends AbstractString +{ + /** + * Creates a new ASN.1 Universal String. + * TODO The encodable characters of this type are not yet checked. + * + * @see http://en.wikipedia.org/wiki/Universal_Character_Set + * + * @param string $string + */ + public function __construct($string) + { + $this->value = $string; + $this->allowAll(); + } + + public function getType() + { + return Identifier::UNIVERSAL_STRING; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/Universal/VisibleString.php b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/VisibleString.php new file mode 100644 index 000000000..d9326d3fa --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/Universal/VisibleString.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\AbstractString; +use FG\ASN1\Identifier; + +class VisibleString extends AbstractString +{ + /** + * Creates a new ASN.1 Visible String. + * TODO The encodable characters of this type are not yet checked. + * + * @param string $string + */ + public function __construct($string) + { + $this->value = $string; + $this->allowAll(); + } + + public function getType() + { + return Identifier::VISIBLE_STRING; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/UnknownConstructedObject.php b/vendor/fgrosse/phpasn1/lib/ASN1/UnknownConstructedObject.php new file mode 100644 index 000000000..b19a07a18 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/UnknownConstructedObject.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1; + +class UnknownConstructedObject extends Construct +{ + private $identifier; + private $contentLength; + + /** + * @param string $binaryData + * @param int $offsetIndex + * + * @throws \FG\ASN1\Exception\ParserException + */ + public function __construct($binaryData, &$offsetIndex) + { + $this->identifier = self::parseBinaryIdentifier($binaryData, $offsetIndex); + $this->contentLength = self::parseContentLength($binaryData, $offsetIndex); + + $children = []; + $octetsToRead = $this->contentLength; + while ($octetsToRead > 0) { + $newChild = ASNObject::fromBinary($binaryData, $offsetIndex); + $octetsToRead -= $newChild->getObjectLength(); + $children[] = $newChild; + } + + parent::__construct(...$children); + } + + public function getType() + { + return ord($this->identifier); + } + + public function getIdentifier() + { + return $this->identifier; + } + + protected function calculateContentLength() + { + return $this->contentLength; + } + + protected function getEncodedValue() + { + return ''; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/UnknownObject.php b/vendor/fgrosse/phpasn1/lib/ASN1/UnknownObject.php new file mode 100644 index 000000000..4ac536a9b --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/UnknownObject.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1; + +class UnknownObject extends ASNObject +{ + /** @var string */ + private $value; + + private $identifier; + + /** + * @param string|int $identifier Either the first identifier octet as int or all identifier bytes as a string + * @param int $contentLength + */ + public function __construct($identifier, $contentLength) + { + if (is_int($identifier)) { + $identifier = chr($identifier); + } + + $this->identifier = $identifier; + $this->value = "Unparsable Object ({$contentLength} bytes)"; + $this->setContentLength($contentLength); + } + + public function getContent() + { + return $this->value; + } + + public function getType() + { + return ord($this->identifier[0]); + } + + public function getIdentifier() + { + return $this->identifier; + } + + protected function calculateContentLength() + { + return $this->getContentLength(); + } + + protected function getEncodedValue() + { + return ''; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/Utility/BigInteger.php b/vendor/fgrosse/phpasn1/lib/Utility/BigInteger.php new file mode 100644 index 000000000..866162cc0 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/Utility/BigInteger.php @@ -0,0 +1,195 @@ +_fromInteger($val); + } + else { + // convert to string, if not already one + $val = (string)$val; + + // validate string + if (!preg_match('/^-?[0-9]+$/', $val)) { + throw new \InvalidArgumentException('Expects a string representation of an integer.'); + } + $ret->_fromString($val); + } + + return $ret; + } + + /** + * BigInteger constructor. + * Prevent directly instantiating object, use BigInteger::create instead. + */ + protected function __construct() + { + + } + + /** + * Subclasses must provide clone functionality. + * @return BigInteger + */ + abstract public function __clone(); + + /** + * Assign the instance value from base 10 string. + * @param string $str + */ + abstract protected function _fromString($str); + + /** + * Assign the instance value from an integer type. + * @param int $integer + */ + abstract protected function _fromInteger($integer); + + /** + * Must provide string implementation that returns base 10 number. + * @return string + */ + abstract public function __toString(); + + /* INFORMATIONAL FUNCTIONS */ + + /** + * Return integer, if possible. Throws an exception if the number can not be represented as a native integer. + * @return int + * @throws \OverflowException + */ + abstract public function toInteger(); + + /** + * Is represented integer negative? + * @return bool + */ + abstract public function isNegative(); + + /** + * Compare the integer with $number, returns a negative integer if $this is less than number, returns 0 if $this is + * equal to number and returns a positive integer if $this is greater than number. + * @param BigInteger|string|int $number + * @return int + */ + abstract public function compare($number); + + /* MODIFY */ + + /** + * Add another integer $b and returns the result. + * @param BigInteger|string|int $b + * @return BigInteger + */ + abstract public function add($b); + + /** + * Subtract $b from $this and returns the result. + * @param BigInteger|string|int $b + * @return BigInteger + */ + abstract public function subtract($b); + + /** + * Multiply value. + * @param BigInteger|string|int $b + * @return BigInteger + */ + abstract public function multiply($b); + + /** + * The value $this modulus $b. + * @param BigInteger|string|int $b + * @return BigInteger + */ + abstract public function modulus($b); + + /** + * Raise $this to the power of $b and returns the result. + * @param BigInteger|string|int $b + * @return BigInteger + */ + abstract public function toPower($b); + + /** + * Shift the value to the right by a set number of bits and returns the result. + * @param int $bits + * @return BigInteger + */ + abstract public function shiftRight($bits = 8); + + /** + * Shift the value to the left by a set number of bits and returns the result. + * @param int $bits + * @return BigInteger + */ + abstract public function shiftLeft($bits = 8); + + /** + * Returns the absolute value. + * @return BigInteger + */ + abstract public function absoluteValue(); +} diff --git a/vendor/fgrosse/phpasn1/lib/Utility/BigIntegerBcmath.php b/vendor/fgrosse/phpasn1/lib/Utility/BigIntegerBcmath.php new file mode 100644 index 000000000..25ad89164 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/Utility/BigIntegerBcmath.php @@ -0,0 +1,133 @@ +_str = (string)$str; + } + + protected function _fromInteger($integer) + { + $this->_str = (string)$integer; + } + + public function __toString() + { + return $this->_str; + } + + public function toInteger() + { + if ($this->compare(PHP_INT_MAX) > 0 || $this->compare(PHP_INT_MIN) < 0) { + throw new \OverflowException(sprintf('Can not represent %s as integer.', $this->_str)); + } + return (int)$this->_str; + } + + public function isNegative() + { + return bccomp($this->_str, '0', 0) < 0; + } + + protected function _unwrap($number) + { + if ($number instanceof self) { + return $number->_str; + } + return $number; + } + + public function compare($number) + { + return bccomp($this->_str, $this->_unwrap($number), 0); + } + + public function add($b) + { + $ret = new self(); + $ret->_str = bcadd($this->_str, $this->_unwrap($b), 0); + return $ret; + } + + public function subtract($b) + { + $ret = new self(); + $ret->_str = bcsub($this->_str, $this->_unwrap($b), 0); + return $ret; + } + + public function multiply($b) + { + $ret = new self(); + $ret->_str = bcmul($this->_str, $this->_unwrap($b), 0); + return $ret; + } + + public function modulus($b) + { + $ret = new self(); + if ($this->isNegative()) { + // bcmod handles negative numbers differently + $b = $this->_unwrap($b); + $ret->_str = bcsub($b, bcmod(bcsub('0', $this->_str, 0), $b), 0); + } + else { + $ret->_str = bcmod($this->_str, $this->_unwrap($b)); + } + return $ret; + } + + public function toPower($b) + { + $ret = new self(); + $ret->_str = bcpow($this->_str, $this->_unwrap($b), 0); + return $ret; + } + + public function shiftRight($bits = 8) + { + $ret = new self(); + $ret->_str = bcdiv($this->_str, bcpow('2', $bits)); + return $ret; + } + + public function shiftLeft($bits = 8) { + $ret = new self(); + $ret->_str = bcmul($this->_str, bcpow('2', $bits)); + return $ret; + } + + public function absoluteValue() + { + $ret = new self(); + if (-1 === bccomp($this->_str, '0', 0)) { + $ret->_str = bcsub('0', $this->_str, 0); + } + else { + $ret->_str = $this->_str; + } + return $ret; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/Utility/BigIntegerGmp.php b/vendor/fgrosse/phpasn1/lib/Utility/BigIntegerGmp.php new file mode 100644 index 000000000..0791226a5 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/Utility/BigIntegerGmp.php @@ -0,0 +1,133 @@ +_rh = gmp_add($this->_rh, 0); + } + + protected function _fromString($str) + { + $this->_rh = gmp_init($str, 10); + } + + protected function _fromInteger($integer) + { + $this->_rh = gmp_init($integer, 10); + } + + public function __toString() + { + return gmp_strval($this->_rh, 10); + } + + public function toInteger() + { + if ($this->compare(PHP_INT_MAX) > 0 || $this->compare(PHP_INT_MIN) < 0) { + throw new \OverflowException(sprintf('Can not represent %s as integer.', $this)); + } + return gmp_intval($this->_rh); + } + + public function isNegative() + { + return gmp_sign($this->_rh) === -1; + } + + protected function _unwrap($number) + { + if ($number instanceof self) { + return $number->_rh; + } + return $number; + } + + public function compare($number) + { + return gmp_cmp($this->_rh, $this->_unwrap($number)); + } + + public function add($b) + { + $ret = new self(); + $ret->_rh = gmp_add($this->_rh, $this->_unwrap($b)); + return $ret; + } + + public function subtract($b) + { + $ret = new self(); + $ret->_rh = gmp_sub($this->_rh, $this->_unwrap($b)); + return $ret; + } + + public function multiply($b) + { + $ret = new self(); + $ret->_rh = gmp_mul($this->_rh, $this->_unwrap($b)); + return $ret; + } + + public function modulus($b) + { + $ret = new self(); + $ret->_rh = gmp_mod($this->_rh, $this->_unwrap($b)); + return $ret; + } + + public function toPower($b) + { + if ($b instanceof self) { + // gmp_pow accepts just an integer + if ($b->compare(PHP_INT_MAX) > 0) { + throw new \UnexpectedValueException('Unable to raise to power greater than PHP_INT_MAX.'); + } + $b = gmp_intval($b->_rh); + } + $ret = new self(); + $ret->_rh = gmp_pow($this->_rh, $b); + return $ret; + } + + public function shiftRight($bits=8) + { + $ret = new self(); + $ret->_rh = gmp_div($this->_rh, gmp_pow(2, $bits)); + return $ret; + } + + public function shiftLeft($bits=8) + { + $ret = new self(); + $ret->_rh = gmp_mul($this->_rh, gmp_pow(2, $bits)); + return $ret; + } + + public function absoluteValue() + { + $ret = new self(); + $ret->_rh = gmp_abs($this->_rh); + return $ret; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/X509/AlgorithmIdentifier.php b/vendor/fgrosse/phpasn1/lib/X509/AlgorithmIdentifier.php new file mode 100644 index 000000000..a06b56f77 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/X509/AlgorithmIdentifier.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\X509; + +use FG\ASN1\Universal\NullObject; +use FG\ASN1\Composite\AttributeTypeAndValue; + +class AlgorithmIdentifier extends AttributeTypeAndValue +{ + public function __construct($objectIdentifierString) + { + parent::__construct($objectIdentifierString, new NullObject()); + } +} diff --git a/vendor/fgrosse/phpasn1/lib/X509/CSR/Attributes.php b/vendor/fgrosse/phpasn1/lib/X509/CSR/Attributes.php new file mode 100644 index 000000000..5a965e2f8 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/X509/CSR/Attributes.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\X509\CSR; + +use FG\ASN1\ASNObject; +use FG\X509\CertificateExtensions; +use FG\ASN1\OID; +use FG\ASN1\Parsable; +use FG\ASN1\Construct; +use FG\ASN1\Identifier; +use FG\ASN1\Universal\Set; +use FG\ASN1\Universal\Sequence; +use FG\ASN1\Universal\ObjectIdentifier; + +class Attributes extends Construct implements Parsable +{ + public function getType() + { + return 0xA0; + } + + public function addAttribute($objectIdentifier, Set $attribute) + { + if (is_string($objectIdentifier)) { + $objectIdentifier = new ObjectIdentifier($objectIdentifier); + } + $attributeSequence = new Sequence($objectIdentifier, $attribute); + $attributeSequence->getNumberOfLengthOctets(); // length and number of length octets is calculated + $this->addChild($attributeSequence); + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + self::parseIdentifier($binaryData[$offsetIndex], 0xA0, $offsetIndex++); + $contentLength = self::parseContentLength($binaryData, $offsetIndex); + $octetsToRead = $contentLength; + + $parsedObject = new self(); + while ($octetsToRead > 0) { + $initialOffset = $offsetIndex; // used to calculate how much bits have been read + self::parseIdentifier($binaryData[$offsetIndex], Identifier::SEQUENCE, $offsetIndex++); + self::parseContentLength($binaryData, $offsetIndex); + + $objectIdentifier = ObjectIdentifier::fromBinary($binaryData, $offsetIndex); + $oidString = $objectIdentifier->getContent(); + if ($oidString == OID::PKCS9_EXTENSION_REQUEST) { + $attribute = CertificateExtensions::fromBinary($binaryData, $offsetIndex); + } else { + $attribute = ASNObject::fromBinary($binaryData, $offsetIndex); + } + + $parsedObject->addAttribute($objectIdentifier, $attribute); + $octetsToRead -= ($offsetIndex - $initialOffset); + } + + $parsedObject->setContentLength($contentLength); + + return $parsedObject; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/X509/CSR/CSR.php b/vendor/fgrosse/phpasn1/lib/X509/CSR/CSR.php new file mode 100644 index 000000000..69decdbb5 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/X509/CSR/CSR.php @@ -0,0 +1,137 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\X509\CSR; + +use FG\ASN1\OID; +use FG\ASN1\Universal\Integer; +use FG\ASN1\Universal\BitString; +use FG\ASN1\Universal\Sequence; +use FG\X509\CertificateSubject; +use FG\X509\AlgorithmIdentifier; +use FG\X509\PublicKey; + +class CSR extends Sequence +{ + const CSR_VERSION_NR = 0; + + protected $subject; + protected $publicKey; + protected $signature; + protected $signatureAlgorithm; + + protected $startSequence; + + /** + * @param string $commonName + * @param string $email + * @param string $organization + * @param string $locality + * @param string $state + * @param string $country + * @param string $organizationalUnit + * @param string $publicKey + * @param string $signature + * @param string $signatureAlgorithm + */ + public function __construct($commonName, $email, $organization, $locality, $state, $country, $organizationalUnit, $publicKey, $signature, $signatureAlgorithm = OID::SHA1_WITH_RSA_SIGNATURE) + { + $this->subject = new CertificateSubject( + $commonName, + $email, + $organization, + $locality, + $state, + $country, + $organizationalUnit + ); + $this->publicKey = $publicKey; + $this->signature = $signature; + $this->signatureAlgorithm = $signatureAlgorithm; + + $this->createCSRSequence(); + } + + protected function createCSRSequence() + { + $versionNr = new Integer(self::CSR_VERSION_NR); + $publicKey = new PublicKey($this->publicKey); + $signature = new BitString($this->signature); + $signatureAlgorithm = new AlgorithmIdentifier($this->signatureAlgorithm); + + $certRequestInfo = new Sequence($versionNr, $this->subject, $publicKey); + + $this->addChild($certRequestInfo); + $this->addChild($signatureAlgorithm); + $this->addChild($signature); + } + + public function __toString() + { + $tmp = base64_encode($this->getBinary()); + + for ($i = 0; $i < strlen($tmp); $i++) { + if (($i + 2) % 65 == 0) { + $tmp = substr($tmp, 0, $i + 1)."\n".substr($tmp, $i + 1); + } + } + + $result = '-----BEGIN CERTIFICATE REQUEST-----'.PHP_EOL; + $result .= $tmp.PHP_EOL; + $result .= '-----END CERTIFICATE REQUEST-----'; + + return $result; + } + + public function getVersion() + { + return self::CSR_VERSION_NR; + } + + public function getOrganizationName() + { + return $this->subject->getOrganization(); + } + + public function getLocalName() + { + return $this->subject->getLocality(); + } + + public function getState() + { + return $this->subject->getState(); + } + + public function getCountry() + { + return $this->subject->getCountry(); + } + + public function getOrganizationalUnit() + { + return $this->subject->getOrganizationalUnit(); + } + + public function getPublicKey() + { + return $this->publicKey; + } + + public function getSignature() + { + return $this->signature; + } + + public function getSignatureAlgorithm() + { + return $this->signatureAlgorithm; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/X509/CertificateExtensions.php b/vendor/fgrosse/phpasn1/lib/X509/CertificateExtensions.php new file mode 100644 index 000000000..6ed1c6a7c --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/X509/CertificateExtensions.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\X509; + +use FG\ASN1\Exception\ParserException; +use FG\ASN1\OID; +use FG\ASN1\ASNObject; +use FG\ASN1\Parsable; +use FG\ASN1\Identifier; +use FG\ASN1\Universal\OctetString; +use FG\ASN1\Universal\Set; +use FG\ASN1\Universal\Sequence; +use FG\ASN1\Universal\ObjectIdentifier; +use FG\X509\SAN\SubjectAlternativeNames; + +class CertificateExtensions extends Set implements Parsable +{ + private $innerSequence; + private $extensions = []; + + public function __construct() + { + $this->innerSequence = new Sequence(); + parent::__construct($this->innerSequence); + } + + public function addSubjectAlternativeNames(SubjectAlternativeNames $sans) + { + $this->addExtension(OID::CERT_EXT_SUBJECT_ALT_NAME, $sans); + } + + private function addExtension($oidString, ASNObject $extension) + { + $sequence = new Sequence(); + $sequence->addChild(new ObjectIdentifier($oidString)); + $sequence->addChild($extension); + + $this->innerSequence->addChild($sequence); + $this->extensions[] = $extension; + } + + public function getContent() + { + return $this->extensions; + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + self::parseIdentifier($binaryData[$offsetIndex], Identifier::SET, $offsetIndex++); + self::parseContentLength($binaryData, $offsetIndex); + + $tmpOffset = $offsetIndex; + $extensions = Sequence::fromBinary($binaryData, $offsetIndex); + $tmpOffset += 1 + $extensions->getNumberOfLengthOctets(); + + $parsedObject = new self(); + foreach ($extensions as $extension) { + if ($extension->getType() != Identifier::SEQUENCE) { + //FIXME wrong offset index + throw new ParserException('Could not parse Certificate Extensions: Expected ASN.1 Sequence but got '.$extension->getTypeName(), $offsetIndex); + } + + $tmpOffset += 1 + $extension->getNumberOfLengthOctets(); + $children = $extension->getChildren(); + if (count($children) < 2) { + throw new ParserException('Could not parse Certificate Extensions: Needs at least two child elements per extension sequence (object identifier and octet string)', $tmpOffset); + } + /** @var \FG\ASN1\ASNObject $objectIdentifier */ + $objectIdentifier = $children[0]; + + /** @var OctetString $octetString */ + $octetString = $children[1]; + + if ($objectIdentifier->getType() != Identifier::OBJECT_IDENTIFIER) { + throw new ParserException('Could not parse Certificate Extensions: Expected ASN.1 Object Identifier but got '.$extension->getTypeName(), $tmpOffset); + } + + $tmpOffset += $objectIdentifier->getObjectLength(); + + if ($objectIdentifier->getContent() == OID::CERT_EXT_SUBJECT_ALT_NAME) { + $sans = SubjectAlternativeNames::fromBinary($binaryData, $tmpOffset); + $parsedObject->addSubjectAlternativeNames($sans); + } else { + // can now only parse SANs. There might be more in the future + $tmpOffset += $octetString->getObjectLength(); + } + } + + $parsedObject->getBinary(); // Determine the number of content octets and object sizes once (just to let the equality unit tests pass :/ ) + return $parsedObject; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/X509/CertificateSubject.php b/vendor/fgrosse/phpasn1/lib/X509/CertificateSubject.php new file mode 100644 index 000000000..0a04d5740 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/X509/CertificateSubject.php @@ -0,0 +1,108 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\X509; + +use FG\ASN1\Composite\RelativeDistinguishedName; +use FG\ASN1\Identifier; +use FG\ASN1\OID; +use FG\ASN1\Parsable; +use FG\ASN1\Composite\RDNString; +use FG\ASN1\Universal\Sequence; + +class CertificateSubject extends Sequence implements Parsable +{ + private $commonName; + private $email; + private $organization; + private $locality; + private $state; + private $country; + private $organizationalUnit; + + /** + * @param string $commonName + * @param string $email + * @param string $organization + * @param string $locality + * @param string $state + * @param string $country + * @param string $organizationalUnit + */ + public function __construct($commonName, $email, $organization, $locality, $state, $country, $organizationalUnit) + { + parent::__construct( + new RDNString(OID::COUNTRY_NAME, $country), + new RDNString(OID::STATE_OR_PROVINCE_NAME, $state), + new RDNString(OID::LOCALITY_NAME, $locality), + new RDNString(OID::ORGANIZATION_NAME, $organization), + new RDNString(OID::OU_NAME, $organizationalUnit), + new RDNString(OID::COMMON_NAME, $commonName), + new RDNString(OID::PKCS9_EMAIL, $email) + ); + + $this->commonName = $commonName; + $this->email = $email; + $this->organization = $organization; + $this->locality = $locality; + $this->state = $state; + $this->country = $country; + $this->organizationalUnit = $organizationalUnit; + } + + public function getCommonName() + { + return $this->commonName; + } + + public function getEmail() + { + return $this->email; + } + + public function getOrganization() + { + return $this->organization; + } + + public function getLocality() + { + return $this->locality; + } + + public function getState() + { + return $this->state; + } + + public function getCountry() + { + return $this->country; + } + + public function getOrganizationalUnit() + { + return $this->organizationalUnit; + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + self::parseIdentifier($binaryData[$offsetIndex], Identifier::SEQUENCE, $offsetIndex++); + $contentLength = self::parseContentLength($binaryData, $offsetIndex); + + $names = []; + $octetsToRead = $contentLength; + while ($octetsToRead > 0) { + $relativeDistinguishedName = RelativeDistinguishedName::fromBinary($binaryData, $offsetIndex); + $octetsToRead -= $relativeDistinguishedName->getObjectLength(); + $names[] = $relativeDistinguishedName; + } + } +} diff --git a/vendor/fgrosse/phpasn1/lib/X509/PrivateKey.php b/vendor/fgrosse/phpasn1/lib/X509/PrivateKey.php new file mode 100644 index 000000000..d57ad8659 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/X509/PrivateKey.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\X509; + +use FG\ASN1\OID; +use FG\ASN1\Universal\NullObject; +use FG\ASN1\Universal\Sequence; +use FG\ASN1\Universal\BitString; +use FG\ASN1\Universal\ObjectIdentifier; + +class PrivateKey extends Sequence +{ + /** + * @param string $hexKey + * @param \FG\ASN1\ASNObject|string $algorithmIdentifierString + */ + public function __construct($hexKey, $algorithmIdentifierString = OID::RSA_ENCRYPTION) + { + parent::__construct( + new Sequence( + new ObjectIdentifier($algorithmIdentifierString), + new NullObject() + ), + new BitString($hexKey) + ); + } +} diff --git a/vendor/fgrosse/phpasn1/lib/X509/PublicKey.php b/vendor/fgrosse/phpasn1/lib/X509/PublicKey.php new file mode 100644 index 000000000..ab8b4514b --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/X509/PublicKey.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\X509; + +use FG\ASN1\OID; +use FG\ASN1\Universal\NullObject; +use FG\ASN1\Universal\Sequence; +use FG\ASN1\Universal\BitString; +use FG\ASN1\Universal\ObjectIdentifier; + +class PublicKey extends Sequence +{ + /** + * @param string $hexKey + * @param \FG\ASN1\ASNObject|string $algorithmIdentifierString + */ + public function __construct($hexKey, $algorithmIdentifierString = OID::RSA_ENCRYPTION) + { + parent::__construct( + new Sequence( + new ObjectIdentifier($algorithmIdentifierString), + new NullObject() + ), + new BitString($hexKey) + ); + } +} diff --git a/vendor/fgrosse/phpasn1/lib/X509/SAN/DNSName.php b/vendor/fgrosse/phpasn1/lib/X509/SAN/DNSName.php new file mode 100644 index 000000000..502738b01 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/X509/SAN/DNSName.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\X509\SAN; + +use FG\ASN1\Universal\GeneralString; + +class DNSName extends GeneralString +{ + const IDENTIFIER = 0x82; // not sure yet why this is the identifier used in SAN extensions + + public function __construct($dnsNameString) + { + parent::__construct($dnsNameString); + } + + public function getType() + { + return self::IDENTIFIER; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/X509/SAN/IPAddress.php b/vendor/fgrosse/phpasn1/lib/X509/SAN/IPAddress.php new file mode 100644 index 000000000..f55be95bb --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/X509/SAN/IPAddress.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\X509\SAN; + +use FG\ASN1\ASNObject; +use FG\ASN1\Parsable; +use FG\ASN1\Exception\ParserException; + +class IPAddress extends ASNObject implements Parsable +{ + const IDENTIFIER = 0x87; // not sure yet why this is the identifier used in SAN extensions + + /** @var string */ + private $value; + + public function __construct($ipAddressString) + { + $this->value = $ipAddressString; + } + + public function getType() + { + return self::IDENTIFIER; + } + + public function getContent() + { + return $this->value; + } + + protected function calculateContentLength() + { + return 4; + } + + protected function getEncodedValue() + { + $ipParts = explode('.', $this->value); + $binary = chr($ipParts[0]); + $binary .= chr($ipParts[1]); + $binary .= chr($ipParts[2]); + $binary .= chr($ipParts[3]); + + return $binary; + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + self::parseIdentifier($binaryData[$offsetIndex], self::IDENTIFIER, $offsetIndex++); + $contentLength = self::parseContentLength($binaryData, $offsetIndex); + if ($contentLength != 4) { + throw new ParserException("A FG\\X509\SAN\IPAddress should have a content length of 4. Extracted length was {$contentLength}", $offsetIndex); + } + + $ipAddressString = ord($binaryData[$offsetIndex++]).'.'; + $ipAddressString .= ord($binaryData[$offsetIndex++]).'.'; + $ipAddressString .= ord($binaryData[$offsetIndex++]).'.'; + $ipAddressString .= ord($binaryData[$offsetIndex++]); + + $parsedObject = new self($ipAddressString); + $parsedObject->getObjectLength(); + + return $parsedObject; + } +} diff --git a/vendor/fgrosse/phpasn1/lib/X509/SAN/SubjectAlternativeNames.php b/vendor/fgrosse/phpasn1/lib/X509/SAN/SubjectAlternativeNames.php new file mode 100644 index 000000000..271ddde7b --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/X509/SAN/SubjectAlternativeNames.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\X509\SAN; + +use FG\ASN1\Exception\ParserException; +use FG\ASN1\ASNObject; +use FG\ASN1\OID; +use FG\ASN1\Parsable; +use FG\ASN1\Identifier; +use FG\ASN1\Universal\Sequence; + +/** + * See section 8.3.2.1 of ITU-T X.509. + */ +class SubjectAlternativeNames extends ASNObject implements Parsable +{ + private $alternativeNamesSequence; + + public function __construct() + { + $this->alternativeNamesSequence = new Sequence(); + } + + protected function calculateContentLength() + { + return $this->alternativeNamesSequence->getObjectLength(); + } + + public function getType() + { + return Identifier::OCTETSTRING; + } + + public function addDomainName(DNSName $domainName) + { + $this->alternativeNamesSequence->addChild($domainName); + } + + public function addIP(IPAddress $ip) + { + $this->alternativeNamesSequence->addChild($ip); + } + + public function getContent() + { + return $this->alternativeNamesSequence->getContent(); + } + + protected function getEncodedValue() + { + return $this->alternativeNamesSequence->getBinary(); + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + self::parseIdentifier($binaryData[$offsetIndex], Identifier::OCTETSTRING, $offsetIndex++); + $contentLength = self::parseContentLength($binaryData, $offsetIndex); + + if ($contentLength < 2) { + throw new ParserException('Can not parse Subject Alternative Names: The Sequence within the octet string after the Object identifier '.OID::CERT_EXT_SUBJECT_ALT_NAME." is too short ({$contentLength} octets)", $offsetIndex); + } + + $offsetOfSequence = $offsetIndex; + $sequence = Sequence::fromBinary($binaryData, $offsetIndex); + $offsetOfSequence += $sequence->getNumberOfLengthOctets() + 1; + + if ($sequence->getObjectLength() != $contentLength) { + throw new ParserException('Can not parse Subject Alternative Names: The Sequence length does not match the length of the surrounding octet string', $offsetIndex); + } + + $parsedObject = new self(); + /** @var \FG\ASN1\ASNObject $object */ + foreach ($sequence as $object) { + if ($object->getType() == DNSName::IDENTIFIER) { + $domainName = DNSName::fromBinary($binaryData, $offsetOfSequence); + $parsedObject->addDomainName($domainName); + } elseif ($object->getType() == IPAddress::IDENTIFIER) { + $ip = IPAddress::fromBinary($binaryData, $offsetOfSequence); + $parsedObject->addIP($ip); + } else { + throw new ParserException('Could not parse Subject Alternative Name: Only DNSName and IP SANs are currently supported', $offsetIndex); + } + } + + $parsedObject->getBinary(); // Determine the number of content octets and object sizes once (just to let the equality unit tests pass :/ ) + return $parsedObject; + } +} diff --git a/vendor/predis/predis/CHANGELOG.md b/vendor/predis/predis/CHANGELOG.md new file mode 100644 index 000000000..11fd9dbe9 --- /dev/null +++ b/vendor/predis/predis/CHANGELOG.md @@ -0,0 +1,1078 @@ +v1.1.6 (2020-09-11) +================================================================================ + +- __FIX__: reverted support for sentinels authentication implemented in v1.1.5 +as it was bugged (see ISSUE #658), sorry for the trouble. This is now postponed +as it requires a more thorough investigation. + + +v1.1.5 (2020-09-10) +================================================================================ + +- __FIX__:~~authentication for sentinels is now supported, previously it was not +possible to specify a `password` for sentinels as its value was stripped during +initialization because sentinels did not support authentication until Redis 5. +**Please note** that with the current implementation each sentinel must have +its own `password` parameter set in the parameters list despite this password is +the same for all sentinels (read how `requirepass` works on the Redis docs). In +this case you should avoid using the global `parameters` client option used to +set default parameters for every connection created by Predis as this would end +up using the same password even when connecting to actual Redis nodes.~~ + +- __FIX__: the username is now correctly retrieved from the userinfo fragment of +the URI when using the "redis" scheme and a "username:password" pair is present. +Values retrieved from the userinfo fragment always override the ones specified +in `username` and `password` if those fields are present in the query string. + +- __FIX__: `Predis\Connection\WebdisConnection` was unable to connect to Webdis +when using an IPv4 address in the URL and this is probably due to some change in +cURL internals since the last time we tested it. + +- __FIX__: an exception is thrown whe passing `FALSE` or any value evaluating to +`FALSE` to the `replication` client option. This was supposed to be unsupported, +in fact it actually breaks client initialization and raises a PHP warning. Now +the user is alerted with an `InvalidArgumentException` and a proper message. +(PR #381). + + +v1.1.4 (2020-08-31) +================================================================================ + +- Improved @method annotations for methods responding to Redis commands defined + by `Predis\ClientInterface` and `Predis\ClientContextInterface`. (PR #456 and + PR #497, other fixes applied after further analysys). + +- __FIX__: the client can now handle ACL authentication when connecting to Redis + 6.x simply by passing both `username` and `password` to connection parameters. + See [the Redis docs](https://redis.io/topics/acl) for details on this topic. + +- __FIX__: NULL or zero-length string values passed to `password` and `database` + in the connection parameters list do not trigger spurious `AUTH` and `SELECT` + commands anymore when connecting to Redis (ISSUE #436). + +- __FIX__: initializing an iteration over a client instance when it is connected + to a standalone Redis server will not throw an exception anymore, instead it + will return an iterator that will run for just one loop returning a new client + instance using the underlying single-node connection (ISSUE #552, PR #556). + +- __FIX__: `Predis\Cluster\Distributor\HashRingaddNodeToRing()` was calculating + the hash required for distribution by using `crc32()` directly instead of the + method `Predis\Cluster\Hash\HashGeneratorInterface::hash()` implemented by the + class itself. This bug fix does not have any impact on existing clusters that + use client-side sharding based on this distributor simply because it does not + take any external hash generators so distribution is not going to be affected. + +- __FIX__: `SORT` now always trigger a switch to the master node in replication + configurations instead of just when the `STORE` modifier is specified, this is + because `SORT` is always considered to be a write operation and actually fails + with a `-READONLY` error response when executed against a replica node. (ISSUE + #554). + + +v1.1.3 (2020-08-18) +================================================================================ + +- Ensure compatibility with PHP 8. + +- Moved repository from `github.com/nrk/predis` to `github.com/predis/predis`. + +- __FIX__: Moved `cweagans/composer-patches` dependency to `require-dev`. + +- __FIX__: Include PHPUnit `.patch` files in exports. + + +v1.1.2 (2020-08-11) +================================================================================ + +- __FIX__: pure CRC16 implementation failed to calculate the correct hash when + the input value passed to the `hash()` method is an integer (PR #450). + +- __FIX__: make PHP iterator abstractions for `ZSCAN` and `HSCAN` working with + PHP 7.2 due to a breaking change, namely the removal of `each()` (PR #448). + + +v1.1.1 (2016-06-16) +================================================================================ + +- __FIX__: `password` and `database` from the global `parameters` client option + were still being applied to sentinels connections making them fail (sentinels + do not understand the `AUTH` and `SELECT` commands) (PR #346). + +- __FIX__: when a sentinel instance reports no sentinel for a service, invoking + `connect()` on the redis-sentinel connection backend should fall back to the + master connection instead of failing (ISSUE #342). + +- __FIX__: the two connection backends based on ext-phpiredis has some kind of + issues with the GC and the internal use of closures as reader callbacks that + prevented connections going out of scope from being properly collected and the + underlying stream or socket resources from being closed and freed. This should + not have had any actual effect in real-world scenarios due to the lifecycle of + PHP scripts, but we fixed it anyway (ISSUE #345). + + +v1.1.0 (2016-06-02) +================================================================================ + +- The default server profile for the client now targets Redis 3.2. + +- Responses to the following commands are not casted into booleans anymore, the + original integer value is returned: `SETNX`, `MSETNX`, `SMOVE`, `SISMEMBER`, + `HSET`, `HSETNX`, `HEXISTS`, `PFADD`, `EXISTS`, `MOVE`, `PERSIST`, `EXPIRE`, + `EXPIREAT`, `RENAMENX`. This change does not have a significant impact unless + when using strict comparisons (=== and !==) the returned value. + +- Non-boolean string values passed to the `persistent` connection parameter can + be used to create different persistent connections. Note that this feature was + already present in Predis but required both `persistent` and `path` to be set + as illustrated by [#139](https://github.com/nrk/predis/pull/139). This change + is needed to prevent confusion with how `path` is used to select a database + when using the `redis` scheme. + +- The client throws exceptions when Redis returns any kind of error response to + initialization commands (the ones being automatically sent when a connection + is established, such as `SELECT` and `AUTH` when database and password are set + in connection parameters) regardless of the value of the exception option. + +- Using `unix:///path/to/socket` in URI strings to specify a UNIX domain socket + file is now deprecated in favor of the format `unix:/path/to/socket` (note the + lack of the double slash after the scheme) and will not be supported starting + with the next major release. + +- Implemented full support for redis-sentinel. + +- Implemented the ability to specify default connection parameters for aggregate + connections with the new `parameters` client option. These parameters augment + the usual user-supplied connection parameters (but do not take the precedence + over them) when creating new connections and they are mostly useful when the + client is using aggregate connections such as redis-cluster and redis-sentinel + as these backends can create new connections on the fly based on responses and + redirections from Redis. + +- Redis servers protected by SSL-encrypted connections can be accessed by using + the `tls` or `rediss` scheme in connection parameters along with SSL-specific + options in the `ssl` parameter (see http://php.net/manual/context.ssl.php). + +- `Predis\Client` implements `IteratorAggregate` making it possible to iterate + over traversable aggregate connections and get a new client instance for each + Redis node. + +- Iterating over an instance of `Predis\Connection\Aggregate\RedisCluster` will + return all the connections mapped in the slots map instead of just the ones in + the pool. This change makes it possible, when the slots map is retrieved from + Redis, to iterate over all of the master nodes in the cluster. When the use of + `CLUSTER SLOTS` is disabled via the `useClusterSlots()` method, the iteration + returns only the connections with slots ranges associated in their parameters + or the ones initialized by `-MOVED` responses in order to make the behaviour + of the iteration consistent between the two modes of operation. + +- Various improvements to `Predis\Connection\Aggregate\MasterSlaveReplication` + (the "basic" replication backend, not the new one based on redis-sentinel): + + - When the client is not able to send a read-only command to a slave because + the current connection fails or the slave is resyncing (`-LOADING` response + returned by Redis), the backend discards the failed connection and performs + a new attempt on the next slave. When no other slave is available the master + server is used for read-only commands as last resort. + + - It is possible to discover the current replication configuration on the fly + by invoking the `discover()` method which internally relies on the output of + the command `INFO REPLICATION` executed against the master server or one of + the slaves. The backend can also be configured to do this automatically when + it fails to reach one of the servers. + + - Implemented the `switchToMaster()` and `switchToSlave()` methods to make it + easier to force a switch to the master server or a random slave when needed. + + +v1.0.4 (2016-05-30) +================================================================================ + +- Added new profile for Redis 3.2 with its new commands: `HSTRLEN`, `BITFIELD`, + `GEOADD`, `GEOHASH`, `GEOPOS`, `GEODIST`, `GEORADIUS`, `GEORADIUSBYMEMBER`. + The default server profile for Predis is still the one for Redis 3.0 you must + set the `profile` client option to `3.2` when initializing the client in order + to be able to use them when connecting to Redis 3.2. + +- Various improvements in the handling of redis-cluster: + + - If the connection to a specific node fails when executing a command, the + client tries to connect to another node in order to refresh the slots map + and perform a new attempt to execute the command. + + - Connections to nodes can be preassigned to non-contiguous slot ranges via + the `slots` parameter using a comma separator. This is how it looks like + in practice: `tcp://127.0.0.1:6379?slots=0-5460,5500-5600,11000`. + +- __FIX__: broken values returned by `Predis\Collection\Iterator\HashKey` when + iterating hash keys containing integer fields (PR #330, ISSUE #331). + +- __FIX__: prevent failures when `Predis\Connection\StreamConnection` serializes + commands with holes in their arguments (e.g. `[0 => 'key:0', 2 => 'key:2']`). + The same fix has been applied to `Predis\Protocol\Text\RequestSerializer`. + (ISSUE #316). + + +v1.0.3 (2015-07-30) +================================================================================ + +- __FIX__: the previous release introduced a severe regression on HHVM that made + the library unable to connect to Redis when using IPv4 addresses. Code running + on the standard PHP interpreter is not affected. + + +v1.0.2 (2015-07-30) +================================================================================ + +- IPv6 is now fully supported. + +- Added `redis` as an accepted scheme for connection parameters. When using this + scheme, the rules used to parse URI strings match the provisional registration + [published by IANA](http://www.iana.org/assignments/uri-schemes/prov/redis). + +- Added new or missing commands: `HSTRLEN` (>= 3.2), `ZREVRANGEBYLEX` (>= 2.8) + and `MIGRATE` (>= 2.6). + +- Implemented support for the `ZADD` modifiers `NX|XX`, `CH`, `INCR` (Redis >= + 3.0.2) using the simplified signature where scores and members are passed as + a named array. + +- __FIX__: `Predis\Configuration\Options` must not trigger the autoloader when + option values are strings (ISSUE #257). + +- __FIX__: `BITPOS` was not defined in the key-prefix processor (ISSUE #265) and + in the replication strategy. + + +v1.0.1 (2015-01-02) +================================================================================ + +- Added `BITPOS` to the server profile for Redis 2.8. + +- Connection timeout for read/write operations can now be set for UNIX sockets + where the underlying connection uses PHP's stream. + +- __FIX__: broken values returned by `Predis\Collection\Iterator\SortedSetKey` + when iterating sorted set containing integer members (ISSUE #216). + +- __FIX__: applied a minor workaround for a bug in old versions of PHP < 5.3.9 + affecting inheritance. + +- __FIX__: prevent E_NOTICE warnings when using INFO [section] returns an empty + response due to an unsupported specific set of information requested to Redis. + + +v1.0.0 (2014-08-01) +================================================================================ + +- Switched to PSR-4 for autoloading. + +- The default server profile for Redis is `3.0`. + +- Removed server profile for Redis 1.2. + +- Added `SENTINEL` to the profile for Redis 2.6 and `PUBSUB` to the profile for + Redis 2.8. + +- `Predis\Client` can now send raw commands using `Predis\Client::executeRaw()`. + +- Status responses are returned as instances of `Predis\Response\Status`, for + example +OK is not returned as boolean TRUE anymore which is a breaking change + for those using strict comparisons. Status responses can be casted to string + values carrying the original payload, so one can do `$response == 'OK'` which + is also more akin to how Redis replies to clients. + +- Commands `ZRANGE`, `ZRANGEBYSCORE`, `ZREVRANGE` and `ZREVRANGEBYSCORE` using + `WITHSCORE` return a named array of member => score instead of using an array + of [member, score] elements. Insertion order is preserved anyway due to how + PHP works internally. + +- The command `ZSCAN` returns a named array of member => score instead of using + an array of [member, score] elements. Insertion order is preserved anyway due + to how PHP works internally. + +- The rules for redis-cluster are now leveraged for empty key tags when using + client-side sharding, which means that when one or the first occurrence of {} + is found in a key it will most likely produce a different hash than previous + versions of Predis thus leading to a different partitioning in these cases. + +- Invoking `Predis\Client::connect()` when the underlying connection has been + already established does not throw any exception anymore, now the connection + simply does not attempt to perform any operation. + +- Added the `aggregate` client option, useful to fully customize how the client + should aggregate multiple connections when an array of connection parameters + is passed to `Predis\Client::__construct()`. + +- Dropped support for streamable multibulk responses. Actually we still ship the + iterator response classes just in case anyone would want to build custom stuff + at a level lower than the client abstraction (our standard and composable text + protocol processors still handle them and can be used as an example). + +- Simplified the implementation of connection parameters by removing method used + to cast to int / bool / float certain parameters supplied by users. Casting + values, if deemed necessary, should be done by the consumer or you can just + subclass `Predis\Connection\Parameters` and override the `filter()` method. + +- Changed a couple of options for our transaction abstraction: + + - `exceptions`: overrides the value of the client option with the same name. + Please note that it does not affect all the transaction control commands + such as `MULTI`, `EXEC`, `DISCARD`, `WATCH` and `UNWATCH`. + - `on_retry`: this option has been removed. + +- Removed pipeline executors, now command pipelines can be easily customized by + extending the standard `Predis\Pipeline\Pipeline` class. Accepted options when + creating a pipeline using `Predis\Client::pipeline()` are: + + - `atomic`: returns a pipeline wrapped in a MULTI / EXEC transaction + (class: `Predis\Pipeline\Atomic`). + - `fire-and-forget`: returns a pipeline that does not read back responses + (class: `Predis\Pipeline\FireAndForget`). + +- Renamed the two base abstract command classes: + + - `Predis\Command\AbstractCommand` is now `Predis\Command\Command` + - `Predis\Command\ScriptedCommand` is now `Predis\Command\ScriptCommand` + +- Dropped `Predis\Command\Command::__toString()` (see issue #151). + +- The key prefixing logic has been moved from command classes to the key prefix + processor. Developers can define or override handlers used to prefix keys, but + they can also define the needed logic in their command classes by implementing + `Predis\Command\PrefixableCommandInterface` just like before. + +- `Predis\PubSub\DispatcherLoop` now takes a `Predis\PubSub\Consumer` instance + as the sole argument of its constructor instead of `Predis\ClientInterface`. + +- All of the interfaces and classes related to translated Redis response types + have been moved in the new `Predis\Response` namespace and most of them have + been renamed to make their fully-qualified name less redundant. Now the base + response interface is `Predis\Response\ResponseInterface`. + +- Renamed interface `Predis\Command\Processor\CommandProcessorInterface` to a + shorter `Predis\Command\Processor\ProcessorInterface`. Also removed interface + for chain processors since it is basically useless. + +- Renamed `Predis\ExecutableContextInterface` to `Predis\ClientContextInterface` + and augmented it with a couple of required methods since this interface is no + more comparable to a basic client as it could be misleading. + +- The `Predis\Option` namespace is now known as `Predis\Configuration` and have + a fully-reworked `Options` class with the ability to lazily initialize values + using objects that responds to `__invoke()` (not all the kinds of callables) + even for custom options defined by the user. + +- Renamed `Predis\Connection\ConnectionInterface::writeCommand()` into + `writeRequest()` for consistency with its counterpart, `readResponse()`. + +- Renamed `Predis\Connection\SingleConnectionInterface::pushInitCommand()` into + `addConnectCommand()` which is more obvious. + +- Renamed the connection class based on both ext-phpiredis and ext-socket into + `Predis\Connection\PhpiredisSocketConnection`. The one based on PHP's streams + is still named `Predis\Connection\PhpiredisStreamConnection`. + +- Renamed the connection factory class to `Predis\Connection\Factory`. Now its + constructor does not require anymore a profile instance to create `AUTH` and + `SELECT` commands when parameters contain both `password` and `database`. Raw + commands will be used instead. + +- Renamed the connection parameters class to `Predis\Connection\Parameters`. Now + its constructor accepts only named arrays, but instances can still be created + using both URIs or arrays using the static method `Parameters::create()`. + +- The profile factory code has been extracted from the abstract Redis profile + class and now lives in `Predis\Profile\Factory`. + +- The `Predis\Connection` namespace has been completely reorganized by renaming + a few classes and interfaces and adding some sub-namespaces. + +- Most classes and interfaces in the `Predis\Protocol` namespace have been moved + or renamed while rationalizing the whole API for external protocol processors. + + +v0.8.7 (2014-08-01) +================================================================================ + +- Added `3.0` in the server profiles aliases list for Redis 3.0. `2.8` is still + the default server profile and `dev` still targets Redis 3.0. + +- Added `COMMAND` to the server profile for Redis 2.8. + +- Switched internally to the `CLUSTER SLOTS` command instead of `CLUSTER NODES` + to fetch the updated slots map from redis-cluster. This change requires users + to upgrade Redis nodes to >= 3.0.0b7. + +- The updated slots map is now fetched automatically from redis-cluster upon the + first `-MOVED` response by default. This change makes it possible to feed the + client constructor with only a few nodes of the actual cluster composition, + without needing a more complex configuration. + +- Implemented support for `PING` in PUB/SUB loop for Redis >= 3.0.0b8. + +- The default client-side sharding strategy and the one for redis-cluster now + share the same implementations as they follow the same rules. One difference, + aside from the different hashing function used to calculate distribution, is + in how empty hash tags like {} are treated by redis-cluster. + +- __FIX__: the patch applied to fix #180 introduced a regression affecting read/ + write timeouts in `Predis\Connection\PhpiredisStreamConnection`. Unfortunately + the only possible solution requires PHP 5.4+. On PHP 5.3, read/write timeouts + will be ignored from now on. + + +v0.8.6 (2014-07-15) +================================================================================ + +- Redis 2.8 is now the default server profile as there are no changes that would + break compatibility with previous releases. + +- Added `PFADD`, `PFCOUNT`, `PFMERGE` to the server profile for Redis 2.8 for + handling the HyperLogLog data structure introduced in Redis 2.8.9. + +- Added `ZLEXCOUNT`, `ZRANGEBYLEX`, `ZREMRANGEBYLEX` to the server profile for + Redis 2.8 for handling lexicographic operations on members of sorted sets. + +- Added support for key hash tags when using redis-cluster (Redis 3.0.0b1). + +- __FIX__: minor tweaks to make Predis compatible with HHVM >= 2.4.0. + +- __FIX__: responses to `INFO` are now properly parsed and will not break when + redis sentinel is being used (ISSUE #154). + +- __FIX__: added missing support for `INCRBYFLOAT` in cluster and replication + configurations (ISSUE #159). + +- __FIX__: fix parsing of the output of `CLUSTER NODES` to fetch the slots map + from a node when redis-cluster has slaves in its configuration (ISSUE #165). + +- __FIX__: prevent a stack overflow when iterating over large Redis collections + using our abstraction for cursor-based iterators (ISSUE #182). + +- __FIX__: properly discards transactions when the server immediately returns an + error response (e.g. -OOM or -ERR on invalid arguments for a command) instead + of a +QUEUED response (ISSUE #187). + +- Upgraded to PHPUnit 4.* for the test suite. + + +v0.8.5 (2014-01-16) +================================================================================ + +- Added `2.8` in the server profiles aliases list for Redis 2.8. `2.6` is still + the default server profile and `dev` now targets Redis 3.0. + +- Added `SCAN`, `SSCAN`, `ZSCAN`, `HSCAN` to the server profile for Redis 2.8. + +- Implemented PHP iterators for incremental iterations over Redis collections: + + - keyspace (cursor-based iterator using `SCAN`) + - sets (cursor-based iterator using `SSCAN`) + - sorted sets (cursor-based iterator using `ZSCAN`) + - hashes (cursor-based iterator using `HSCAN`) + - lists (plain iterator using `LRANGE`) + +- It is now possible to execute "raw commands" using `Predis\Command\RawCommand` + and a variable list of command arguments. Input arguments are not filtered and + responses are not parsed, which means arguments must follow the signature of + the command as defined by Redis and complex responses are left untouched. + +- URI parsing for connection parameters has been improved and has slightly less + overhead when the number of fields in the querystring grows. New features are: + + - Parsing does not break when value of a field contains one or more "=". + - Repeated fieldnames using [] produce an array of values. + - Empty or incomplete "key=value" pairs result in an empty string for "key". + +- Various improvements and fixes to the redis-cluster connection backend: + + - __FIX__: the `ASKING` command is sent upon -ASK redirections. + - An updated slots-map can be fetched from nodes using the `CLUSTER NODES` + command. By default this is a manual operation but can be enabled to get + automatically done upon -MOVED redirections. + - It is possible to specify a common set of connection parameters that are + applied to connections created on the fly upon redirections to nodes not + part of the initial pool. + +- List of deprecated methods: + + - `Predis\Client::multiExec()`: superseded by `Predis\Client::transaction()` + and to be removed in the next major release. + - `Predis\Client::pubSub()`: superseded by `Predis\Client::pubSubLoop()` and + to be removed in the next major release. This change was needed due to the + recently introduced `PUBSUB` command in Redis 2.8. + + +v0.8.4 (2013-07-27) +================================================================================ + +- Added `DUMP` and `RESTORE` to the server profile for Redis 2.6. + +- Connection exceptions now report basic host details in their messages. + +- Allow `Predis\Connection\PhpiredisConnection` to use a random IP when a host + actually has several IPs (ISSUE #116). + +- __FIX__: allow `HMSET` when using a cluster of Redis nodes with client-side + sharding or redis-cluster (ISSUE #106). + +- __FIX__: set `WITHSCORES` modifer for `ZRANGE`, `ZREVRANGE`, `ZRANGEBYSCORE` + and `ZREVRANGEBYSCORE` only when the options array passed to these commands + has `WITHSCORES` set to `true` (ISSUE #107). + +- __FIX__: scripted commands falling back from `EVALSHA` to `EVAL` resulted in + PHP errors when using a prefixed client (ISSUE #109). + +- __FIX__: `Predis\PubSub\DispatcherLoop` now works properly when using key + prefixing (ISSUE #114). + + +v0.8.3 (2013-02-18) +================================================================================ + +- Added `CLIENT SETNAME` and `CLIENT GETNAME` (ISSUE #102). + +- Implemented the `Predis\Connection\PhpiredisStreamConnection` class using the + `phpiredis` extension like `Predis\Connection\PhpiredisStreamConnection`, but + without requiring the `socket` extension since it relies on PHP's streams. + +- Added support for the TCP_NODELAY flag via the `tcp_nodelay` parameter for + stream-based connections, namely `Predis\Connection\StreamConnection` and + `Predis\Connection\PhpiredisStreamConnection` (requires PHP >= 5.4.0). + +- Updated the aggregated connection class for redis-cluster to work with 16384 + hash slots instead of 4096 to reflect the recent change from redis unstable + ([see this commit](https://github.com/antirez/redis/commit/ebd666d)). + +- The constructor of `Predis\Client` now accepts a callable as first argument + returning `Predis\Connection\ConnectionInterface`. Users can create their + own self-contained strategies to create and set up the underlying connection. + +- Users should return `0` from `Predis\Command\ScriptedCommand::getKeysCount()` + instead of `FALSE` to indicate that all of the arguments of a Lua script must + be used to populate `ARGV[]`. This does not represent a breaking change. + +- The `Predis\Helpers` class has been deprecated and it will be removed in + future releases. + + +v0.8.2 (2013-02-03) +================================================================================ + +- Added `Predis\Session\SessionHandler` to make it easy to store PHP sessions + on Redis using Predis. Please note that this class needs either PHP >= 5.4.0 + or a polyfill for PHP's `SessionHandlerInterface`. + +- Added the ability to get the default value of a client option directly from + `Predis\Option\ClientOption` using the `getDefault()` method by passing the + option name or its instance. + +- __FIX__: the standard pipeline executor was not using the response parser + methods associated to commands to process raw responses (ISSUE #101). + + +v0.8.1 (2013-01-19) +================================================================================ + +- The `connections` client option can now accept a callable object returning + an instance of `Predis\Connection\ConnectionFactoryInterface`. + +- Client options accepting callable objects as factories now pass their actual + instance to the callable as the second argument. + +- `Predis\Command\Processor\KeyPrefixProcessor` can now be directly casted to + string to obtain the current prefix, useful with string interpolation. + +- Added an optional callable argument to `Predis\Cluster\Distribution\HashRing` + and `Predis\Cluster\Distribution\KetamaPureRing` constructor that can be used + to customize how the distributor should extract the connection hash when + initializing the nodes distribution (ISSUE #36). + +- Correctly handle `TTL` and `PTTL` returning -2 on non existing keys starting + with Redis 2.8. + +- __FIX__: a missing use directive in `Predis\Transaction\MultiExecContext` + caused PHP errors when Redis did not return `+QUEUED` replies to commands + when inside a MULTI / EXEC context. + +- __FIX__: the `parseResponse()` method implemented for a scripted command was + ignored when retrying to execute a Lua script by falling back to `EVAL` after + a `-NOSCRIPT` error (ISSUE #94). + +- __FIX__: when subclassing `Predis\Client` the `getClientFor()` method returns + a new instance of the subclass instead of a new instance of `Predis\Client`. + + +v0.8.0 (2012-10-23) +================================================================================ + +- The default server profile for Redis is now `2.6`. + +- Certain connection parameters have been renamed: + + - `connection_async` is now `async_connect` + - `connection_timeout` is now `timeout` + - `connection_persistent` is now `persistent` + +- The `throw_errors` connection parameter has been removed and replaced by the + new `exceptions` client option since exceptions on `-ERR` replies returned by + Redis are not generated by connection classes anymore but instead are thrown + by the client class and other abstractions such as pipeline contexts. + +- Added smart support for redis-cluster (Redis v3.0) in addition to the usual + cluster implementation that uses client-side sharding. + +- Various namespaces and classes have been renamed to follow rules inspired by + the Symfony2 naming conventions. + +- The second argument of the constructor of `Predis\Client` does not accept + strings or instances of `Predis\Profile\ServerProfileInterface` anymore. + To specify a server profile you must explicitly set `profile` in the array + of client options. + +- `Predis\Command\ScriptedCommand` internally relies on `EVALSHA` instead of + `EVAL` thus avoiding to send Lua scripts bodies on each request. The client + automatically resends the command falling back to `EVAL` when Redis returns a + `-NOSCRIPT` error. Automatic fallback to `EVAL` does not work with pipelines, + inside a `MULTI / EXEC` context or with plain `EVALSHA` commands. + +- Complex responses are no more parsed by connection classes as they must be + processed by consumer classes using the handler associated to the issued + command. This means that executing commands directly on connections only + returns simple Redis types, but nothing changes when using `Predis\Client` + or the provided abstractions for pipelines and transactions. + +- Iterators for multi-bulk replies now skip the response parsing method of the + command that generated the response and are passed directly to user code. + Pipeline and transaction objects still consume automatically iterators. + +- Cluster and replication connections now extend a new common interface, + `Predis\Connection\AggregatedConnectionInterface`. + +- `Predis\Connection\MasterSlaveReplication` now uses an external strategy + class to handle the logic for checking readable / writable commands and Lua + scripts. + +- Command pipelines have been optimized for both speed and code cleanness, but + at the cost of bringing a breaking change in the signature of the interface + for pipeline executors. + +- Added a new pipeline executor that sends commands wrapped in a MULTI / EXEC + context to make the execution atomic: if a pipeline fails at a certain point + then the whole pipeline is discarded. + +- The key-hashing mechanism for commands is now handled externally and is no + more a competence of each command class. This change is neeeded to support + both client-side sharding and Redis cluster. + +- `Predis\Options\Option` is now abstract, see `Predis\Option\AbstractOption`. + + +v0.7.3 (2012-06-01) +================================================================================ + +- New commands available in the Redis v2.6 profile (dev): `BITOP`, `BITCOUNT`. + +- When the number of keys `Predis\Commands\ScriptedCommand` is negative, Predis + will count from the end of the arguments list to calculate the actual number + of keys that will be interpreted as elements for `KEYS` by the underlying + `EVAL` command. + +- __FIX__: `examples\CustomDistributionStrategy.php` had a mistyped constructor + call and produced a bad distribution due to an error as pointed in ISSUE #63. + This bug is limited to the above mentioned example and does not affect the + classes implemented in the `Predis\Distribution` namespace. + +- __FIX__: `Predis\Commands\ServerEvalSHA::getScriptHash()` was calculating the + hash while it just needs to return the first argument of the command. + +- __FIX__: `Predis\Autoloader` has been modified to allow cascading autoloaders + for the `Predis` namespace. + + +v0.7.2 (2012-04-01) +================================================================================ + +- Added `2.6` in the server profiles aliases list for the upcoming Redis 2.6. + `2.4` is still the default server profile. `dev` now targets Redis 2.8. + +- Connection instances can be serialized and unserialized using `serialize()` + and `unserialize()`. This is handy in certain scenarios such as client-side + clustering or replication to lower the overhead of initializing a connection + object with many sub-connections since unserializing them can be up to 5x + times faster. + +- Reworked the default autoloader to make it faster. It is also possible to + prepend it in PHP's autoload stack. + +- __FIX__: fixed parsing of the payload returned by `MONITOR` with Redis 2.6. + + +v0.7.1 (2011-12-27) +================================================================================ + +- The PEAR channel on PearHub has been deprecated in favour of `pear.nrk.io`. + +- Miscellaneous minor fixes. + +- Added transparent support for master / slave replication configurations where + write operations are performed on the master server and read operations are + routed to one of the slaves. Please refer to ISSUE #21 for a bit of history + and more details about replication support in Predis. + +- The `profile` client option now accepts a callable object used to initialize + a new instance of `Predis\Profiles\IServerProfile`. + +- Exposed a method for MULTI / EXEC contexts that adds the ability to execute + instances of Redis commands against transaction objects. + + +v0.7.0 (2011-12-11) +================================================================================ + +- Predis now adheres to the PSR-0 standard which means that there is no more a + single file holding all the classes of the library, but multiple files (one + for each class). You can use any PSR-0 compatible autoloader to load Predis + or just leverage the default one shipped with the library by requiring the + `Predis/Autoloader.php` and call `Predis\Autoloader::register()`. + +- The default server profile for Redis is now 2.4. The `dev` profile supports + all the features of Redis 2.6 (currently unstable) such as Lua scripting. + +- Support for long aliases (method names) for Redis commands has been dropped. + +- Redis 1.0 is no more supported. From now on Predis will use only the unified + protocol to serialize commands. + +- It is possible to prefix keys transparently on a client-level basis with the + new `prefix` client option. + +- An external connection factory is used to initialize new connection instances + and developers can now register their own connection classes using the new + `connections` client option. + +- It is possible to connect locally to Redis using UNIX domain sockets. Just + use `unix:///path/to/redis.sock` or a named array just like in the following + example: `array('scheme' => 'unix', 'path' => '/path/to/redis.sock');`. + +- If the `phpiredis` extension is loaded by PHP, it is now possible to use an + alternative connection class that leverages it to make Predis faster on many + cases, especially when dealing with big multibulk replies, with the the only + downside that persistent connections are not supported. Please refer to the + documentation to see how to activate this class using the new `connections` + client option. + +- Predis is capable to talk with Webdis, albeit with some limitations such as + the lack of pipelining and transactions, just by using the `http` scheme in + in the connection parameters. All is needed is PHP with the `curl` and the + `phpiredis` extensions loaded. + +- Way too many changes in the public API to make a list here, we just tried to + make all the Redis commands compatible with previous releases of v0.6 so that + you do not have to worry if you are simply using Predis as a client. Probably + the only breaking changes that should be mentioned here are: + + - `throw_on_error` has been renamed to `throw_errors` and it is a connection + parameter instead of a client option, along with `iterable_multibulk`. + + - `key_distribution` has been removed from the client options. To customize + the distribution strategy you must provide a callable object to the new + `cluster` client option to configure and then return a new instance of + `Predis\Network\IConnectionCluster`. + + - `Predis\Client::create()` has been removed. Just use the constructor to set + up a new instance of `Predis\Client`. + + - `Predis\Client::pipelineSafe()` was deprecated in Predis v0.6.1 and now has + finally removed. Use `Predis\Client::pipeline(array('safe' => true))`. + + - `Predis\Client::rawCommand()` has been removed due to inconsistencies with + the underlying connection abstractions. You can still get the raw resource + out of a connection with `Predis\Network\IConnectionSingle::getResource()` + so that you can talk directly with Redis. + +- The `Predis\MultiBulkCommand` class has been merged into `Predis\Command` and + thus removed. Serialization of commands is now a competence of connections. + +- The `Predis\IConnection` interface has been splitted into two new interfaces: + `Predis\Network\IConnectionSingle` and `Predis\Network\IConnectionCluster`. + +- The constructor of `Predis\Client` now accepts more type of arguments such as + instances of `Predis\IConnectionParameters` and `Predis\Network\IConnection`. + + +v0.6.6 (2011-04-01) +================================================================================ + +- Switched to Redis 2.2 as the default server profile (there are no changes + that would break compatibility with previous releases). Long command names + are no more supported by default but if you need them you can still require + `Predis_Compatibility.php` to avoid breaking compatibility. + +- Added a `VERSION` constant to `Predis\Client`. + +- Some performance improvements for multibulk replies (parsing them is about + 16% faster than the previous version). A few core classes have been heavily + optimized to reduce overhead when creating new instances. + +- Predis now uses by default a new protocol reader, more lightweight and + faster than the default handler-based one. Users can revert to the old + protocol reader with the `reader` client option set to `composable`. + This client option can also accept custom reader classes implementing the + new `Predis\IResponseReader` interface. + +- Added support for connecting to Redis using UNIX domain sockets (ISSUE #25). + +- The `read_write_timeout` connection parameter can now be set to 0 or false + to disable read and write timeouts on connections. The old behaviour of -1 + is still intact. + +- `ZUNIONSTORE` and `ZINTERSTORE` can accept an array to specify a list of the + source keys to be used to populate the destination key. + +- `MGET`, `SINTER`, `SUNION` and `SDIFF` can accept an array to specify a list + of keys. `SINTERSTORE`, `SUNIONSTORE` and `SDIFFSTORE` can also accept an + array to specify the list of source keys. + +- `SUBSCRIBE` and `PSUBSCRIBE` can accept a list of channels for subscription. + +- __FIX__: some client-side clean-ups for `MULTI/EXEC` were handled incorrectly + in a couple of corner cases (ISSUE #27). + + +v0.6.5 (2011-02-12) +================================================================================ + +- __FIX__: due to an untested internal change introduced in v0.6.4, a wrong + handling of bulk reads of zero-length values was producing protocol + desynchronization errors (ISSUE #20). + + +v0.6.4 (2011-02-12) +================================================================================ + +- Various performance improvements (15% ~ 25%) especially when dealing with + long multibulk replies or when using clustered connections. + +- Added the `on_retry` option to `Predis\MultiExecBlock` that can be used to + specify an external callback (or any callable object) that gets invoked + whenever a transaction is aborted by the server. + +- Added inline (p)subscribtion via options when initializing an instance of + `Predis\PubSubContext`. + + +v0.6.3 (2011-01-01) +================================================================================ + +- New commands available in the Redis v2.2 profile (dev): + - Strings: `SETRANGE`, `GETRANGE`, `SETBIT`, `GETBIT` + - Lists : `BRPOPLPUSH` + +- The abstraction for `MULTI/EXEC` transactions has been dramatically improved + by providing support for check-and-set (CAS) operations when using Redis >= + 2.2. Aborted transactions can also be optionally replayed in automatic up + to a user-defined number of times, after which a `Predis\AbortedMultiExec` + exception is thrown. + + +v0.6.2 (2010-11-28) +================================================================================ + +- Minor internal improvements and clean ups. + +- New commands available in the Redis v2.2 profile (dev): + - Strings: `STRLEN` + - Lists : `LINSERT`, `RPUSHX`, `LPUSHX` + - ZSets : `ZREVRANGEBYSCORE` + - Misc. : `PERSIST` + +- WATCH also accepts a single array parameter with the keys that should be + monitored during a transaction. + +- Improved the behaviour of `Predis\MultiExecBlock` in certain corner cases. + +- Improved parameters checking for the SORT command. + +- __FIX__: the `STORE` parameter for the `SORT` command didn't work correctly + when using `0` as the target key (ISSUE #13). + +- __FIX__: the methods for `UNWATCH` and `DISCARD` do not break anymore method + chaining with `Predis\MultiExecBlock`. + + +v0.6.1 (2010-07-11) +================================================================================ + +- Minor internal improvements and clean ups. + +- New commands available in the Redis v2.2 profile (dev): + - Misc. : `WATCH`, `UNWATCH` + +- Optional modifiers for `ZRANGE`, `ZREVRANGE` and `ZRANGEBYSCORE` queries are + supported using an associative array passed as the last argument of their + respective methods. + +- The `LIMIT` modifier for `ZRANGEBYSCORE` can be specified using either: + - an indexed array: `array($offset, $count)` + - an associative array: `array('offset' => $offset, 'count' => $count)` + +- The method `Predis\Client::__construct()` now accepts also instances of + `Predis\ConnectionParameters`. + +- `Predis\MultiExecBlock` and `Predis\PubSubContext` now throw an exception + when trying to create their instances using a profile that does not + support the required Redis commands or when the client is connected to + a cluster of connections. + +- Various improvements to `Predis\MultiExecBlock`: + - fixes and more consistent behaviour across various usage cases. + - support for `WATCH` and `UNWATCH` when using the current development + profile (Redis v2.2) and aborted transactions. + +- New signature for `Predis\Client::multiExec()` which is now able to accept + an array of options for the underlying instance of `Predis\MultiExecBlock`. + Backwards compatibility with previous releases of Predis is ensured. + +- New signature for `Predis\Client::pipeline()` which is now able to accept + an array of options for the underlying instance of Predis\CommandPipeline. + Backwards compatibility with previous releases of Predis is ensured. + The method `Predis\Client::pipelineSafe()` is to be considered deprecated. + +- __FIX__: The `WEIGHT` modifier for `ZUNIONSTORE` and `ZINTERSTORE` was + handled incorrectly with more than two weights specified. + + +v0.6.0 (2010-05-24) +================================================================================ + +- Switched to the new multi-bulk request protocol for all of the commands + in the Redis 1.2 and Redis 2.0 profiles. Inline and bulk requests are now + deprecated as they will be removed in future releases of Redis. + +- The default server profile is `2.0` (targeting Redis 2.0.x). If you are + using older versions of Redis, it is highly recommended that you specify + which server profile the client should use (e.g. `1.2` when connecting + to instances of Redis 1.2.x). + +- Support for Redis 1.0 is now optional and it is provided by requiring + 'Predis_Compatibility.php' before creating an instance of `Predis\Client`. + +- New commands added to the Redis 2.0 profile since Predis 0.5.1: + - Strings: `SETEX`, `APPEND`, `SUBSTR` + - ZSets : `ZCOUNT`, `ZRANK`, `ZUNIONSTORE`, `ZINTERSTORE`, `ZREMBYRANK`, + `ZREVRANK` + - Hashes : `HSET`, `HSETNX`, `HMSET`, `HINCRBY`, `HGET`, `HMGET`, `HDEL`, + `HEXISTS`, `HLEN`, `HKEYS`, `HVALS`, `HGETALL` + - PubSub : `PUBLISH`, `SUBSCRIBE`, `UNSUBSCRIBE` + - Misc. : `DISCARD`, `CONFIG` + +- Introduced client-level options with the new `Predis\ClientOptions` class. + Options can be passed to the constructor of `Predis\Client` in its second + argument as an array or an instance of `Predis\ClientOptions`. For brevity's + sake and compatibility with older versions, the constructor still accepts + an instance of `Predis\RedisServerProfile` in its second argument. The + currently supported client options are: + + - `profile` [default: `2.0` as of Predis 0.6.0]: specifies which server + profile to use when connecting to Redis. This option accepts an instance + of `Predis\RedisServerProfile` or a string that indicates the version. + + - `key_distribution` [default: `Predis\Distribution\HashRing`]: specifies + which key distribution strategy to use to distribute keys among the + servers that compose a cluster. This option accepts an instance of + `Predis\Distribution\IDistributionStrategy` so that users can implement + their own key distribution strategy. `Predis\Distribution\KetamaPureRing` + is an alternative distribution strategy providing a pure-PHP implementation + of the same algorithm used by libketama. + + - `throw_on_error` [default: `TRUE`]: server errors can optionally be handled + "silently": instead of throwing an exception, the client returns an error + response type. + + - `iterable_multibulk` [EXPERIMENTAL - default: `FALSE`]: in addition to the + classic way of fetching a whole multibulk reply into an array, the client + can now optionally stream a multibulk reply down to the user code by using + PHP iterators. It is just a little bit slower, but it can save a lot of + memory in certain scenarios. + +- New parameters for connections: + + - `alias` [default: not set]: every connection can now be identified by an + alias that is useful to get a specific connections when connected to a + cluster of Redis servers. + - `weight` [default: not set]: allows to balance keys asymmetrically across + multiple servers. This is useful when you have servers with different + amounts of memory to distribute the load of your keys accordingly. + - `connection_async` [default: `FALSE`]: estabilish connections to servers + in a non-blocking way, so that the client is not blocked while the socket + resource performs the actual connection. + - `connection_persistent` [default: `FALSE`]: the underlying socket resource + is left open when a script ends its lifecycle. Persistent connections can + lead to unpredictable or strange behaviours, so they should be used with + extreme care. + +- Introduced the `Predis\Pipeline\IPipelineExecutor` interface. Classes that + implements this interface are used internally by the `Predis\CommandPipeline` + class to change the behaviour of the pipeline when writing/reading commands + from one or multiple servers. Here is the list of the default executors: + + - `Predis\Pipeline\StandardExecutor`: exceptions generated by server errors + might be thrown depending on the options passed to the client (see the + `throw_on_error` client option). Instead, protocol or network errors always + throw exceptions. This is the default executor for single and clustered + connections and shares the same behaviour of Predis 0.5.x. + - `Predis\Pipeline\SafeExecutor`: exceptions generated by server, protocol + or network errors are not thrown but returned in the response array as + instances of `Predis\ResponseError` or `Predis\CommunicationException`. + - `Predis\Pipeline\SafeClusterExecutor`: this executor shares the same + behaviour of `Predis\Pipeline\SafeExecutor` but it is geared towards + clustered connections. + +- Support for PUB/SUB is handled by the new `Predis\PubSubContext` class, which + could also be used to build a callback dispatcher for PUB/SUB scenarios. + +- When connected to a cluster of connections, it is now possible to get a + new `Predis\Client` instance for a single connection of the cluster by + passing its alias/index to the new `Predis\Client::getClientFor()` method. + +- `Predis\CommandPipeline` and `Predis\MultiExecBlock` return their instances + when invokink commands, thus allowing method chaining in pipelines and + multi-exec blocks. + +- `Predis\MultiExecBlock` can handle the new `DISCARD` command. + +- Connections now support float values for the `connection_timeout` parameter + to express timeouts with a microsecond resolution. + +- __FIX__: TCP connections now respect the read/write timeout parameter when + reading the payload of server responses. Previously, `stream_get_contents()` + was being used internally to read data from a connection but it looks like + PHP does not honour the specified timeout for socket streams when inside + this function. + +- __FIX__: The `GET` parameter for the `SORT` command now accepts also multiple + key patterns by passing an array of strings. (ISSUE #1). + +* __FIX__: Replies to the `DEL` command return the number of elements deleted + by the server and not 0 or 1 interpreted as a boolean response. (ISSUE #4). + + +v0.5.1 (2010-01-23) +================================================================================ + +* `RPOPLPUSH` has been changed from bulk command to inline command in Redis + 1.2.1, so `ListPopLastPushHead` now extends `InlineCommand`. The old behavior + is still available via the `ListPopLastPushHeadBulk` class so that you can + override the server profile if you need the old (and uncorrect) behaviour + when connecting to a Redis 1.2.0 instance. + +* Added missing support for `BGREWRITEAOF` for Redis >= 1.2.0. + +* Implemented a factory method for the `RedisServerProfile` class to ease the + creation of new server profile instances based on a version string. + + +v0.5.0 (2010-01-09) +================================================================================ +* First versioned release of Predis diff --git a/vendor/predis/predis/CONTRIBUTING.md b/vendor/predis/predis/CONTRIBUTING.md new file mode 100644 index 000000000..3cf3c21be --- /dev/null +++ b/vendor/predis/predis/CONTRIBUTING.md @@ -0,0 +1,44 @@ +## Filing bug reports ## + +Bugs or feature requests can be posted on the [GitHub issues](http://github.com/predis/predis/issues) +section of the project. + +When reporting bugs, in addition to the obvious description of your issue you __must__ always provide +some essential information about your environment such as: + + 1. version of Predis (check the `VERSION` file or the `Predis\Client::VERSION` constant). + 2. version of Redis (check `redis_version` returned by [`INFO`](http://redis.io/commands/info)). + 3. version of PHP. + 4. name and version of the operating system. + 5. when possible, a small snippet of code that reproduces the issue. + +__Think about it__: we do not have a crystal ball and cannot predict things or peer into the unknown +so please provide as much details as possible to help us isolating issues and fix them. + +__Never__ use GitHub issues to post generic questions about Predis! When you have questions about +how Predis works or how it can be used, please just hop me an email and I will get back to you as +soon as possible. + + +## Contributing code ## + +If you want to work on Predis, it is highly recommended that you first run the test suite in order +to check that everything is OK and report strange behaviours or bugs. When modifying Predis please +make sure that no warnings or notices are emitted by PHP running the interpreter in your development +environment with the `error_reporting` variable set to `E_ALL | E_STRICT`. + +The recommended way to contribute to Predis is to fork the project on GitHub, create topic branches +on your newly created repository to fix bugs or add new features (possibly with tests covering your +modifications) and then open a pull request with a description of the applied changes. Obviously you +can use any other Git hosting provider of your preference. + +We always aim for consistency in our code base so you should follow basic coding rules as defined by +[PSR-1](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md) +and [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) +and stick with the conventions used in Predis to name classes and interfaces. Indentation should be +done with 4 spaces and code should be wrapped at 100 columns (please try to stay within this limit +even if the above mentioned official coding guidelines set the soft limit to 120 columns). + +Please follow these [commit guidelines](http://git-scm.com/book/ch5-2.html#Commit-Guidelines) when +committing your code to Git and always write a meaningful (not necessarily extended) description of +your changes before opening pull requests. diff --git a/vendor/predis/predis/FAQ.md b/vendor/predis/predis/FAQ.md new file mode 100644 index 000000000..152f534a5 --- /dev/null +++ b/vendor/predis/predis/FAQ.md @@ -0,0 +1,177 @@ +# Some frequently asked questions about Predis # +________________________________________________ + +### What is the point of Predis? ### + +The main point of Predis is about offering a highly customizable and extensible client for Redis, +that can be easily extended by developers while still being reasonabily fast. With Predis you can +swap almost any class with your own custom implementation: you can have custom connection classes, +new distribution strategies for client-side sharding, or handlers to replace or add Redis commands. +All of this can be achieved without messing with the source code of the library and directly in your +own application. Given the fast pace at which Redis is developed and adds new features, this can be +a great asset since it allows developers to add new and still missing features or commands or change +the standard behaviour of the library without the need to break dependencies in production code (at +least to some degree). + +### Does Predis support UNIX domain sockets and persistent connections? ### + +Yes. Obviously persistent connections actually work only when using PHP configured as a persistent +process reused by the web server (see [PHP-FPM](http://php-fpm.org)). + +### Does Predis support SSL-encrypted connections? ### + +Yes. Encrypted connections are mostly useful when connecting to Redis instances exposed by various +cloud hosting providers without the need to configure an SSL proxy, but you should also take into +account the general performances degradation especially during the connect() operation when the TLS +handshake must be performed to secure the connection. Persistent SSL-encrypted connections may help +in that respect, but they are supported only when running on PHP >= 7.0.0. + +### Does Predis support transparent (de)serialization of values? ### + +No and it will not ever do that by default. The reason behind this decision is that serialization is +usually something that developers prefer to customize depending on their needs and can not be easily +generalized when using Redis because of the many possible access patterns for your data. This does +not mean that it is impossible to have such a feature since you can leverage the extensibility of +this library to define your own serialization-aware commands. You can find more details about how to +do that [on this issue](http://github.com/predis/predis/issues/29#issuecomment-1202624). + +### How can I force Predis to connect to Redis before sending any command? ### + +Explicitly connecting to Redis is usually not needed since the client initializes connections lazily +only when they are needed. Admittedly, this behavior can be inconvenient in certain scenarios when +you absolutely need to perform an upfront check to determine if the server is up and running and +eventually catch exceptions on failures. Forcing the client to open the underlying connection can be +done by invoking `Predis\Client::connect()`: + +```php +$client = new Predis\Client(); + +try { + $client->connect(); +} catch (Predis\Connection\ConnectionException $exception) { + // We could not connect to Redis! Your handling code goes here. +} + +$client->info(); +``` + +### How Predis abstracts Redis commands? ### + +The approach used to implement Redis commands is quite simple: by default each command follows the +same signature as defined on the [Redis documentation](http://redis.io/commands) which makes things +pretty easy if you already know how Redis works or you need to look up how to use certain commands. +Alternatively, variadic commands can accept an array for keys or values (depending on the command) +instead of a list of arguments. Commands such as [`RPUSH`](http://redis.io/commands/rpush) and +[`HMSET`](http://redis.io/commands/hmset) are great examples: + +```php +$client->rpush('my:list', 'value1', 'value2', 'value3'); // plain method arguments +$client->rpush('my:list', ['value1', 'value2', 'value3']); // single argument array + +$client->hmset('my:hash', 'field1', 'value1', 'field2', 'value2'); // plain method arguments +$client->hmset('my:hash', ['field1'=>'value1', 'field2'=>'value2']); // single named array +``` + +An exception to this rule is [`SORT`](http://redis.io/commands/sort) for which modifiers are passed +[using a named array](tests/Predis/Command/KeySortTest.php#L54-L75). + + +# Speaking about performances... # +_________________________________________________ + + +### Predis is a pure-PHP implementation: it can not be fast enough! ### + +It really depends, but most of the times the answer is: _yes, it is fast enough_. I will give you a +couple of easy numbers with a simple test that uses a single client and is executed by PHP 5.5.6 +against a local instance of Redis 2.8 that runs under Ubuntu 13.10 on a Intel Q6600: + +``` +21000 SET/sec using 12 bytes for both key and value. +21000 GET/sec while retrieving the very same values. +0.130 seconds to fetch 30000 keys using _KEYS *_. +``` + +How does it compare with [__phpredis__](http://github.com/nicolasff/phpredis), a nice C extension +providing an efficient client for Redis? + +``` +30100 SET/sec using 12 bytes for both key and value +29400 GET/sec while retrieving the very same values +0.035 seconds to fetch 30000 keys using "KEYS *"". +``` + +Wow __phpredis__ seems much faster! Well, we are comparing a C extension with a pure-PHP library so +lower numbers are quite expected but there is a fundamental flaw in them: is this really how you are +going to use Redis in your application? Are you really going to send thousands of commands using a +for-loop on each page request using a single client instance? If so... well I guess you are probably +doing something wrong. Also, if you need to `SET` or `GET` multiple keys you should definitely use +commands such as `MSET` and `MGET`. You can also use pipelining to get more performances when this +technique can be used. + +There is one more thing: we have tested the overhead of Predis by connecting on a localhost instance +of Redis but how these numbers change when we hit the physical network by connecting to remote Redis +instances? + +``` +Using Predis: +3200 SET/sec using 12 bytes for both key and value +3200 GET/sec while retrieving the very same values +0.132 seconds to fetch 30000 keys using "KEYS *". + +Using phpredis: +3500 SET/sec using 12 bytes for both key and value +3500 GET/sec while retrieving the very same values +0.045 seconds to fetch 30000 keys using "KEYS *". +``` + +There you go, you get almost the same average numbers and the reason is simple: network latency is a +real performance killer and you cannot do (almost) anything about that. As a disclaimer, remember +that we are measuring the overhead of client libraries implementations and the effects of network +round-trip times, so we are not really measuring how fast Redis is. Redis shines best with thousands +of concurrent clients doing requests! Also, actual performances should be measured according to how +your application will use Redis. + +### I am convinced, but performances for multi-bulk responses are still worse ### + +Fair enough, but there is an option available if you need even more speed and consists on installing +__[phpiredis](http://github.com/nrk/phpiredis)__ (note the additional _i_ in the name) and let the +client use it. __phpiredis__ is another C extension that wraps __hiredis__ (the official C client +library for Redis) with a thin layer exposing its features to PHP. You can then choose between two +different connection classes: + + - `Predis\Connection\PhpiredisStreamConnection` (using native PHP streams). + - `Predis\Connection\PhpiredisSocketConnection` (requires `ext-socket`). + +You will now get the benefits of a faster protocol serializer and parser just by adding a couple of +lines of code: + +```php +$client = new Predis\Client('tcp://127.0.0.1', array( + 'connections' => array( + 'tcp' => 'Predis\Connection\PhpiredisStreamConnection', + 'unix' => 'Predis\Connection\PhpiredisSocketConnection', + ), +)); +``` + +Dead simple. Nothing changes in the way you use the library in your application. So how fast is it +our basic benchmark script now? There are not much improvements for inline or short bulk responses +like the ones returned by `SET` and `GET`, but the speed for parsing multi-bulk responses is now on +par with phpredis: + +``` +Fatching 30000 keys with _KEYS *_ using Predis paired with phpiredis:: + +0.035 seconds from a local Redis instance +0.047 seconds from a remote Redis instance +``` + +### If I need an extension to get better performances, why not using phpredis? ### + +Good question. Generically speaking if you need absolute uber-speed using Redis on the localhost and +you do not care about abstractions built around some Redis features such as MULTI / EXEC, or if you +do not need any kind of extensibility or guaranteed backwards compatibility with different versions +of Redis (Predis currently supports from 1.2 up to 2.8 and the current development version), then +using __phpredis__ makes absolutely sense. Otherwise, Predis is perfect for the job and by adding +__phpiredis__ you can get a nice speed bump almost for free. diff --git a/vendor/predis/predis/LICENSE b/vendor/predis/predis/LICENSE new file mode 100644 index 000000000..c35b65747 --- /dev/null +++ b/vendor/predis/predis/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2009-2016 Daniele Alessandri + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/predis/predis/README.md b/vendor/predis/predis/README.md new file mode 100644 index 000000000..d16ea9fb5 --- /dev/null +++ b/vendor/predis/predis/README.md @@ -0,0 +1,489 @@ +# Predis # + +[![Software license][ico-license]](LICENSE) +[![Latest stable][ico-version-stable]][link-packagist] +[![Latest development][ico-version-dev]][link-packagist] +[![Monthly installs][ico-downloads-monthly]][link-downloads] +[![Build status][ico-travis]][link-travis] + +Flexible and feature-complete [Redis](http://redis.io) client for PHP >= 5.3 and HHVM >= 2.3.0. + +Predis does not require any additional C extension by default, but it can be optionally paired with +[phpiredis](https://github.com/nrk/phpiredis) to lower the overhead of the serialization and parsing +of the [Redis RESP Protocol](http://redis.io/topics/protocol). + +More details about this project can be found on the [frequently asked questions](FAQ.md). + + +## Main features ## + +- Support for different versions of Redis (from __2.0__ to __3.2__) using profiles. +- Support for clustering using client-side sharding and pluggable keyspace distributors. +- Support for [redis-cluster](http://redis.io/topics/cluster-tutorial) (Redis >= 3.0). +- Support for master-slave replication setups and [redis-sentinel](http://redis.io/topics/sentinel). +- Transparent key prefixing of keys using a customizable prefix strategy. +- Command pipelining on both single nodes and clusters (client-side sharding only). +- Abstraction for Redis transactions (Redis >= 2.0) and CAS operations (Redis >= 2.2). +- Abstraction for Lua scripting (Redis >= 2.6) and automatic switching between `EVALSHA` or `EVAL`. +- Abstraction for `SCAN`, `SSCAN`, `ZSCAN` and `HSCAN` (Redis >= 2.8) based on PHP iterators. +- Connections are established lazily by the client upon the first command and can be persisted. +- Connections can be established via TCP/IP (also TLS/SSL-encrypted) or UNIX domain sockets. +- Support for [Webdis](http://webd.is) (requires both `ext-curl` and `ext-phpiredis`). +- Support for custom connection classes for providing different network or protocol backends. +- Flexible system for defining custom commands and profiles and override the default ones. + + +## How to _install_ and use Predis ## + +This library can be found on [Packagist](http://packagist.org/packages/predis/predis) for an easier +management of projects dependencies using [Composer](http://packagist.org/about-composer) or on our +[own PEAR channel](http://pear.nrk.io) for a more traditional installation using PEAR. Ultimately, +compressed archives of each release are [available on GitHub](https://github.com/predis/predis/releases). + + +### Loading the library ### + +Predis relies on the autoloading features of PHP to load its files when needed and complies with the +[PSR-4 standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md). +Autoloading is handled automatically when dependencies are managed through Composer, but it is also +possible to leverage its own autoloader in projects or scripts lacking any autoload facility: + +```php +// Prepend a base path if Predis is not available in your "include_path". +require 'Predis/Autoloader.php'; + +Predis\Autoloader::register(); +``` + +It is also possible to create a [phar](http://www.php.net/manual/en/intro.phar.php) archive directly +from the repository by launching the `bin/create-phar` script. The generated phar already contains a +stub defining its own autoloader, so you just need to `require()` it to start using the library. + + +### Connecting to Redis ### + +When creating a client instance without passing any connection parameter, Predis assumes `127.0.0.1` +and `6379` as default host and port. The default timeout for the `connect()` operation is 5 seconds: + +```php +$client = new Predis\Client(); +$client->set('foo', 'bar'); +$value = $client->get('foo'); +``` + +Connection parameters can be supplied either in the form of URI strings or named arrays. The latter +is the preferred way to supply parameters, but URI strings can be useful when parameters are read +from non-structured or partially-structured sources: + +```php +// Parameters passed using a named array: +$client = new Predis\Client([ + 'scheme' => 'tcp', + 'host' => '10.0.0.1', + 'port' => 6379, +]); + +// Same set of parameters, passed using an URI string: +$client = new Predis\Client('tcp://10.0.0.1:6379'); +``` + +Password protected servers can be accessed by adding `password` to the parameters set. When ACLs are +enabled on Redis >= 6.0, both `username` and `password` are required for user authentication. + +It is also possible to connect to local instances of Redis using UNIX domain sockets, in this case +the parameters must use the `unix` scheme and specify a path for the socket file: + +```php +$client = new Predis\Client(['scheme' => 'unix', 'path' => '/path/to/redis.sock']); +$client = new Predis\Client('unix:/path/to/redis.sock'); +``` + +The client can leverage TLS/SSL encryption to connect to secured remote Redis instances without the +need to configure an SSL proxy like stunnel. This can be useful when connecting to nodes running on +various cloud hosting providers. Encryption can be enabled with using the `tls` scheme and an array +of suitable [options](http://php.net/manual/context.ssl.php) passed via the `ssl` parameter: + +```php +// Named array of connection parameters: +$client = new Predis\Client([ + 'scheme' => 'tls', + 'ssl' => ['cafile' => 'private.pem', 'verify_peer' => true], +]); + +// Same set of parameters, but using an URI string: +$client = new Predis\Client('tls://127.0.0.1?ssl[cafile]=private.pem&ssl[verify_peer]=1'); +``` + +The connection schemes [`redis`](http://www.iana.org/assignments/uri-schemes/prov/redis) (alias of +`tcp`) and [`rediss`](http://www.iana.org/assignments/uri-schemes/prov/rediss) (alias of `tls`) are +also supported, with the difference that URI strings containing these schemes are parsed following +the rules described on their respective IANA provisional registration documents. + +The actual list of supported connection parameters can vary depending on each connection backend so +it is recommended to refer to their specific documentation or implementation for details. + +When an array of connection parameters is provided, Predis automatically works in cluster mode using +client-side sharding. Both named arrays and URI strings can be mixed when providing configurations +for each node: + +```php +$client = new Predis\Client([ + 'tcp://10.0.0.1?alias=first-node', + ['host' => '10.0.0.2', 'alias' => 'second-node'], +]); +``` + +See the [aggregate connections](#aggregate-connections) section of this document for more details. + +Connections to Redis are lazy meaning that the client connects to a server only if and when needed. +While it is recommended to let the client do its own stuff under the hood, there may be times when +it is still desired to have control of when the connection is opened or closed: this can easily be +achieved by invoking `$client->connect()` and `$client->disconnect()`. Please note that the effect +of these methods on aggregate connections may differ depending on each specific implementation. + + +### Client configuration ### + +Many aspects and behaviors of the client can be configured by passing specific client options to the +second argument of `Predis\Client::__construct()`: + +```php +$client = new Predis\Client($parameters, ['profile' => '2.8', 'prefix' => 'sample:']); +``` + +Options are managed using a mini DI-alike container and their values can be lazily initialized only +when needed. The client options supported by default in Predis are: + + - `profile`: specifies the profile to use to match a specific version of Redis. + - `prefix`: prefix string automatically applied to keys found in commands. + - `exceptions`: whether the client should throw or return responses upon Redis errors. + - `connections`: list of connection backends or a connection factory instance. + - `cluster`: specifies a cluster backend (`predis`, `redis` or callable object). + - `replication`: specifies a replication backend (`TRUE`, `sentinel` or callable object). + - `aggregate`: overrides `cluster` and `replication` to provide a custom connections aggregator. + - `parameters`: list of default connection parameters for aggregate connections. + +Users can also provide custom options with values or callable objects (for lazy initialization) that +are stored in the options container for later use through the library. + + +### Aggregate connections ### + +Aggregate connections are the foundation upon which Predis implements clustering and replication and +they are used to group multiple connections to single Redis nodes and hide the specific logic needed +to handle them properly depending on the context. Aggregate connections usually require an array of +connection parameters when creating a new client instance. + +#### Cluster #### + +By default, when no specific client options are set and an array of connection parameters is passed +to the client's constructor, Predis configures itself to work in clustering mode using a traditional +client-side sharding approach to create a cluster of independent nodes and distribute the keyspace +among them. This approach needs some form of external health monitoring of nodes and requires manual +operations to rebalance the keyspace when changing its configuration by adding or removing nodes: + +```php +$parameters = ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3']; + +$client = new Predis\Client($parameters); +``` + +Along with Redis 3.0, a new supervised and coordinated type of clustering was introduced in the form +of [redis-cluster](http://redis.io/topics/cluster-tutorial). This kind of approach uses a different +algorithm to distribute the keyspaces, with Redis nodes coordinating themselves by communicating via +a gossip protocol to handle health status, rebalancing, nodes discovery and request redirection. In +order to connect to a cluster managed by redis-cluster, the client requires a list of its nodes (not +necessarily complete since it will automatically discover new nodes if necessary) and the `cluster` +client options set to `redis`: + +```php +$parameters = ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3']; +$options = ['cluster' => 'redis']; + +$client = new Predis\Client($parameters, $options); +``` + +#### Replication #### + +The client can be configured to operate in a single master / multiple slaves setup to provide better +service availability. When using replication, Predis recognizes read-only commands and sends them to +a random slave in order to provide some sort of load-balancing and switches to the master as soon as +it detects a command that performs any kind of operation that would end up modifying the keyspace or +the value of a key. Instead of raising a connection error when a slave fails, the client attempts to +fall back to a different slave among the ones provided in the configuration. + +The basic configuration needed to use the client in replication mode requires one Redis server to be +identified as the master (this can be done via connection parameters using the `alias` parameter set +to `master`) and one or more servers acting as slaves: + +```php +$parameters = ['tcp://10.0.0.1?alias=master', 'tcp://10.0.0.2', 'tcp://10.0.0.3']; +$options = ['replication' => true]; + +$client = new Predis\Client($parameters, $options); +``` + +The above configuration has a static list of servers and relies entirely on the client's logic, but +it is possible to rely on [`redis-sentinel`](http://redis.io/topics/sentinel) for a more robust HA +environment with sentinel servers acting as a source of authority for clients for service discovery. +The minimum configuration required by the client to work with redis-sentinel is a list of connection +parameters pointing to a bunch of sentinel instances, the `replication` option set to `sentinel` and +the `service` option set to the name of the service: + +```php +$sentinels = ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3']; +$options = ['replication' => 'sentinel', 'service' => 'mymaster']; + +$client = new Predis\Client($sentinels, $options); +``` + +If the master and slave nodes are configured to require an authentication from clients, a password +must be provided via the global `parameters` client option. This option can also be used to specify +a different database index. The client options array would then look like this: + +```php +$options = [ + 'replication' => 'sentinel', + 'service' => 'mymaster', + 'parameters' => [ + 'password' => $secretpassword, + 'database' => 10, + ], +]; +``` + +While Predis is able to distinguish commands performing write and read-only operations, `EVAL` and +`EVALSHA` represent a corner case in which the client switches to the master node because it cannot +tell when a Lua script is safe to be executed on slaves. While this is indeed the default behavior, +when certain Lua scripts do not perform write operations it is possible to provide an hint to tell +the client to stick with slaves for their execution: + +```php +$parameters = ['tcp://10.0.0.1?alias=master', 'tcp://10.0.0.2', 'tcp://10.0.0.3']; +$options = ['replication' => function () { + // Set scripts that won't trigger a switch from a slave to the master node. + $strategy = new Predis\Replication\ReplicationStrategy(); + $strategy->setScriptReadOnly($LUA_SCRIPT); + + return new Predis\Connection\Aggregate\MasterSlaveReplication($strategy); +}]; + +$client = new Predis\Client($parameters, $options); +$client->eval($LUA_SCRIPT, 0); // Sticks to slave using `eval`... +$client->evalsha(sha1($LUA_SCRIPT), 0); // ... and `evalsha`, too. +``` + +The [`examples`](examples/) directory contains a few scripts that demonstrate how the client can be +configured and used to leverage replication in both basic and complex scenarios. + + +### Command pipelines ### + +Pipelining can help with performances when many commands need to be sent to a server by reducing the +latency introduced by network round-trip timings. Pipelining also works with aggregate connections. +The client can execute the pipeline inside a callable block or return a pipeline instance with the +ability to chain commands thanks to its fluent interface: + +```php +// Executes a pipeline inside the given callable block: +$responses = $client->pipeline(function ($pipe) { + for ($i = 0; $i < 1000; $i++) { + $pipe->set("key:$i", str_pad($i, 4, '0', 0)); + $pipe->get("key:$i"); + } +}); + +// Returns a pipeline that can be chained thanks to its fluent interface: +$responses = $client->pipeline()->set('foo', 'bar')->get('foo')->execute(); +``` + + +### Transactions ### + +The client provides an abstraction for Redis transactions based on `MULTI` and `EXEC` with a similar +interface to command pipelines: + +```php +// Executes a transaction inside the given callable block: +$responses = $client->transaction(function ($tx) { + $tx->set('foo', 'bar'); + $tx->get('foo'); +}); + +// Returns a transaction that can be chained thanks to its fluent interface: +$responses = $client->transaction()->set('foo', 'bar')->get('foo')->execute(); +``` + +This abstraction can perform check-and-set operations thanks to `WATCH` and `UNWATCH` and provides +automatic retries of transactions aborted by Redis when `WATCH`ed keys are touched. For an example +of a transaction using CAS you can see [the following example](examples/transaction_using_cas.php). + + +### Adding new commands ### + +While we try to update Predis to stay up to date with all the commands available in Redis, you might +prefer to stick with an old version of the library or provide a different way to filter arguments or +parse responses for specific commands. To achieve that, Predis provides the ability to implement new +command classes to define or override commands in the default server profiles used by the client: + +```php +// Define a new command by extending Predis\Command\Command: +class BrandNewRedisCommand extends Predis\Command\Command +{ + public function getId() + { + return 'NEWCMD'; + } +} + +// Inject your command in the current profile: +$client = new Predis\Client(); +$client->getProfile()->defineCommand('newcmd', 'BrandNewRedisCommand'); + +$response = $client->newcmd(); +``` + +There is also a method to send raw commands without filtering their arguments or parsing responses. +Users must provide the list of arguments for the command as an array, following the signatures as +defined by the [Redis documentation for commands](http://redis.io/commands): + +```php +$response = $client->executeRaw(['SET', 'foo', 'bar']); +``` + + +### Script commands ### + +While it is possible to leverage [Lua scripting](http://redis.io/commands/eval) on Redis 2.6+ using +directly [`EVAL`](http://redis.io/commands/eval) and [`EVALSHA`](http://redis.io/commands/evalsha), +Predis offers script commands as an higher level abstraction built upon them to make things simple. +Script commands can be registered in the server profile used by the client and are accessible as if +they were plain Redis commands, but they define Lua scripts that get transmitted to the server for +remote execution. Internally they use [`EVALSHA`](http://redis.io/commands/evalsha) by default and +identify a script by its SHA1 hash to save bandwidth, but [`EVAL`](http://redis.io/commands/eval) +is used as a fall back when needed: + +```php +// Define a new script command by extending Predis\Command\ScriptCommand: +class ListPushRandomValue extends Predis\Command\ScriptCommand +{ + public function getKeysCount() + { + return 1; + } + + public function getScript() + { + return <<getProfile()->defineCommand('lpushrand', 'ListPushRandomValue'); + +$response = $client->lpushrand('random_values', $seed = mt_rand()); +``` + + +### Customizable connection backends ### + +Predis can use different connection backends to connect to Redis. Two of them leverage a third party +extension such as [phpiredis](https://github.com/nrk/phpiredis) resulting in major performance gains +especially when dealing with big multibulk responses. While one is based on PHP streams, the other +is based on socket resources provided by `ext-socket`. Both support TCP/IP and UNIX domain sockets: + +```php +$client = new Predis\Client('tcp://127.0.0.1', [ + 'connections' => [ + 'tcp' => 'Predis\Connection\PhpiredisStreamConnection', // PHP stream resources + 'unix' => 'Predis\Connection\PhpiredisSocketConnection', // ext-socket resources + ], +]); +``` + +Developers can create their own connection classes to support whole new network backends, extend +existing classes or provide completely different implementations. Connection classes must implement +`Predis\Connection\NodeConnectionInterface` or extend `Predis\Connection\AbstractConnection`: + +```php +class MyConnectionClass implements Predis\Connection\NodeConnectionInterface +{ + // Implementation goes here... +} + +// Use MyConnectionClass to handle connections for the `tcp` scheme: +$client = new Predis\Client('tcp://127.0.0.1', [ + 'connections' => ['tcp' => 'MyConnectionClass'], +]); +``` + +For a more in-depth insight on how to create new connection backends you can refer to the actual +implementation of the standard connection classes available in the `Predis\Connection` namespace. + + +## Development ## + + +### Reporting bugs and contributing code ### + +Contributions to Predis are highly appreciated either in the form of pull requests for new features, +bug fixes, or just bug reports. We only ask you to adhere to a [basic set of rules](CONTRIBUTING.md) +before submitting your changes or filing bugs on the issue tracker to make it easier for everyone to +stay consistent while working on the project. + + +### Test suite ### + +__ATTENTION__: Do not ever run the test suite shipped with Predis against instances of Redis running +in production environments or containing data you are interested in! + +Predis has a comprehensive test suite covering every aspect of the library. This test suite performs +integration tests against a running instance of Redis (>= 2.4.0 is required) to verify the correct +behavior of the implementation of each command and automatically skips commands not defined in the +specified Redis profile. If you do not have Redis up and running, integration tests can be disabled. +By default the test suite is configured to execute integration tests using the profile for Redis 3.2 +(which is the current stable version of Redis) but can optionally target a Redis instance built from +the `unstable` branch by modifying `phpunit.xml` and setting `REDIS_SERVER_VERSION` to `dev` so that +the development server profile will be used. You can refer to [the tests README](tests/README.md) +for more detailed information about testing Predis. + +Predis uses Travis CI for continuous integration and the history for past and current builds can be +found [on its project page](http://travis-ci.org/predis/predis). + + +## Other ## + + +### Project related links ### + +- [Source code](https://github.com/predis/predis) +- [Wiki](https://github.com/predis/predis/wiki) +- [Issue tracker](https://github.com/predis/predis/issues) +- [PEAR channel](http://pear.nrk.io) + + +### Author ### + +- [Daniele Alessandri](mailto:suppakilla@gmail.com) ([twitter](http://twitter.com/JoL1hAHN)) + + +### License ### + +The code for Predis is distributed under the terms of the MIT license (see [LICENSE](LICENSE)). + +[ico-license]: https://img.shields.io/github/license/predis/predis.svg?style=flat-square +[ico-version-stable]: https://img.shields.io/packagist/v/predis/predis.svg?style=flat-square +[ico-version-dev]: https://img.shields.io/packagist/vpre/predis/predis.svg?style=flat-square +[ico-downloads-monthly]: https://img.shields.io/packagist/dm/predis/predis.svg?style=flat-square +[ico-travis]: https://img.shields.io/travis/predis/predis.svg?style=flat-square +[ico-hhvm]: https://img.shields.io/hhvm/predis/predis.svg?style=flat-square + +[link-packagist]: https://packagist.org/packages/predis/predis +[link-travis]: https://travis-ci.org/predis/predis +[link-downloads]: https://packagist.org/packages/predis/predis/stats diff --git a/vendor/predis/predis/VERSION b/vendor/predis/predis/VERSION new file mode 100644 index 000000000..0664a8fd2 --- /dev/null +++ b/vendor/predis/predis/VERSION @@ -0,0 +1 @@ +1.1.6 diff --git a/vendor/predis/predis/autoload.php b/vendor/predis/predis/autoload.php new file mode 100644 index 000000000..6b5a00b1b --- /dev/null +++ b/vendor/predis/predis/autoload.php @@ -0,0 +1,14 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require __DIR__.'/src/Autoloader.php'; + +Predis\Autoloader::register(); diff --git a/vendor/predis/predis/bin/create-command-test b/vendor/predis/predis/bin/create-command-test new file mode 100755 index 000000000..930797df0 --- /dev/null +++ b/vendor/predis/predis/bin/create-command-test @@ -0,0 +1,275 @@ +#!/usr/bin/env php + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +// -------------------------------------------------------------------------- // +// This script can be used to automatically generate a file with the scheleton +// of a test case to test a Redis command by specifying the name of the class +// in the Predis\Command namespace (only classes in this namespace are valid). +// For example, to generate a test case for SET (which is represented by the +// Predis\Command\StringSet class): +// +// $ ./bin/generate-command-test --class=StringSet +// +// Here is a list of optional arguments: +// +// --realm: each command has its own realm (commands that operate on strings, +// lists, sets and such) but while this realm is usually inferred from the name +// of the specified class, sometimes it can be useful to override it with a +// custom one. +// +// --output: write the generated test case to the specified path instead of +// the default one. +// +// --overwrite: pre-existing test files are not overwritten unless this option +// is explicitly specified. +// -------------------------------------------------------------------------- // + +use Predis\Command\CommandInterface; +use Predis\Command\PrefixableCommandInterface; + +class CommandTestCaseGenerator +{ + private $options; + + public function __construct(array $options) + { + if (!isset($options['class'])) { + throw new RuntimeException("Missing 'class' option."); + } + $this->options = $options; + } + + public static function fromCommandLine() + { + $parameters = array( + 'c:' => 'class:', + 'r::' => 'realm::', + 'o::' => 'output::', + 'x::' => 'overwrite::' + ); + + $getops = getopt(implode(array_keys($parameters)), $parameters); + + $options = array( + 'overwrite' => false, + 'tests' => __DIR__.'/../tests/Predis', + ); + + foreach ($getops as $option => $value) { + switch ($option) { + case 'c': + case 'class': + $options['class'] = $value; + break; + + case 'r': + case 'realm': + $options['realm'] = $value; + break; + + case 'o': + case 'output': + $options['output'] = $value; + break; + + case 'x': + case 'overwrite': + $options['overwrite'] = true; + break; + } + } + + if (!isset($options['class'])) { + throw new RuntimeException("Missing 'class' option."); + } + + $options['fqn'] = "Predis\\Command\\{$options['class']}"; + $options['path'] = "Command/{$options['class']}.php"; + + $source = __DIR__.'/../src/'.$options['path']; + if (!file_exists($source)) { + throw new RuntimeException("Cannot find class file for {$options['fqn']} in $source."); + } + + if (!isset($options['output'])) { + $options['output'] = sprintf("%s/%s", $options['tests'], str_replace('.php', 'Test.php', $options['path'])); + } + + return new self($options); + } + + protected function getTestRealm() + { + if (isset($this->options['realm'])) { + if (!$this->options['realm']) { + throw new RuntimeException('Invalid value for realm has been sepcified (empty).'); + } + return $this->options['realm']; + } + + $fqnParts = explode('\\', $this->options['fqn']); + $class = array_pop($fqnParts); + list($realm,) = preg_split('/([[:upper:]][[:lower:]]+)/', $class, 2, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + + return strtolower($realm); + } + + public function generate() + { + $reflection = new ReflectionClass($class = $this->options['fqn']); + + if (!$reflection->isInstantiable()) { + throw new RuntimeException("Class $class must be instantiable, abstract classes or interfaces are not allowed."); + } + if (!$reflection->implementsInterface('Predis\Command\CommandInterface')) { + throw new RuntimeException("Class $class must implement Predis\Command\CommandInterface."); + } + + /* + * @var CommandInterface + */ + $instance = $reflection->newInstance(); + + $buffer = $this->getTestCaseBuffer($instance); + + return $buffer; + } + + public function save() + { + $options = $this->options; + if (file_exists($options['output']) && !$options['overwrite']) { + throw new RuntimeException("File {$options['output']} already exist. Specify the --overwrite option to overwrite the existing file."); + } + file_put_contents($options['output'], $this->generate()); + } + + protected function getTestCaseBuffer(CommandInterface $instance) + { + $id = $instance->getId(); + $fqn = get_class($instance); + $fqnParts = explode('\\', $fqn); + $class = array_pop($fqnParts) . "Test"; + $realm = $this->getTestRealm(); + + $buffer =<< + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @group commands + * @group realm-$realm + */ +class $class extends PredisCommandTestCase +{ + /** + * {@inheritdoc} + */ + protected function getExpectedCommand() + { + return '$fqn'; + } + + /** + * {@inheritdoc} + */ + protected function getExpectedId() + { + return '$id'; + } + + /** + * @group disconnected + */ + public function testFilterArguments() + { + \$this->markTestIncomplete('This test has not been implemented yet.'); + + \$arguments = array(/* add arguments */); + \$expected = array(/* add arguments */); + + \$command = \$this->getCommand(); + \$command->setArguments(\$arguments); + + \$this->assertSame(\$expected, \$command->getArguments()); + } + + /** + * @group disconnected + */ + public function testParseResponse() + { + \$this->markTestIncomplete('This test has not been implemented yet.'); + + \$raw = null; + \$expected = null; + + \$command = \$this->getCommand(); + + \$this->assertSame(\$expected, \$command->parseResponse(\$raw)); + } + +PHP; + + if ($instance instanceof PrefixableCommandInterface) { + $buffer .=<<markTestIncomplete('This test has not been implemented yet.'); + + \$arguments = array(/* add arguments */); + \$expected = array(/* add arguments */); + + \$command = \$this->getCommandWithArgumentsArray(\$arguments); + \$command->prefixKeys('prefix:'); + + \$this->assertSame(\$expected, \$command->getArguments()); + } + + /** + * @group disconnected + */ + public function testPrefixKeysIgnoredOnEmptyArguments() + { + \$command = \$this->getCommand(); + \$command->prefixKeys('prefix:'); + + \$this->assertSame(array(), \$command->getArguments()); + } + +PHP; + } + + return "$buffer}\n"; + } +} + +// ------------------------------------------------------------------------- // + +require __DIR__.'/../autoload.php'; + +$generator = CommandTestCaseGenerator::fromCommandLine(); +$generator->save(); diff --git a/vendor/predis/predis/bin/create-pear b/vendor/predis/predis/bin/create-pear new file mode 100755 index 000000000..cfa0c2146 --- /dev/null +++ b/vendor/predis/predis/bin/create-pear @@ -0,0 +1,233 @@ +#!/usr/bin/env php + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +// -------------------------------------------------------------------------- // +// In order to be able to execute this script to create a PEAR package of Predis +// the `pear` binary must be available and executable in your $PATH. +// The parts used to parse author and version strings are taken from Onion (used +// by this library in the past) just to keep on relying on the package.ini file +// to simplify things. We might consider to switch to using the PEAR classes +// directly in the future. +// -------------------------------------------------------------------------- // + +function executeWithBackup($file, $callback) +{ + $exception = null; + $backup = "$file.backup"; + + copy($file, $backup); + + try { + call_user_func($callback, $file); + } catch (Exception $exception) { + // NOOP + } + + unlink($file); + rename($backup, $file); + + if ($exception) { + throw $exception; + } +} + +function parseAuthor($string) +{ + $author = array(); + + if (preg_match('/^\s*(.+?)\s*(?:"(\S+)"\s*)?<(\S+)>\s*$/x', $string , $regs)) { + if (count($regs) == 4) { + list($_,$name,$user,$email) = $regs; + $author['name'] = $name; + $author['user'] = $user; + $author['email'] = $email; + } elseif (count($regs) == 3) { + list($_,$name,$email) = $regs; + $author['name'] = $name; + $author['email'] = $email; + } + } else { + $author['name'] = $string; + } + + return $author; +} + +function parseVersion($string) +{ + $version_pattern = '([0-9.]+)'; + + if (preg_match("/^\s*$version_pattern\s*\$/x", $string, $regs)) { + return array('min' => $regs[1] ?: '0.0.0'); + } elseif (preg_match("/^\s*[>=]+\s*$version_pattern\s*\$/x", $string, $regs)) { + return array('min' => $regs[1] ?: '0.0.0'); + } elseif (preg_match("/^\s*[<=]+\s*$version_pattern\s*\$/x", $string, $regs)) { + return array('max' => $regs[1]); + } elseif (preg_match("/^\s*$version_pattern\s*<=>\s*$version_pattern\s*\$/x", $string, $regs)) { + return array( + 'min' => $regs[1] ?: '0.0.0', + 'max' => $regs[2], + ); + } + + return null; +} + +function addRolePath($pkg, $path, $role) +{ + if (is_dir($path)) { + $dirRoot = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS); + $dirTree = new RecursiveIteratorIterator($dirRoot, RecursiveIteratorIterator::CHILD_FIRST); + + foreach ($dirTree as $fileinfo) { + if ($fileinfo->isFile()) { + addPackageFile($pkg, $fileinfo, $role, $path); + } + } + } else { + foreach (glob($path) as $filename) { + addPackageFile($pkg, new SplFileInfo($filename), $role); + } + } +} + +function addPackageFile($pkg, $fileinfo, $role, $baseDir = '') +{ + $fileNode = $pkg->contents->dir->addChild('file'); + $fileNode->addAttribute('name', $filepath = $fileinfo->getPathname()); + $fileNode->addAttribute('role', $role); + $fileNode->addAttribute('md5sum', md5_file($filepath)); + + $installNode = $pkg->phprelease->filelist->addChild('install'); + $installNode->addAttribute('name', $filepath); + $installNode->addAttribute('as', !$baseDir ? basename($filepath) : substr($filepath, strlen($baseDir) + 1)); +} + +function generatePackageXml($packageINI) +{ + $XML = << + +XML; + + $cfg = parse_ini_file($packageINI, true); + $pkg = new SimpleXMLElement($XML); + + $pkg->name = $cfg['package']['name']; + $pkg->channel = $cfg['package']['channel']; + $pkg->summary = $cfg['package']['desc']; + $pkg->description = $cfg['package']['desc']; + + $author = parseAuthor($cfg['package']['author']); + $pkg->addChild('lead'); + $pkg->lead->name = $author['name']; + $pkg->lead->user = $author['user']; + $pkg->lead->email = $author['email']; + $pkg->lead->active = 'yes'; + + $datetime = new DateTime('now'); + $pkg->date = $datetime->format('Y-m-d'); + $pkg->time = $datetime->format('H:i:s'); + + $pkg->addChild('version'); + $pkg->version->release = $cfg['package']['version']; + $pkg->version->api = $cfg['package']['version']; + + $pkg->addChild('stability'); + $pkg->stability->release = $cfg['package']['stability']; + $pkg->stability->api = $cfg['package']['stability']; + + $pkg->license = $cfg['package']['license']; + $pkg->notes = '-'; + + $pkg->addChild('contents')->addChild('dir')->addAttribute('name', '/'); + + $pkg->addChild('dependencies')->addChild('required'); + foreach ($cfg['require'] as $required => $version) { + $version = parseVersion($version); + $pkg->dependencies->required->addChild($required); + + if (isset($version['min'])) { + $pkg->dependencies->required->$required->min = $version['min']; + } + if (isset($version['max'])) { + $pkg->dependencies->required->$required->min = $version['max']; + } + } + + $pkg->addChild('phprelease')->addChild('filelist'); + + $pathToRole = array( + 'doc' => 'doc', 'docs' => 'doc', 'examples' => 'doc', + 'lib' => 'php', 'src' => 'php', + 'test' => 'test', 'tests' => 'test', + ); + + foreach (array_merge($pathToRole, $cfg['roles'] ?: array()) as $path => $role) { + addRolePath($pkg, $path, $role); + } + + return $pkg; +} + +function rewritePackageInstallAs($pkg) +{ + foreach ($pkg->phprelease->filelist->install as $file) { + if (preg_match('/^src\//', $file['name'])) { + $file['as'] = "Predis/{$file['as']}"; + } + } +} + +function savePackageXml($xml) +{ + $dom = new DOMDocument("1.0", "UTF-8"); + $dom->preserveWhiteSpace = false; + $dom->formatOutput = true; + $dom->loadXML($xml->asXML()); + + file_put_contents('package.xml', $dom->saveXML()); +} + +function buildPackage() +{ + passthru('pear -q package && rm package.xml'); +} + +function modifyPhpunitXml($file) +{ + $cfg = new SimpleXMLElement($file, null, true); + + $cfg[0]['bootstrap'] = str_replace('tests/', '', $cfg[0]['bootstrap']); + $cfg->testsuites->testsuite->directory = str_replace('tests/', '', $cfg->testsuites->testsuite->directory); + + $cfg->saveXml($file); +} + +// -------------------------------------------------------------------------- // + +executeWithBackup(__DIR__.'/../phpunit.xml.dist', function ($file) { + modifyPhpunitXml($file); + + $pkg = generatePackageXml('package.ini'); + rewritePackageInstallAs($pkg); + savePackageXml($pkg); + + buildPackage(); +}); diff --git a/vendor/predis/predis/bin/create-phar b/vendor/predis/predis/bin/create-phar new file mode 100755 index 000000000..23618096d --- /dev/null +++ b/vendor/predis/predis/bin/create-phar @@ -0,0 +1,71 @@ +#!/usr/bin/env php + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +// -------------------------------------------------------------------------- // +// In order to be able to execute this script to create a Phar archive of Predis, +// the Phar module must be loaded and the "phar.readonly" directive php.ini must +// be set to "off". You can change the values in the $options array to customize +// the creation of the Phar archive to better suit your needs. +// -------------------------------------------------------------------------- // + +$options = array( + 'name' => 'predis', + 'project_path' => __DIR__ . '/../src', + 'compression' => Phar::NONE, + 'append_version' => true, +); + +function getPharFilename($options) +{ + $filename = $options['name']; + + // NOTE: do not consider "append_version" with Phar compression do to a bug in + // Phar::compress() when renaming phar archives containing dots in their name. + if ($options['append_version'] && $options['compression'] === Phar::NONE) { + $versionFile = @fopen(__DIR__ . '/../VERSION', 'r'); + + if ($versionFile === false) { + throw new Exception("Could not locate the VERSION file."); + } + + $version = trim(fgets($versionFile)); + fclose($versionFile); + $filename .= "_$version"; + } + + return "$filename.phar"; +} + +function getPharStub($options) +{ + return <<compress($options['compression']); +$phar->setStub(getPharStub($options)); +$phar->buildFromDirectory($options['project_path']); diff --git a/vendor/predis/predis/bin/create-single-file b/vendor/predis/predis/bin/create-single-file new file mode 100755 index 000000000..ecb876b0f --- /dev/null +++ b/vendor/predis/predis/bin/create-single-file @@ -0,0 +1,662 @@ +#!/usr/bin/env php + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +// -------------------------------------------------------------------------- // +// This script can be used to automatically glue all the .php files of Predis +// into a single monolithic script file that can be used without an autoloader, +// just like the other previous versions of the library. +// +// Much of its complexity is due to the fact that we cannot simply join PHP +// files, but namespaces and classes definitions must follow a precise order +// when dealing with subclassing and inheritance. +// +// The current implementation is pretty naïve, but it should do for now. +// -------------------------------------------------------------------------- // + +class CommandLine +{ + public static function getOptions() + { + $parameters = array( + 's:' => 'source:', + 'o:' => 'output:', + 'e:' => 'exclude:', + 'E:' => 'exclude-classes:', + ); + + $getops = getopt(implode(array_keys($parameters)), $parameters); + + $options = array( + 'source' => __DIR__ . "/../src", + 'output' => PredisFile::NS_ROOT . '.php', + 'exclude' => array(), + ); + + foreach ($getops as $option => $value) { + switch ($option) { + case 's': + case 'source': + $options['source'] = $value; + break; + + case 'o': + case 'output': + $options['output'] = $value; + break; + + case 'E': + case 'exclude-classes': + $options['exclude'] = @file($value, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: $value; + break; + + case 'e': + case 'exclude': + $options['exclude'] = is_array($value) ? $value : array($value); + break; + } + } + + return $options; + } +} + +class PredisFile +{ + const NS_ROOT = 'Predis'; + + private $namespaces; + + public function __construct() + { + $this->namespaces = array(); + } + + public static function from($libraryPath, array $exclude = array()) + { + $predisFile = new PredisFile(); + $libIterator = new RecursiveDirectoryIterator($libraryPath); + + foreach (new RecursiveIteratorIterator($libIterator) as $classFile) + { + if (!$classFile->isFile()) { + continue; + } + + $namespace = self::NS_ROOT.strtr(str_replace($libraryPath, '', $classFile->getPath()), '/', '\\'); + + if (in_array(sprintf('%s\\%s', $namespace, $classFile->getBasename('.php')), $exclude)) { + continue; + } + + $phpNamespace = $predisFile->getNamespace($namespace); + + if ($phpNamespace === false) { + $phpNamespace = new PhpNamespace($namespace); + $predisFile->addNamespace($phpNamespace); + } + + $phpClass = new PhpClass($phpNamespace, $classFile); + } + + return $predisFile; + } + + public function addNamespace(PhpNamespace $namespace) + { + if (isset($this->namespaces[(string)$namespace])) { + throw new InvalidArgumentException("Duplicated namespace"); + } + $this->namespaces[(string)$namespace] = $namespace; + } + + public function getNamespaces() + { + return $this->namespaces; + } + + public function getNamespace($namespace) + { + if (!isset($this->namespaces[$namespace])) { + return false; + } + + return $this->namespaces[$namespace]; + } + + public function getClassByFQN($classFqn) + { + if (($nsLastPos = strrpos($classFqn, '\\')) !== false) { + $namespace = $this->getNamespace(substr($classFqn, 0, $nsLastPos)); + if ($namespace === false) { + return null; + } + $className = substr($classFqn, $nsLastPos + 1); + + return $namespace->getClass($className); + } + + return null; + } + + private function calculateDependencyScores(&$classes, $fqn) + { + if (!isset($classes[$fqn])) { + $classes[$fqn] = 0; + } + + $classes[$fqn] += 1; + + if (($phpClass = $this->getClassByFQN($fqn)) === null) { + throw new RuntimeException( + "Cannot found the class $fqn which is required by other subclasses. Are you missing a file?" + ); + } + + foreach ($phpClass->getDependencies() as $fqn) { + $this->calculateDependencyScores($classes, $fqn); + } + } + + private function getDependencyScores() + { + $classes = array(); + + foreach ($this->getNamespaces() as $phpNamespace) { + foreach ($phpNamespace->getClasses() as $phpClass) { + $this->calculateDependencyScores($classes, $phpClass->getFQN()); + } + } + + return $classes; + } + + private function getOrderedNamespaces($dependencyScores) + { + $namespaces = array_fill_keys(array_unique( + array_map( + function ($fqn) { return PhpNamespace::extractName($fqn); }, + array_keys($dependencyScores) + ) + ), 0); + + foreach ($dependencyScores as $classFqn => $score) { + $namespaces[PhpNamespace::extractName($classFqn)] += $score; + } + + arsort($namespaces); + + return array_keys($namespaces); + } + + private function getOrderedClasses(PhpNamespace $phpNamespace, $classes) + { + $nsClassesFQNs = array_map(function ($cl) { return $cl->getFQN(); }, $phpNamespace->getClasses()); + $nsOrderedClasses = array(); + + foreach ($nsClassesFQNs as $nsClassFQN) { + $nsOrderedClasses[$nsClassFQN] = $classes[$nsClassFQN]; + } + + arsort($nsOrderedClasses); + + return array_keys($nsOrderedClasses); + } + + public function getPhpCode() + { + $buffer = array("getDependencyScores(); + $namespaces = $this->getOrderedNamespaces($classes); + + foreach ($namespaces as $namespace) { + $phpNamespace = $this->getNamespace($namespace); + + // generate namespace directive + $buffer[] = $phpNamespace->getPhpCode(); + $buffer[] = "\n"; + + // generate use directives + $useDirectives = $phpNamespace->getUseDirectives(); + if (count($useDirectives) > 0) { + $buffer[] = $useDirectives->getPhpCode(); + $buffer[] = "\n"; + } + + // generate classes bodies + $nsClasses = $this->getOrderedClasses($phpNamespace, $classes); + foreach ($nsClasses as $classFQN) { + $buffer[] = $this->getClassByFQN($classFQN)->getPhpCode(); + $buffer[] = "\n\n"; + } + + $buffer[] = "/* " . str_repeat("-", 75) . " */"; + $buffer[] = "\n\n"; + } + + return implode($buffer); + } + + public function saveTo($outputFile) + { + // TODO: add more sanity checks + if ($outputFile === null || $outputFile === '') { + throw new InvalidArgumentException('You must specify a valid output file'); + } + file_put_contents($outputFile, $this->getPhpCode()); + } +} + +class PhpNamespace implements IteratorAggregate +{ + private $namespace; + private $classes; + + public function __construct($namespace) + { + $this->namespace = $namespace; + $this->classes = array(); + $this->useDirectives = new PhpUseDirectives($this); + } + + public static function extractName($fqn) + { + $nsSepLast = strrpos($fqn, '\\'); + if ($nsSepLast === false) { + return $fqn; + } + $ns = substr($fqn, 0, $nsSepLast); + + return $ns !== '' ? $ns : null; + } + + public function addClass(PhpClass $class) + { + $this->classes[$class->getName()] = $class; + } + + public function getClass($className) + { + if (isset($this->classes[$className])) { + return $this->classes[$className]; + } + } + + public function getClasses() + { + return array_values($this->classes); + } + + public function getIterator() + { + return new \ArrayIterator($this->getClasses()); + } + + public function getUseDirectives() + { + return $this->useDirectives; + } + + public function getPhpCode() + { + return "namespace $this->namespace;\n"; + } + + public function __toString() + { + return $this->namespace; + } +} + +class PhpUseDirectives implements Countable, IteratorAggregate +{ + private $use; + private $aliases; + private $reverseAliases; + private $namespace; + + public function __construct(PhpNamespace $namespace) + { + $this->namespace = $namespace; + $this->use = array(); + $this->aliases = array(); + $this->reverseAliases = array(); + } + + public function add($use, $as = null) + { + if (in_array($use, $this->use)) { + return; + } + + $rename = null; + $this->use[] = $use; + $aliasedClassName = $as ?: PhpClass::extractName($use); + + if (isset($this->aliases[$aliasedClassName])) { + $parentNs = $this->getParentNamespace(); + + if ($parentNs && false !== $pos = strrpos($parentNs, '\\')) { + $parentNs = substr($parentNs, $pos); + } + + $newAlias = "{$parentNs}_{$aliasedClassName}"; + $rename = (object) array( + 'namespace' => $this->namespace, + 'from' => $aliasedClassName, + 'to' => $newAlias, + ); + + $this->aliases[$newAlias] = $use; + $as = $newAlias; + } else { + $this->aliases[$aliasedClassName] = $use; + } + + if ($as !== null) { + $this->reverseAliases[$use] = $as; + } + + return $rename; + } + + public function getList() + { + return $this->use; + } + + public function getIterator() + { + return new \ArrayIterator($this->getList()); + } + + public function getPhpCode() + { + $reverseAliases = $this->reverseAliases; + + $reducer = function ($str, $use) use ($reverseAliases) { + if (isset($reverseAliases[$use])) { + return $str .= "use $use as {$reverseAliases[$use]};\n"; + } else { + return $str .= "use $use;\n"; + } + }; + + return array_reduce($this->getList(), $reducer, ''); + } + + public function getNamespace() + { + return $this->namespace; + } + + public function getParentNamespace() + { + if (false !== $pos = strrpos($this->namespace, '\\')) { + return substr($this->namespace, 0, $pos); + } + + return ''; + } + + public function getFQN($className) + { + if (($nsSepFirst = strpos($className, '\\')) === false) { + if (isset($this->aliases[$className])) { + return $this->aliases[$className]; + } + + return (string)$this->getNamespace() . "\\$className"; + } + + if ($nsSepFirst != 0) { + throw new InvalidArgumentException("Partially qualified names are not supported"); + } + + return $className; + } + + public function count() + { + return count($this->use); + } +} + +class PhpClass +{ + const LICENSE_HEADER = << + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +LICENSE; + + private $namespace; + private $file; + private $body; + private $implements; + private $extends; + private $name; + + public function __construct(PhpNamespace $namespace, SplFileInfo $classFile) + { + $this->namespace = $namespace; + $this->file = $classFile; + $this->implements = array(); + $this->extends = array(); + + $this->extractData(); + $namespace->addClass($this); + } + + public static function extractName($fqn) + { + $nsSepLast = strrpos($fqn, '\\'); + if ($nsSepLast === false) { + return $fqn; + } + + return substr($fqn, $nsSepLast + 1); + } + + private function extractData() + { + $renames = array(); + $useDirectives = $this->getNamespace()->getUseDirectives(); + + $useExtractor = function ($m) use ($useDirectives, &$renames) { + array_shift($m); + + if (isset($m[1])) { + $m[1] = str_replace(" as ", '', $m[1]); + } + + if ($rename = call_user_func_array(array($useDirectives, 'add'), $m)) { + $renames[] = $rename; + } + }; + + $classBuffer = stream_get_contents(fopen($this->getFile()->getPathname(), 'r')); + + $classBuffer = str_replace(self::LICENSE_HEADER, '', $classBuffer); + + $classBuffer = preg_replace('/<\?php\s?\\n\s?/', '', $classBuffer); + $classBuffer = preg_replace('/\s?\?>\n?/ms', '', $classBuffer); + $classBuffer = preg_replace('/namespace\s+[\w\d_\\\\]+;\s?/', '', $classBuffer); + $classBuffer = preg_replace_callback('/use\s+([\w\d_\\\\]+)(\s+as\s+.*)?;\s?\n?/', $useExtractor, $classBuffer); + + foreach ($renames as $rename) { + $classBuffer = str_replace($rename->from, $rename->to, $classBuffer); + } + + $this->body = trim($classBuffer); + + $this->extractHierarchy(); + } + + private function extractHierarchy() + { + $implements = array(); + $extends = array(); + + $extractor = function ($iterator, $callback) { + $className = ''; + $iterator->seek($iterator->key() + 1); + + while ($iterator->valid()) { + $token = $iterator->current(); + + if (is_string($token)) { + if (preg_match('/\s?,\s?/', $token)) { + $callback(trim($className)); + $className = ''; + } else if ($token == '{') { + $callback(trim($className)); + return; + } + } + + switch ($token[0]) { + case T_NS_SEPARATOR: + $className .= '\\'; + break; + + case T_STRING: + $className .= $token[1]; + break; + + case T_IMPLEMENTS: + case T_EXTENDS: + $callback(trim($className)); + $iterator->seek($iterator->key() - 1); + return; + } + + $iterator->next(); + } + }; + + $tokens = token_get_all("getPhpCode())); + $iterator = new ArrayIterator($tokens); + + while ($iterator->valid()) { + $token = $iterator->current(); + if (is_string($token)) { + $iterator->next(); + continue; + } + + switch ($token[0]) { + case T_CLASS: + case T_INTERFACE: + $iterator->seek($iterator->key() + 2); + $tk = $iterator->current(); + $this->name = $tk[1]; + break; + + case T_IMPLEMENTS: + $extractor($iterator, function ($fqn) use (&$implements) { + $implements[] = $fqn; + }); + break; + + case T_EXTENDS: + $extractor($iterator, function ($fqn) use (&$extends) { + $extends[] = $fqn; + }); + break; + } + + $iterator->next(); + } + + $this->implements = $this->guessFQN($implements); + $this->extends = $this->guessFQN($extends); + } + + public function guessFQN($classes) + { + $useDirectives = $this->getNamespace()->getUseDirectives(); + return array_map(array($useDirectives, 'getFQN'), $classes); + } + + public function getImplementedInterfaces($all = false) + { + if ($all) { + return $this->implements; + } + + return array_filter( + $this->implements, + function ($cn) { return strpos($cn, 'Predis\\') === 0; } + ); + } + + public function getExtendedClasses($all = false) + { + if ($all) { + return $this->extemds; + } + + return array_filter( + $this->extends, + function ($cn) { return strpos($cn, 'Predis\\') === 0; } + ); + } + + public function getDependencies($all = false) + { + return array_merge( + $this->getImplementedInterfaces($all), + $this->getExtendedClasses($all) + ); + } + + public function getNamespace() + { + return $this->namespace; + } + + public function getFile() + { + return $this->file; + } + + public function getName() + { + return $this->name; + } + + public function getFQN() + { + return (string)$this->getNamespace() . '\\' . $this->name; + } + + public function getPhpCode() + { + return $this->body; + } + + public function __toString() + { + return "class " . $this->getName() . '{ ... }'; + } +} + +/* -------------------------------------------------------------------------- */ + +$options = CommandLine::getOptions(); +$predisFile = PredisFile::from($options['source'], $options['exclude']); +$predisFile->saveTo($options['output']); diff --git a/vendor/predis/predis/composer.json b/vendor/predis/predis/composer.json new file mode 100644 index 000000000..4d273062b --- /dev/null +++ b/vendor/predis/predis/composer.json @@ -0,0 +1,58 @@ +{ + "name": "predis/predis", + "type": "library", + "description": "Flexible and feature-complete Redis client for PHP and HHVM", + "keywords": ["nosql", "redis", "predis"], + "homepage": "http://github.com/predis/predis", + "license": "MIT", + "support": { + "issues": "https://github.com/predis/predis/issues" + }, + "authors": [ + { + "name": "Daniele Alessandri", + "email": "suppakilla@gmail.com", + "homepage": "http://clorophilla.net", + "role": "Creator & Maintainer" + }, + { + "name": "Till Krüss", + "homepage": "https://till.im", + "role": "Maintainer" + } + ], + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/tillkruss" + } + ], + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "phpunit/phpunit": "~4.8", + "cweagans/composer-patches": "^1.6" + }, + "suggest": { + "ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol", + "ext-curl": "Allows access to Webdis when paired with phpiredis" + }, + "autoload": { + "psr-4": { + "Predis\\": "src/" + } + }, + "extra": { + "composer-exit-on-patch-failure": true, + "patches": { + "phpunit/phpunit-mock-objects": { + "Fix PHP 7 and 8 compatibility": "./tests/phpunit_mock_objects.patch" + }, + "phpunit/phpunit": { + "Fix PHP 7 compatibility": "./tests/phpunit_php7.patch", + "Fix PHP 8 compatibility": "./tests/phpunit_php8.patch" + } + } + } +} diff --git a/vendor/predis/predis/examples/custom_cluster_distributor.php b/vendor/predis/predis/examples/custom_cluster_distributor.php new file mode 100644 index 000000000..0a3a421e7 --- /dev/null +++ b/vendor/predis/predis/examples/custom_cluster_distributor.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require __DIR__.'/shared.php'; + +// Developers can implement Predis\Distribution\DistributorInterface to create +// their own distributors used by the client to distribute keys among a cluster +// of servers. + +use Predis\Cluster\Distributor\DistributorInterface; +use Predis\Cluster\Hash\HashGeneratorInterface; +use Predis\Cluster\PredisStrategy; +use Predis\Connection\Aggregate\PredisCluster; + +class NaiveDistributor implements DistributorInterface, HashGeneratorInterface +{ + private $nodes; + private $nodesCount; + + public function __construct() + { + $this->nodes = array(); + $this->nodesCount = 0; + } + + public function add($node, $weight = null) + { + $this->nodes[] = $node; + ++$this->nodesCount; + } + + public function remove($node) + { + $this->nodes = array_filter($this->nodes, function ($n) use ($node) { + return $n !== $node; + }); + + $this->nodesCount = count($this->nodes); + } + + public function getSlot($hash) + { + return $this->nodesCount > 1 ? abs($hash % $this->nodesCount) : 0; + } + + public function getBySlot($slot) + { + return isset($this->nodes[$slot]) ? $this->nodes[$slot] : null; + } + + public function getByHash($hash) + { + if (!$this->nodesCount) { + throw new RuntimeException('No connections.'); + } + + $slot = $this->getSlot($hash); + $node = $this->getBySlot($slot); + + return $node; + } + + public function get($value) + { + $hash = $this->hash($value); + $node = $this->getByHash($hash); + + return $node; + } + + public function hash($value) + { + return crc32($value); + } + + public function getHashGenerator() + { + return $this; + } +} + +$options = array( + 'cluster' => function () { + $distributor = new NaiveDistributor(); + $strategy = new PredisStrategy($distributor); + $cluster = new PredisCluster($strategy); + + return $cluster; + }, +); + +$client = new Predis\Client($multiple_servers, $options); + +for ($i = 0; $i < 100; ++$i) { + $client->set("key:$i", str_pad($i, 4, '0', 0)); + $client->get("key:$i"); +} + +$server1 = $client->getClientFor('first')->info(); +$server2 = $client->getClientFor('second')->info(); + +if (isset($server1['Keyspace'], $server2['Keyspace'])) { + $server1 = $server1['Keyspace']; + $server2 = $server2['Keyspace']; +} + +printf("Server '%s' has %d keys while server '%s' has %d keys.\n", + 'first', $server1['db15']['keys'], 'second', $server2['db15']['keys'] +); diff --git a/vendor/predis/predis/examples/debuggable_connection.php b/vendor/predis/predis/examples/debuggable_connection.php new file mode 100644 index 000000000..346b0035a --- /dev/null +++ b/vendor/predis/predis/examples/debuggable_connection.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require __DIR__.'/shared.php'; + +// This is an example of how you can easily extend an existing connection class +// and trace the execution of commands for debugging purposes. This can be quite +// useful as a starting poing to understand how your application interacts with +// Redis. + +use Predis\Command\CommandInterface; +use Predis\Connection\StreamConnection; + +class SimpleDebuggableConnection extends StreamConnection +{ + private $tstart = 0; + private $debugBuffer = array(); + + public function connect() + { + $this->tstart = microtime(true); + + parent::connect(); + } + + private function storeDebug(CommandInterface $command, $direction) + { + $firtsArg = $command->getArgument(0); + $timestamp = round(microtime(true) - $this->tstart, 4); + + $debug = $command->getId(); + $debug .= isset($firtsArg) ? " $firtsArg " : ' '; + $debug .= "$direction $this"; + $debug .= " [{$timestamp}s]"; + + $this->debugBuffer[] = $debug; + } + + public function writeRequest(CommandInterface $command) + { + parent::writeRequest($command); + + $this->storeDebug($command, '->'); + } + + public function readResponse(CommandInterface $command) + { + $response = parent::readResponse($command); + $this->storeDebug($command, '<-'); + + return $response; + } + + public function getDebugBuffer() + { + return $this->debugBuffer; + } +} + +$options = array( + 'connections' => array( + 'tcp' => 'SimpleDebuggableConnection', + ), +); + +$client = new Predis\Client($single_server, $options); +$client->set('foo', 'bar'); +$client->get('foo'); +$client->info(); + +var_export($client->getConnection()->getDebugBuffer()); + +/* OUTPUT: +array ( + 0 => 'SELECT 15 -> 127.0.0.1:6379 [0.0008s]', + 1 => 'SELECT 15 <- 127.0.0.1:6379 [0.001s]', + 2 => 'SET foo -> 127.0.0.1:6379 [0.001s]', + 3 => 'SET foo <- 127.0.0.1:6379 [0.0011s]', + 4 => 'GET foo -> 127.0.0.1:6379 [0.0013s]', + 5 => 'GET foo <- 127.0.0.1:6379 [0.0015s]', + 6 => 'INFO -> 127.0.0.1:6379 [0.0019s]', + 7 => 'INFO <- 127.0.0.1:6379 [0.0022s]', +) +*/ diff --git a/vendor/predis/predis/examples/dispatcher_loop.php b/vendor/predis/predis/examples/dispatcher_loop.php new file mode 100644 index 000000000..7123491b7 --- /dev/null +++ b/vendor/predis/predis/examples/dispatcher_loop.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require __DIR__.'/shared.php'; + +// This is a basic example on how to use the Predis\DispatcherLoop class. +// +// To see this example in action you can just use redis-cli and publish some +// messages to the 'events' and 'control' channel, e.g.: + +// ./redis-cli +// PUBLISH events first +// PUBLISH events second +// PUBLISH events third +// PUBLISH control terminate_dispatcher + +// Create a client and disable r/w timeout on the socket +$client = new Predis\Client($single_server + array('read_write_timeout' => 0)); + +// Return an initialized PubSub consumer instance from the client. +$pubsub = $client->pubSubLoop(); + +// Create a dispatcher loop instance and attach a bunch of callbacks. +$dispatcher = new Predis\PubSub\DispatcherLoop($pubsub); + +// Demonstrate how to use a callable class as a callback for the dispatcher loop. +class EventsListener implements Countable +{ + private $events; + + public function __construct() + { + $this->events = array(); + } + + public function count() + { + return count($this->events); + } + + public function getEvents() + { + return $this->events; + } + + public function __invoke($payload) + { + $this->events[] = $payload; + } +} + +// Attach our callable class to the dispatcher. +$dispatcher->attachCallback('events', ($events = new EventsListener())); + +// Attach a function to control the dispatcher loop termination with a message. +$dispatcher->attachCallback('control', function ($payload) use ($dispatcher) { + if ($payload === 'terminate_dispatcher') { + $dispatcher->stop(); + } +}); + +// Run the dispatcher loop until the callback attached to the 'control' channel +// receives 'terminate_dispatcher' as a message. +$dispatcher->run(); + +// Display our achievements! +echo "We received {$events->count()} messages!", PHP_EOL; + +// Say goodbye :-) +$version = redis_version($client->info()); +echo "Goodbye from Redis $version!", PHP_EOL; diff --git a/vendor/predis/predis/examples/executing_redis_commands.php b/vendor/predis/predis/examples/executing_redis_commands.php new file mode 100644 index 000000000..6e5675361 --- /dev/null +++ b/vendor/predis/predis/examples/executing_redis_commands.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require __DIR__.'/shared.php'; + +$client = new Predis\Client($single_server); + +// Plain old SET and GET example... +$client->set('library', 'predis'); +$response = $client->get('library'); + +var_export($response); echo PHP_EOL; +/* OUTPUT: 'predis' */ + +// Redis has the MSET and MGET commands to set or get multiple keys in one go, +// cases like this Predis accepts arguments for variadic commands both as a list +// of arguments or an array containing all of the keys and/or values. +$mkv = array( + 'uid:0001' => '1st user', + 'uid:0002' => '2nd user', + 'uid:0003' => '3rd user', +); + +$client->mset($mkv); +$response = $client->mget(array_keys($mkv)); + +var_export($response); echo PHP_EOL; +/* OUTPUT: +array ( + 0 => '1st user', + 1 => '2nd user', + 2 => '3rd user', +) */ + +// Predis can also send "raw" commands to Redis. The difference between sending +// commands to Redis the usual way and the "raw" way is that in the latter case +// their arguments are not filtered nor responses coming from Redis are parsed. + +$response = $client->executeRaw(array( + 'MGET', 'uid:0001', 'uid:0002', 'uid:0003', +)); + +var_export($response); echo PHP_EOL; +/* OUTPUT: +array ( + 0 => '1st user', + 1 => '2nd user', + 2 => '3rd user', +) */ diff --git a/vendor/predis/predis/examples/key_prefixing.php b/vendor/predis/predis/examples/key_prefixing.php new file mode 100644 index 000000000..1486330d2 --- /dev/null +++ b/vendor/predis/predis/examples/key_prefixing.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require __DIR__.'/shared.php'; + +// Predis can prefix keys found in commands arguments before sending commands to +// Redis, even for complex commands such as SORT, ZUNIONSTORE and ZINTERSTORE. +// Prefixing keys can be useful to create user-level namespaces for you keyspace +// thus reducing the need for separate logical databases in certain scenarios. + +$client = new Predis\Client($single_server, array('prefix' => 'nrk:')); + +$client->mset(array('foo' => 'bar', 'lol' => 'wut')); +var_export($client->mget('foo', 'lol')); +/* +array ( + 0 => 'bar', + 1 => 'wut', +) +*/ + +var_export($client->keys('*')); +/* +array ( + 0 => 'nrk:foo', + 1 => 'nrk:lol', +) +*/ diff --git a/vendor/predis/predis/examples/lua_scripting_abstraction.php b/vendor/predis/predis/examples/lua_scripting_abstraction.php new file mode 100644 index 000000000..50c07bf49 --- /dev/null +++ b/vendor/predis/predis/examples/lua_scripting_abstraction.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require __DIR__.'/shared.php'; + +// This example will not work with versions of Redis < 2.6. +// +// Additionally to the EVAL command defined in the current development profile, +// the Predis\Command\ScriptCommand class can be used to build an higher level +// abstraction for "scriptable" commands so that they will appear just like any +// other command on the client-side. This is a quick example used to implement +// INCREX. + +use Predis\Command\ScriptCommand; + +class IncrementExistingKeysBy extends ScriptCommand +{ + public function getKeysCount() + { + // Tell Predis to use all the arguments but the last one as arguments + // for KEYS. The last one will be used to populate ARGV. + return -1; + } + + public function getScript() + { + return << function ($options) { + $profile = $options->getDefault('profile'); + $profile->defineCommand('increxby', 'IncrementExistingKeysBy'); + + return $profile; + }, +)); + +$client->mset('foo', 10, 'foobar', 100); + +var_export($client->increxby('foo', 'foofoo', 'foobar', 50)); + +/* +array ( + 0 => 60, + 1 => NULL, + 2 => 150, +) +*/ diff --git a/vendor/predis/predis/examples/monitor_consumer.php b/vendor/predis/predis/examples/monitor_consumer.php new file mode 100644 index 000000000..d472049b2 --- /dev/null +++ b/vendor/predis/predis/examples/monitor_consumer.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require __DIR__.'/shared.php'; + +// This is a basic example on how to use the Predis\Monitor\Consumer class. You +// can use redis-cli to send commands to the same Redis instance your client is +// connected to, and then type "ECHO QUIT_MONITOR" in redis-cli when you want to +// exit the monitor loop and terminate this script in a graceful way. + +// Create a client and disable r/w timeout on the socket. +$client = new Predis\Client($single_server + array('read_write_timeout' => 0)); + +// Use only one instance of DateTime, we will update the timestamp later. +$timestamp = new DateTime(); + +foreach (($monitor = $client->monitor()) as $event) { + $timestamp->setTimestamp((int) $event->timestamp); + + // If we notice a ECHO command with the message QUIT_MONITOR, we stop the + // monitor consumer and then break the loop. + if ($event->command === 'ECHO' && $event->arguments === '"QUIT_MONITOR"') { + echo 'Exiting the monitor loop...', PHP_EOL; + $monitor->stop(); + break; + } + + echo "* Received {$event->command} on DB {$event->database} at {$timestamp->format(DateTime::W3C)}", PHP_EOL; + if (isset($event->arguments)) { + echo " Arguments: {$event->arguments}", PHP_EOL; + } +} + +// Say goodbye :-) +$version = redis_version($client->info()); +echo "Goodbye from Redis $version!", PHP_EOL; diff --git a/vendor/predis/predis/examples/pipelining_commands.php b/vendor/predis/predis/examples/pipelining_commands.php new file mode 100644 index 000000000..632ef94cc --- /dev/null +++ b/vendor/predis/predis/examples/pipelining_commands.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require __DIR__.'/shared.php'; + +// When you have a whole set of consecutive commands to send to a redis server, +// you can use a pipeline to dramatically improve performances. Pipelines can +// greatly reduce the effects of network round-trips. + +$client = new Predis\Client($single_server); + +$responses = $client->pipeline(function ($pipe) { + $pipe->flushdb(); + $pipe->incrby('counter', 10); + $pipe->incrby('counter', 30); + $pipe->exists('counter'); + $pipe->get('counter'); + $pipe->mget('does_not_exist', 'counter'); +}); + +var_export($responses); + +/* OUTPUT: +array ( + 0 => Predis\Response\Status::__set_state(array( + 'payload' => 'OK', + )), + 1 => 10, + 2 => 40, + 3 => true, + 4 => '40', + 5 => array ( + 0 => NULL, + 1 => '40', + ), +) +*/ diff --git a/vendor/predis/predis/examples/pubsub_consumer.php b/vendor/predis/predis/examples/pubsub_consumer.php new file mode 100644 index 000000000..24c485cca --- /dev/null +++ b/vendor/predis/predis/examples/pubsub_consumer.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require __DIR__.'/shared.php'; + +// Starting from Redis 2.0 clients can subscribe and listen for events published +// on certain channels using a Publish/Subscribe (PUB/SUB) approach. + +// Create a client and disable r/w timeout on the socket +$client = new Predis\Client($single_server + array('read_write_timeout' => 0)); + +// Initialize a new pubsub consumer. +$pubsub = $client->pubSubLoop(); + +// Subscribe to your channels +$pubsub->subscribe('control_channel', 'notifications'); + +// Start processing the pubsup messages. Open a terminal and use redis-cli +// to push messages to the channels. Examples: +// ./redis-cli PUBLISH notifications "this is a test" +// ./redis-cli PUBLISH control_channel quit_loop +foreach ($pubsub as $message) { + switch ($message->kind) { + case 'subscribe': + echo "Subscribed to {$message->channel}", PHP_EOL; + break; + + case 'message': + if ($message->channel == 'control_channel') { + if ($message->payload == 'quit_loop') { + echo 'Aborting pubsub loop...', PHP_EOL; + $pubsub->unsubscribe(); + } else { + echo "Received an unrecognized command: {$message->payload}.", PHP_EOL; + } + } else { + echo "Received the following message from {$message->channel}:", + PHP_EOL, " {$message->payload}", PHP_EOL, PHP_EOL; + } + break; + } +} + +// Always unset the pubsub consumer instance when you are done! The +// class destructor will take care of cleanups and prevent protocol +// desynchronizations between the client and the server. +unset($pubsub); + +// Say goodbye :-) +$version = redis_version($client->info()); +echo "Goodbye from Redis $version!", PHP_EOL; diff --git a/vendor/predis/predis/examples/redis_collections_iterators.php b/vendor/predis/predis/examples/redis_collections_iterators.php new file mode 100644 index 000000000..62755c9fc --- /dev/null +++ b/vendor/predis/predis/examples/redis_collections_iterators.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require __DIR__.'/shared.php'; + +use Predis\Collection\Iterator; + +// Starting from Redis 2.8, clients can iterate incrementally over collections +// without blocking the server like it happens when a command such as KEYS is +// executed on a Redis instance storing millions of keys. These commands are: +// +// - SCAN (iterates over the keyspace) +// - SSCAN (iterates over members of a set) +// - ZSCAN (iterates over members and ranks of a sorted set) +// - HSCAN (iterates over fields and values of an hash). + +// Predis provides a specialized abstraction for each command based on standard +// SPL iterators making it possible to easily consume SCAN-based iterations in +// your PHP code. +// +// See http://redis.io/commands/scan for more details. +// + +// Create a client using `2.8` as a server profile (needs Redis 2.8!) +$client = new Predis\Client($single_server, array('profile' => '2.8')); + +// Prepare some keys for our example +$client->del('predis:set', 'predis:zset', 'predis:hash'); +for ($i = 0; $i < 5; ++$i) { + $client->sadd('predis:set', "member:$i"); + $client->zadd('predis:zset', -$i, "member:$i"); + $client->hset('predis:hash', "field:$i", "value:$i"); +} + +// === Keyspace iterator based on SCAN === +echo 'Scan the keyspace matching only our prefixed keys:', PHP_EOL; +foreach (new Iterator\Keyspace($client, 'predis:*') as $key) { + echo " - $key", PHP_EOL; +} + +/* OUTPUT +Scan the keyspace matching only our prefixed keys: + - predis:zset + - predis:set + - predis:hash +*/ + +// === Set iterator based on SSCAN === +echo 'Scan members of `predis:set`:', PHP_EOL; +foreach (new Iterator\SetKey($client, 'predis:set') as $member) { + echo " - $member", PHP_EOL; +} + +/* OUTPUT +Scan members of `predis:set`: + - member:1 + - member:4 + - member:0 + - member:3 + - member:2 +*/ + +// === Sorted set iterator based on ZSCAN === +echo 'Scan members and ranks of `predis:zset`:', PHP_EOL; +foreach (new Iterator\SortedSetKey($client, 'predis:zset') as $member => $rank) { + echo " - $member [rank: $rank]", PHP_EOL; +} + +/* OUTPUT +Scan members and ranks of `predis:zset`: + - member:4 [rank: -4] + - member:3 [rank: -3] + - member:2 [rank: -2] + - member:1 [rank: -1] + - member:0 [rank: 0] +*/ + +// === Hash iterator based on HSCAN === +echo 'Scan fields and values of `predis:hash`:', PHP_EOL; +foreach (new Iterator\HashKey($client, 'predis:hash') as $field => $value) { + echo " - $field => $value", PHP_EOL; +} + +/* OUTPUT +Scan fields and values of `predis:hash`: + - field:0 => value:0 + - field:1 => value:1 + - field:2 => value:2 + - field:3 => value:3 + - field:4 => value:4 +*/ diff --git a/vendor/predis/predis/examples/replication_complex.php b/vendor/predis/predis/examples/replication_complex.php new file mode 100644 index 000000000..71193d236 --- /dev/null +++ b/vendor/predis/predis/examples/replication_complex.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require __DIR__.'/shared.php'; + +// Predis allows to set Lua scripts as read-only operations for replication. +// This works for both EVAL and EVALSHA and also for the client-side abstraction +// built upon them (Predis\Command\ScriptCommand). This example shows a slightly +// more complex configuration that injects a new script command in the server +// profile used by the new client instance and marks it marks it as a read-only +// operation for replication so that it will be executed on slaves. + +use Predis\Command\ScriptCommand; +use Predis\Connection\Aggregate\MasterSlaveReplication; +use Predis\Replication\ReplicationStrategy; + +// ------------------------------------------------------------------------- // + +// Define a new script command that returns all the fields of a variable number +// of hashes with a single roundtrip. + +class HashMultipleGetAll extends ScriptCommand +{ + const BODY = << function ($options, $option) { + $profile = $options->getDefault($option); + $profile->defineCommand('hmgetall', 'HashMultipleGetAll'); + + return $profile; + }, + 'replication' => function () { + $strategy = new ReplicationStrategy(); + $strategy->setScriptReadOnly(HashMultipleGetAll::BODY); + + $replication = new MasterSlaveReplication($strategy); + + return $replication; + }, +); + +// ------------------------------------------------------------------------- // + +$client = new Predis\Client($parameters, $options); + +// Execute the following commands on the master server using redis-cli: +// $ ./redis-cli HMSET metavars foo bar hoge piyo +// $ ./redis-cli HMSET servers master host1 slave host2 + +$hashes = $client->hmgetall('metavars', 'servers'); + +$replication = $client->getConnection(); +$stillOnSlave = $replication->getCurrent() === $replication->getConnectionById('slave'); + +echo 'Is still on slave? ', $stillOnSlave ? 'YES!' : 'NO!', PHP_EOL; +var_export($hashes); diff --git a/vendor/predis/predis/examples/replication_sentinel.php b/vendor/predis/predis/examples/replication_sentinel.php new file mode 100644 index 000000000..f1e0de8a0 --- /dev/null +++ b/vendor/predis/predis/examples/replication_sentinel.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require __DIR__.'/shared.php'; + +// Predis supports redis-sentinel to provide high availability in master / slave +// scenarios. The only but relevant difference with a basic replication scenario +// is that sentinel servers can manage the master server and its slaves based on +// their state, which means that they are able to provide an authoritative and +// updated configuration to clients thus avoiding static configurations for the +// replication servers and their roles. + +// Instead of connection parameters pointing to redis nodes, we provide a list +// of instances of redis-sentinel. Users should always provide a timeout value +// low enough to not hinder operations just in case a sentinel is unreachable +// but Predis uses a default value of 100 milliseconds for sentinel parameters +// without an explicit timeout value. +// +// NOTE: in real-world scenarios sentinels should be running on different hosts! +$sentinels = array( + 'tcp://127.0.0.1:5380?timeout=0.100', + 'tcp://127.0.0.1:5381?timeout=0.100', + 'tcp://127.0.0.1:5382?timeout=0.100', +); + +$client = new Predis\Client($sentinels, array( + 'replication' => 'sentinel', + 'service' => 'mymaster', +)); + +// Read operation. +$exists = $client->exists('foo') ? 'yes' : 'no'; +$current = $client->getConnection()->getCurrent()->getParameters(); +echo "Does 'foo' exist on {$current->alias}? $exists.", PHP_EOL; + +// Write operation. +$client->set('foo', 'bar'); +$current = $client->getConnection()->getCurrent()->getParameters(); +echo "Now 'foo' has been set to 'bar' on {$current->alias}!", PHP_EOL; + +// Read operation. +$bar = $client->get('foo'); +$current = $client->getConnection()->getCurrent()->getParameters(); +echo "We fetched 'foo' from {$current->alias} and its value is '$bar'.", PHP_EOL; + +/* OUTPUT: +Does 'foo' exist on slave-127.0.0.1:6381? yes. +Now 'foo' has been set to 'bar' on master! +We fetched 'foo' from master and its value is 'bar'. +*/ diff --git a/vendor/predis/predis/examples/replication_simple.php b/vendor/predis/predis/examples/replication_simple.php new file mode 100644 index 000000000..91b6db9c0 --- /dev/null +++ b/vendor/predis/predis/examples/replication_simple.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require __DIR__.'/shared.php'; + +// Predis supports master / slave replication scenarios where write operations +// are performed on the master server and read operations are executed against +// one of the slaves. The behavior of commands or EVAL scripts can be customized +// at will. As soon as a write operation is performed the client switches to the +// master server for all the subsequent requests (either reads and writes). +// +// This example must be executed using the second Redis server configured as the +// slave of the first one (see the "SLAVEOF" command). +// + +$parameters = array( + 'tcp://127.0.0.1:6379?database=15&alias=master', + 'tcp://127.0.0.1:6380?database=15&alias=slave', +); + +$options = array('replication' => true); + +$client = new Predis\Client($parameters, $options); + +// Read operation. +$exists = $client->exists('foo') ? 'yes' : 'no'; +$current = $client->getConnection()->getCurrent()->getParameters(); +echo "Does 'foo' exist on {$current->alias}? $exists.", PHP_EOL; + +// Write operation. +$client->set('foo', 'bar'); +$current = $client->getConnection()->getCurrent()->getParameters(); +echo "Now 'foo' has been set to 'bar' on {$current->alias}!", PHP_EOL; + +// Read operation. +$bar = $client->get('foo'); +$current = $client->getConnection()->getCurrent()->getParameters(); +echo "We fetched 'foo' from {$current->alias} and its value is '$bar'.", PHP_EOL; + +/* OUTPUT: +Does 'foo' exist on slave? yes. +Now 'foo' has been set to 'bar' on master! +We fetched 'foo' from master and its value is 'bar'. +*/ diff --git a/vendor/predis/predis/examples/session_handler.php b/vendor/predis/predis/examples/session_handler.php new file mode 100644 index 000000000..4868a4df1 --- /dev/null +++ b/vendor/predis/predis/examples/session_handler.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require __DIR__.'/shared.php'; + +// This example demonstrates how to use Predis to save PHP sessions on Redis. +// +// The value of `session.gc_maxlifetime` in `php.ini` will be used by default as +// the TTL for keys holding session data but this value can be overridden when +// creating the session handler instance using the `gc_maxlifetime` option. +// +// NOTE: this class requires PHP >= 5.4 but can be used on PHP 5.3 if a polyfill +// for SessionHandlerInterface is provided either by you or an external package +// like `symfony/http-foundation`. +// +// See http://www.php.net/class.sessionhandlerinterface.php for more details. +// + +if (!interface_exists('SessionHandlerInterface')) { + die('ATTENTION: the session handler implemented by Predis requires PHP >= 5.4.0 '. + "or a polyfill for SessionHandlerInterface provided by an external package.\n"); +} + +// Instantiate a new client just like you would normally do. Using a prefix for +// keys will effectively prefix all session keys with the specified string. +$client = new Predis\Client($single_server, array('prefix' => 'sessions:')); + +// Set `gc_maxlifetime` to specify a time-to-live of 5 seconds for session keys. +$handler = new Predis\Session\Handler($client, array('gc_maxlifetime' => 5)); + +// Register the session handler. +$handler->register(); + +// We just set a fixed session ID only for the sake of our example. +session_id('example_session_id'); + +session_start(); + +if (isset($_SESSION['foo'])) { + echo "Session has `foo` set to {$_SESSION['foo']}", PHP_EOL; +} else { + $_SESSION['foo'] = $value = mt_rand(); + echo "Empty session, `foo` has been set with $value", PHP_EOL; +} diff --git a/vendor/predis/predis/examples/shared.php b/vendor/predis/predis/examples/shared.php new file mode 100644 index 000000000..80d6b4dd1 --- /dev/null +++ b/vendor/predis/predis/examples/shared.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (PHP_SAPI !== 'cli') { + die("Example scripts are meant to be executed locally via CLI."); +} + +require __DIR__.'/../autoload.php'; + +function redis_version($info) +{ + if (isset($info['Server']['redis_version'])) { + return $info['Server']['redis_version']; + } elseif (isset($info['redis_version'])) { + return $info['redis_version']; + } else { + return 'unknown version'; + } +} + +$single_server = array( + 'host' => '127.0.0.1', + 'port' => 6379, + 'database' => 15, +); + +$multiple_servers = array( + array( + 'host' => '127.0.0.1', + 'port' => 6379, + 'database' => 15, + 'alias' => 'first', + ), + array( + 'host' => '127.0.0.1', + 'port' => 6380, + 'database' => 15, + 'alias' => 'second', + ), +); diff --git a/vendor/predis/predis/examples/transaction_using_cas.php b/vendor/predis/predis/examples/transaction_using_cas.php new file mode 100644 index 000000000..f72c4655f --- /dev/null +++ b/vendor/predis/predis/examples/transaction_using_cas.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require __DIR__.'/shared.php'; + +// This is an implementation of an atomic client-side ZPOP using the support for +// check-and-set (CAS) operations with MULTI/EXEC transactions, as described in +// "WATCH explained" from http://redis.io/topics/transactions +// +// First, populate your database with a tiny sample data set: +// +// ./redis-cli +// SELECT 15 +// ZADD zset 1 a 2 b 3 c +// +// Then execute this script four times and see its output. +// + +function zpop($client, $key) +{ + $element = null; + $options = array( + 'cas' => true, // Initialize with support for CAS operations + 'watch' => $key, // Key that needs to be WATCHed to detect changes + 'retry' => 3, // Number of retries on aborted transactions, after + // which the client bails out with an exception. + ); + + $client->transaction($options, function ($tx) use ($key, &$element) { + @list($element) = $tx->zrange($key, 0, 0); + + if (isset($element)) { + $tx->multi(); // With CAS, MULTI *must* be explicitly invoked. + $tx->zrem($key, $element); + } + }); + + return $element; +} + +$client = new Predis\Client($single_server); +$zpopped = zpop($client, 'zset'); + +echo isset($zpopped) ? "ZPOPed $zpopped" : 'Nothing to ZPOP!', PHP_EOL; diff --git a/vendor/predis/predis/package.ini b/vendor/predis/predis/package.ini new file mode 100644 index 000000000..2c2bfe1fb --- /dev/null +++ b/vendor/predis/predis/package.ini @@ -0,0 +1,36 @@ +; This file is meant to be used with Onion http://c9s.github.com/Onion/ +; For instructions on how to build a PEAR package of Predis please follow +; the instructions at this URL: +; +; https://github.com/c9s/Onion#a-quick-tutorial-for-building-pear-package +; + +[package] +name = "Predis" +desc = "Flexible and feature-complete Redis client for PHP and HHVM" +homepage = "http://github.com/nrk/predis" +license = "MIT" +version = "1.1.6" +stability = "stable" +channel = "pear.nrk.io" + +author = "Daniele Alessandri \"nrk\" " + +[require] +php = ">= 5.3.9" +pearinstaller = "1.4.1" + +[roles] +*.xml.dist = test +*.md = doc +LICENSE = doc + +[optional phpiredis] +hint = "Add support for faster protocol handling with phpiredis" +extensions[] = socket +extensions[] = phpiredis + +[optional webdis] +hint = "Add support for Webdis" +extensions[] = curl +extensions[] = phpiredis diff --git a/vendor/predis/predis/src/Autoloader.php b/vendor/predis/predis/src/Autoloader.php new file mode 100644 index 000000000..17ec2ff1d --- /dev/null +++ b/vendor/predis/predis/src/Autoloader.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis; + +/** + * Implements a lightweight PSR-0 compliant autoloader for Predis. + * + * @author Eric Naeseth + * @author Daniele Alessandri + */ +class Autoloader +{ + private $directory; + private $prefix; + private $prefixLength; + + /** + * @param string $baseDirectory Base directory where the source files are located. + */ + public function __construct($baseDirectory = __DIR__) + { + $this->directory = $baseDirectory; + $this->prefix = __NAMESPACE__.'\\'; + $this->prefixLength = strlen($this->prefix); + } + + /** + * Registers the autoloader class with the PHP SPL autoloader. + * + * @param bool $prepend Prepend the autoloader on the stack instead of appending it. + */ + public static function register($prepend = false) + { + spl_autoload_register(array(new self(), 'autoload'), true, $prepend); + } + + /** + * Loads a class from a file using its fully qualified name. + * + * @param string $className Fully qualified name of a class. + */ + public function autoload($className) + { + if (0 === strpos($className, $this->prefix)) { + $parts = explode('\\', substr($className, $this->prefixLength)); + $filepath = $this->directory.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $parts).'.php'; + + if (is_file($filepath)) { + require $filepath; + } + } + } +} diff --git a/vendor/predis/predis/src/Client.php b/vendor/predis/predis/src/Client.php new file mode 100644 index 000000000..37715e9db --- /dev/null +++ b/vendor/predis/predis/src/Client.php @@ -0,0 +1,549 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis; + +use Predis\Command\CommandInterface; +use Predis\Command\RawCommand; +use Predis\Command\ScriptCommand; +use Predis\Configuration\Options; +use Predis\Configuration\OptionsInterface; +use Predis\Connection\AggregateConnectionInterface; +use Predis\Connection\ConnectionInterface; +use Predis\Connection\ParametersInterface; +use Predis\Monitor\Consumer as MonitorConsumer; +use Predis\Pipeline\Pipeline; +use Predis\PubSub\Consumer as PubSubConsumer; +use Predis\Response\ErrorInterface as ErrorResponseInterface; +use Predis\Response\ResponseInterface; +use Predis\Response\ServerException; +use Predis\Transaction\MultiExec as MultiExecTransaction; + +/** + * Client class used for connecting and executing commands on Redis. + * + * This is the main high-level abstraction of Predis upon which various other + * abstractions are built. Internally it aggregates various other classes each + * one with its own responsibility and scope. + * + * {@inheritdoc} + * + * @author Daniele Alessandri + */ +class Client implements ClientInterface, \IteratorAggregate +{ + const VERSION = '1.1.6'; + + protected $connection; + protected $options; + private $profile; + + /** + * @param mixed $parameters Connection parameters for one or more servers. + * @param mixed $options Options to configure some behaviours of the client. + */ + public function __construct($parameters = null, $options = null) + { + $this->options = $this->createOptions($options ?: array()); + $this->connection = $this->createConnection($parameters ?: array()); + $this->profile = $this->options->profile; + } + + /** + * Creates a new instance of Predis\Configuration\Options from different + * types of arguments or simply returns the passed argument if it is an + * instance of Predis\Configuration\OptionsInterface. + * + * @param mixed $options Client options. + * + * @throws \InvalidArgumentException + * + * @return OptionsInterface + */ + protected function createOptions($options) + { + if (is_array($options)) { + return new Options($options); + } + + if ($options instanceof OptionsInterface) { + return $options; + } + + throw new \InvalidArgumentException('Invalid type for client options.'); + } + + /** + * Creates single or aggregate connections from different types of arguments + * (string, array) or returns the passed argument if it is an instance of a + * class implementing Predis\Connection\ConnectionInterface. + * + * Accepted types for connection parameters are: + * + * - Instance of Predis\Connection\ConnectionInterface. + * - Instance of Predis\Connection\ParametersInterface. + * - Array + * - String + * - Callable + * + * @param mixed $parameters Connection parameters or connection instance. + * + * @throws \InvalidArgumentException + * + * @return ConnectionInterface + */ + protected function createConnection($parameters) + { + if ($parameters instanceof ConnectionInterface) { + return $parameters; + } + + if ($parameters instanceof ParametersInterface || is_string($parameters)) { + return $this->options->connections->create($parameters); + } + + if (is_array($parameters)) { + if (!isset($parameters[0])) { + return $this->options->connections->create($parameters); + } + + $options = $this->options; + + if ($options->defined('aggregate')) { + $initializer = $this->getConnectionInitializerWrapper($options->aggregate); + $connection = $initializer($parameters, $options); + } elseif ($options->defined('replication')) { + $replication = $options->replication; + + if ($replication instanceof AggregateConnectionInterface) { + $connection = $replication; + $options->connections->aggregate($connection, $parameters); + } else { + $initializer = $this->getConnectionInitializerWrapper($replication); + $connection = $initializer($parameters, $options); + } + } else { + $connection = $options->cluster; + $options->connections->aggregate($connection, $parameters); + } + + return $connection; + } + + if (is_callable($parameters)) { + $initializer = $this->getConnectionInitializerWrapper($parameters); + $connection = $initializer($this->options); + + return $connection; + } + + throw new \InvalidArgumentException('Invalid type for connection parameters.'); + } + + /** + * Wraps a callable to make sure that its returned value represents a valid + * connection type. + * + * @param mixed $callable + * + * @return \Closure + */ + protected function getConnectionInitializerWrapper($callable) + { + return function () use ($callable) { + $connection = call_user_func_array($callable, func_get_args()); + + if (!$connection instanceof ConnectionInterface) { + throw new \UnexpectedValueException( + 'The callable connection initializer returned an invalid type.' + ); + } + + return $connection; + }; + } + + /** + * {@inheritdoc} + */ + public function getProfile() + { + return $this->profile; + } + + /** + * {@inheritdoc} + */ + public function getOptions() + { + return $this->options; + } + + /** + * Creates a new client instance for the specified connection ID or alias, + * only when working with an aggregate connection (cluster, replication). + * The new client instances uses the same options of the original one. + * + * @param string $connectionID Identifier of a connection. + * + * @throws \InvalidArgumentException + * + * @return Client + */ + public function getClientFor($connectionID) + { + if (!$connection = $this->getConnectionById($connectionID)) { + throw new \InvalidArgumentException("Invalid connection ID: $connectionID."); + } + + return new static($connection, $this->options); + } + + /** + * Opens the underlying connection and connects to the server. + */ + public function connect() + { + $this->connection->connect(); + } + + /** + * Closes the underlying connection and disconnects from the server. + */ + public function disconnect() + { + $this->connection->disconnect(); + } + + /** + * Closes the underlying connection and disconnects from the server. + * + * This is the same as `Client::disconnect()` as it does not actually send + * the `QUIT` command to Redis, but simply closes the connection. + */ + public function quit() + { + $this->disconnect(); + } + + /** + * Returns the current state of the underlying connection. + * + * @return bool + */ + public function isConnected() + { + return $this->connection->isConnected(); + } + + /** + * {@inheritdoc} + */ + public function getConnection() + { + return $this->connection; + } + + /** + * Retrieves the specified connection from the aggregate connection when the + * client is in cluster or replication mode. + * + * @param string $connectionID Index or alias of the single connection. + * + * @throws NotSupportedException + * + * @return Connection\NodeConnectionInterface + */ + public function getConnectionById($connectionID) + { + if (!$this->connection instanceof AggregateConnectionInterface) { + throw new NotSupportedException( + 'Retrieving connections by ID is supported only by aggregate connections.' + ); + } + + return $this->connection->getConnectionById($connectionID); + } + + /** + * Executes a command without filtering its arguments, parsing the response, + * applying any prefix to keys or throwing exceptions on Redis errors even + * regardless of client options. + * + * It is possible to identify Redis error responses from normal responses + * using the second optional argument which is populated by reference. + * + * @param array $arguments Command arguments as defined by the command signature. + * @param bool $error Set to TRUE when Redis returned an error response. + * + * @return mixed + */ + public function executeRaw(array $arguments, &$error = null) + { + $error = false; + + $response = $this->connection->executeCommand( + new RawCommand($arguments) + ); + + if ($response instanceof ResponseInterface) { + if ($response instanceof ErrorResponseInterface) { + $error = true; + } + + return (string) $response; + } + + return $response; + } + + /** + * {@inheritdoc} + */ + public function __call($commandID, $arguments) + { + return $this->executeCommand( + $this->createCommand($commandID, $arguments) + ); + } + + /** + * {@inheritdoc} + */ + public function createCommand($commandID, $arguments = array()) + { + return $this->profile->createCommand($commandID, $arguments); + } + + /** + * {@inheritdoc} + */ + public function executeCommand(CommandInterface $command) + { + $response = $this->connection->executeCommand($command); + + if ($response instanceof ResponseInterface) { + if ($response instanceof ErrorResponseInterface) { + $response = $this->onErrorResponse($command, $response); + } + + return $response; + } + + return $command->parseResponse($response); + } + + /** + * Handles -ERR responses returned by Redis. + * + * @param CommandInterface $command Redis command that generated the error. + * @param ErrorResponseInterface $response Instance of the error response. + * + * @throws ServerException + * + * @return mixed + */ + protected function onErrorResponse(CommandInterface $command, ErrorResponseInterface $response) + { + if ($command instanceof ScriptCommand && $response->getErrorType() === 'NOSCRIPT') { + $eval = $this->createCommand('EVAL'); + $eval->setRawArguments($command->getEvalArguments()); + + $response = $this->executeCommand($eval); + + if (!$response instanceof ResponseInterface) { + $response = $command->parseResponse($response); + } + + return $response; + } + + if ($this->options->exceptions) { + throw new ServerException($response->getMessage()); + } + + return $response; + } + + /** + * Executes the specified initializer method on `$this` by adjusting the + * actual invokation depending on the arity (0, 1 or 2 arguments). This is + * simply an utility method to create Redis contexts instances since they + * follow a common initialization path. + * + * @param string $initializer Method name. + * @param array $argv Arguments for the method. + * + * @return mixed + */ + private function sharedContextFactory($initializer, $argv = null) + { + switch (count($argv)) { + case 0: + return $this->$initializer(); + + case 1: + return is_array($argv[0]) + ? $this->$initializer($argv[0]) + : $this->$initializer(null, $argv[0]); + + case 2: + list($arg0, $arg1) = $argv; + + return $this->$initializer($arg0, $arg1); + + default: + return $this->$initializer($this, $argv); + } + } + + /** + * Creates a new pipeline context and returns it, or returns the results of + * a pipeline executed inside the optionally provided callable object. + * + * @param mixed ... Array of options, a callable for execution, or both. + * + * @return Pipeline|array + */ + public function pipeline(/* arguments */) + { + return $this->sharedContextFactory('createPipeline', func_get_args()); + } + + /** + * Actual pipeline context initializer method. + * + * @param array $options Options for the context. + * @param mixed $callable Optional callable used to execute the context. + * + * @return Pipeline|array + */ + protected function createPipeline(array $options = null, $callable = null) + { + if (isset($options['atomic']) && $options['atomic']) { + $class = 'Predis\Pipeline\Atomic'; + } elseif (isset($options['fire-and-forget']) && $options['fire-and-forget']) { + $class = 'Predis\Pipeline\FireAndForget'; + } else { + $class = 'Predis\Pipeline\Pipeline'; + } + + /* + * @var ClientContextInterface + */ + $pipeline = new $class($this); + + if (isset($callable)) { + return $pipeline->execute($callable); + } + + return $pipeline; + } + + /** + * Creates a new transaction context and returns it, or returns the results + * of a transaction executed inside the optionally provided callable object. + * + * @param mixed ... Array of options, a callable for execution, or both. + * + * @return MultiExecTransaction|array + */ + public function transaction(/* arguments */) + { + return $this->sharedContextFactory('createTransaction', func_get_args()); + } + + /** + * Actual transaction context initializer method. + * + * @param array $options Options for the context. + * @param mixed $callable Optional callable used to execute the context. + * + * @return MultiExecTransaction|array + */ + protected function createTransaction(array $options = null, $callable = null) + { + $transaction = new MultiExecTransaction($this, $options); + + if (isset($callable)) { + return $transaction->execute($callable); + } + + return $transaction; + } + + /** + * Creates a new publish/subscribe context and returns it, or starts its loop + * inside the optionally provided callable object. + * + * @param mixed ... Array of options, a callable for execution, or both. + * + * @return PubSubConsumer|null + */ + public function pubSubLoop(/* arguments */) + { + return $this->sharedContextFactory('createPubSub', func_get_args()); + } + + /** + * Actual publish/subscribe context initializer method. + * + * @param array $options Options for the context. + * @param mixed $callable Optional callable used to execute the context. + * + * @return PubSubConsumer|null + */ + protected function createPubSub(array $options = null, $callable = null) + { + $pubsub = new PubSubConsumer($this, $options); + + if (!isset($callable)) { + return $pubsub; + } + + foreach ($pubsub as $message) { + if (call_user_func($callable, $pubsub, $message) === false) { + $pubsub->stop(); + } + } + } + + /** + * Creates a new monitor consumer and returns it. + * + * @return MonitorConsumer + */ + public function monitor() + { + return new MonitorConsumer($this); + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $clients = array(); + $connection = $this->getConnection(); + + if (!$connection instanceof \Traversable) { + return new \ArrayIterator(array( + (string) $connection => new static($connection, $this->getOptions()) + )); + } + + foreach ($connection as $node) { + $clients[(string) $node] = new static($node, $this->getOptions()); + } + + return new \ArrayIterator($clients); + } +} diff --git a/vendor/predis/predis/src/ClientContextInterface.php b/vendor/predis/predis/src/ClientContextInterface.php new file mode 100644 index 000000000..85e4edcd4 --- /dev/null +++ b/vendor/predis/predis/src/ClientContextInterface.php @@ -0,0 +1,199 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis; + +use Predis\Command\CommandInterface; + +/** + * Interface defining a client-side context such as a pipeline or transaction. + * + * @method $this del(array|string $keys) + * @method $this dump($key) + * @method $this exists($key) + * @method $this expire($key, $seconds) + * @method $this expireat($key, $timestamp) + * @method $this keys($pattern) + * @method $this move($key, $db) + * @method $this object($subcommand, $key) + * @method $this persist($key) + * @method $this pexpire($key, $milliseconds) + * @method $this pexpireat($key, $timestamp) + * @method $this pttl($key) + * @method $this randomkey() + * @method $this rename($key, $target) + * @method $this renamenx($key, $target) + * @method $this scan($cursor, array $options = null) + * @method $this sort($key, array $options = null) + * @method $this ttl($key) + * @method $this type($key) + * @method $this append($key, $value) + * @method $this bitcount($key, $start = null, $end = null) + * @method $this bitop($operation, $destkey, $key) + * @method $this bitfield($key, $subcommand, ...$subcommandArg) + * @method $this bitpos($key, $bit, $start = null, $end = null) + * @method $this decr($key) + * @method $this decrby($key, $decrement) + * @method $this get($key) + * @method $this getbit($key, $offset) + * @method $this getrange($key, $start, $end) + * @method $this getset($key, $value) + * @method $this incr($key) + * @method $this incrby($key, $increment) + * @method $this incrbyfloat($key, $increment) + * @method $this mget(array $keys) + * @method $this mset(array $dictionary) + * @method $this msetnx(array $dictionary) + * @method $this psetex($key, $milliseconds, $value) + * @method $this set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null) + * @method $this setbit($key, $offset, $value) + * @method $this setex($key, $seconds, $value) + * @method $this setnx($key, $value) + * @method $this setrange($key, $offset, $value) + * @method $this strlen($key) + * @method $this hdel($key, array $fields) + * @method $this hexists($key, $field) + * @method $this hget($key, $field) + * @method $this hgetall($key) + * @method $this hincrby($key, $field, $increment) + * @method $this hincrbyfloat($key, $field, $increment) + * @method $this hkeys($key) + * @method $this hlen($key) + * @method $this hmget($key, array $fields) + * @method $this hmset($key, array $dictionary) + * @method $this hscan($key, $cursor, array $options = null) + * @method $this hset($key, $field, $value) + * @method $this hsetnx($key, $field, $value) + * @method $this hvals($key) + * @method $this hstrlen($key, $field) + * @method $this blpop(array|string $keys, $timeout) + * @method $this brpop(array|string $keys, $timeout) + * @method $this brpoplpush($source, $destination, $timeout) + * @method $this lindex($key, $index) + * @method $this linsert($key, $whence, $pivot, $value) + * @method $this llen($key) + * @method $this lpop($key) + * @method $this lpush($key, array $values) + * @method $this lpushx($key, array $values) + * @method $this lrange($key, $start, $stop) + * @method $this lrem($key, $count, $value) + * @method $this lset($key, $index, $value) + * @method $this ltrim($key, $start, $stop) + * @method $this rpop($key) + * @method $this rpoplpush($source, $destination) + * @method $this rpush($key, array $values) + * @method $this rpushx($key, array $values) + * @method $this sadd($key, array $members) + * @method $this scard($key) + * @method $this sdiff(array|string $keys) + * @method $this sdiffstore($destination, array|string $keys) + * @method $this sinter(array|string $keys) + * @method $this sinterstore($destination, array|string $keys) + * @method $this sismember($key, $member) + * @method $this smembers($key) + * @method $this smove($source, $destination, $member) + * @method $this spop($key, $count = null) + * @method $this srandmember($key, $count = null) + * @method $this srem($key, $member) + * @method $this sscan($key, $cursor, array $options = null) + * @method $this sunion(array|string $keys) + * @method $this sunionstore($destination, array|string $keys) + * @method $this zadd($key, array $membersAndScoresDictionary) + * @method $this zcard($key) + * @method $this zcount($key, $min, $max) + * @method $this zincrby($key, $increment, $member) + * @method $this zinterstore($destination, array|string $keys, array $options = null) + * @method $this zrange($key, $start, $stop, array $options = null) + * @method $this zrangebyscore($key, $min, $max, array $options = null) + * @method $this zrank($key, $member) + * @method $this zrem($key, $member) + * @method $this zremrangebyrank($key, $start, $stop) + * @method $this zremrangebyscore($key, $min, $max) + * @method $this zrevrange($key, $start, $stop, array $options = null) + * @method $this zrevrangebyscore($key, $max, $min, array $options = null) + * @method $this zrevrank($key, $member) + * @method $this zunionstore($destination, array|string $keys, array $options = null) + * @method $this zscore($key, $member) + * @method $this zscan($key, $cursor, array $options = null) + * @method $this zrangebylex($key, $start, $stop, array $options = null) + * @method $this zrevrangebylex($key, $start, $stop, array $options = null) + * @method $this zremrangebylex($key, $min, $max) + * @method $this zlexcount($key, $min, $max) + * @method $this pfadd($key, array $elements) + * @method $this pfmerge($destinationKey, array|string $sourceKeys) + * @method $this pfcount(array|string $keys) + * @method $this pubsub($subcommand, $argument) + * @method $this publish($channel, $message) + * @method $this discard() + * @method $this exec() + * @method $this multi() + * @method $this unwatch() + * @method $this watch($key) + * @method $this eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null) + * @method $this evalsha($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null) + * @method $this script($subcommand, $argument = null) + * @method $this auth($password) + * @method $this echo($message) + * @method $this ping($message = null) + * @method $this select($database) + * @method $this bgrewriteaof() + * @method $this bgsave() + * @method $this client($subcommand, $argument = null) + * @method $this config($subcommand, $argument = null) + * @method $this dbsize() + * @method $this flushall() + * @method $this flushdb() + * @method $this info($section = null) + * @method $this lastsave() + * @method $this save() + * @method $this slaveof($host, $port) + * @method $this slowlog($subcommand, $argument = null) + * @method $this time() + * @method $this command() + * @method $this geoadd($key, $longitude, $latitude, $member) + * @method $this geohash($key, array $members) + * @method $this geopos($key, array $members) + * @method $this geodist($key, $member1, $member2, $unit = null) + * @method $this georadius($key, $longitude, $latitude, $radius, $unit, array $options = null) + * @method $this georadiusbymember($key, $member, $radius, $unit, array $options = null) + * + * @author Daniele Alessandri + */ +interface ClientContextInterface +{ + /** + * Sends the specified command instance to Redis. + * + * @param CommandInterface $command Command instance. + * + * @return mixed + */ + public function executeCommand(CommandInterface $command); + + /** + * Sends the specified command with its arguments to Redis. + * + * @param string $method Command ID. + * @param array $arguments Arguments for the command. + * + * @return mixed + */ + public function __call($method, $arguments); + + /** + * Starts the execution of the context. + * + * @param mixed $callable Optional callback for execution. + * + * @return array + */ + public function execute($callable = null); +} diff --git a/vendor/predis/predis/src/ClientException.php b/vendor/predis/predis/src/ClientException.php new file mode 100644 index 000000000..6c07aaf0d --- /dev/null +++ b/vendor/predis/predis/src/ClientException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis; + +/** + * Exception class that identifies client-side errors. + * + * @author Daniele Alessandri + */ +class ClientException extends PredisException +{ +} diff --git a/vendor/predis/predis/src/ClientInterface.php b/vendor/predis/predis/src/ClientInterface.php new file mode 100644 index 000000000..f61dcf63b --- /dev/null +++ b/vendor/predis/predis/src/ClientInterface.php @@ -0,0 +1,240 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis; + +use Predis\Command\CommandInterface; +use Predis\Configuration\OptionsInterface; +use Predis\Connection\ConnectionInterface; +use Predis\Profile\ProfileInterface; + +/** + * Interface defining a client able to execute commands against Redis. + * + * All the commands exposed by the client generally have the same signature as + * described by the Redis documentation, but some of them offer an additional + * and more friendly interface to ease programming which is described in the + * following list of methods: + * + * @method int del(array|string $keys) + * @method string|null dump($key) + * @method int exists($key) + * @method int expire($key, $seconds) + * @method int expireat($key, $timestamp) + * @method array keys($pattern) + * @method int move($key, $db) + * @method mixed object($subcommand, $key) + * @method int persist($key) + * @method int pexpire($key, $milliseconds) + * @method int pexpireat($key, $timestamp) + * @method int pttl($key) + * @method string|null randomkey() + * @method mixed rename($key, $target) + * @method int renamenx($key, $target) + * @method array scan($cursor, array $options = null) + * @method array sort($key, array $options = null) + * @method int ttl($key) + * @method mixed type($key) + * @method int append($key, $value) + * @method int bitcount($key, $start = null, $end = null) + * @method int bitop($operation, $destkey, $key) + * @method array|null bitfield($key, $subcommand, ...$subcommandArg) + * @method int bitpos($key, $bit, $start = null, $end = null) + * @method int decr($key) + * @method int decrby($key, $decrement) + * @method string|null get($key) + * @method int getbit($key, $offset) + * @method string getrange($key, $start, $end) + * @method string|null getset($key, $value) + * @method int incr($key) + * @method int incrby($key, $increment) + * @method string incrbyfloat($key, $increment) + * @method array mget(array $keys) + * @method mixed mset(array $dictionary) + * @method int msetnx(array $dictionary) + * @method mixed psetex($key, $milliseconds, $value) + * @method mixed set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null) + * @method int setbit($key, $offset, $value) + * @method int setex($key, $seconds, $value) + * @method int setnx($key, $value) + * @method int setrange($key, $offset, $value) + * @method int strlen($key) + * @method int hdel($key, array $fields) + * @method int hexists($key, $field) + * @method string|null hget($key, $field) + * @method array hgetall($key) + * @method int hincrby($key, $field, $increment) + * @method string hincrbyfloat($key, $field, $increment) + * @method array hkeys($key) + * @method int hlen($key) + * @method array hmget($key, array $fields) + * @method mixed hmset($key, array $dictionary) + * @method array hscan($key, $cursor, array $options = null) + * @method int hset($key, $field, $value) + * @method int hsetnx($key, $field, $value) + * @method array hvals($key) + * @method int hstrlen($key, $field) + * @method array|null blpop(array|string $keys, $timeout) + * @method array|null brpop(array|string $keys, $timeout) + * @method string|null brpoplpush($source, $destination, $timeout) + * @method string|null lindex($key, $index) + * @method int linsert($key, $whence, $pivot, $value) + * @method int llen($key) + * @method string|null lpop($key) + * @method int lpush($key, array $values) + * @method int lpushx($key, array $values) + * @method array lrange($key, $start, $stop) + * @method int lrem($key, $count, $value) + * @method mixed lset($key, $index, $value) + * @method mixed ltrim($key, $start, $stop) + * @method string|null rpop($key) + * @method string|null rpoplpush($source, $destination) + * @method int rpush($key, array $values) + * @method int rpushx($key, array $values) + * @method int sadd($key, array $members) + * @method int scard($key) + * @method array sdiff(array|string $keys) + * @method int sdiffstore($destination, array|string $keys) + * @method array sinter(array|string $keys) + * @method int sinterstore($destination, array|string $keys) + * @method int sismember($key, $member) + * @method array smembers($key) + * @method int smove($source, $destination, $member) + * @method string|null spop($key, $count = null) + * @method string|null srandmember($key, $count = null) + * @method int srem($key, $member) + * @method array sscan($key, $cursor, array $options = null) + * @method array sunion(array|string $keys) + * @method int sunionstore($destination, array|string $keys) + * @method int zadd($key, array $membersAndScoresDictionary) + * @method int zcard($key) + * @method string zcount($key, $min, $max) + * @method string zincrby($key, $increment, $member) + * @method int zinterstore($destination, array|string $keys, array $options = null) + * @method array zrange($key, $start, $stop, array $options = null) + * @method array zrangebyscore($key, $min, $max, array $options = null) + * @method int|null zrank($key, $member) + * @method int zrem($key, $member) + * @method int zremrangebyrank($key, $start, $stop) + * @method int zremrangebyscore($key, $min, $max) + * @method array zrevrange($key, $start, $stop, array $options = null) + * @method array zrevrangebyscore($key, $max, $min, array $options = null) + * @method int|null zrevrank($key, $member) + * @method int zunionstore($destination, array|string $keys, array $options = null) + * @method string|null zscore($key, $member) + * @method array zscan($key, $cursor, array $options = null) + * @method array zrangebylex($key, $start, $stop, array $options = null) + * @method array zrevrangebylex($key, $start, $stop, array $options = null) + * @method int zremrangebylex($key, $min, $max) + * @method int zlexcount($key, $min, $max) + * @method int pfadd($key, array $elements) + * @method mixed pfmerge($destinationKey, array|string $sourceKeys) + * @method int pfcount(array|string $keys) + * @method mixed pubsub($subcommand, $argument) + * @method int publish($channel, $message) + * @method mixed discard() + * @method array|null exec() + * @method mixed multi() + * @method mixed unwatch() + * @method mixed watch($key) + * @method mixed eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null) + * @method mixed evalsha($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null) + * @method mixed script($subcommand, $argument = null) + * @method mixed auth($password) + * @method string echo($message) + * @method mixed ping($message = null) + * @method mixed select($database) + * @method mixed bgrewriteaof() + * @method mixed bgsave() + * @method mixed client($subcommand, $argument = null) + * @method mixed config($subcommand, $argument = null) + * @method int dbsize() + * @method mixed flushall() + * @method mixed flushdb() + * @method array info($section = null) + * @method int lastsave() + * @method mixed save() + * @method mixed slaveof($host, $port) + * @method mixed slowlog($subcommand, $argument = null) + * @method array time() + * @method array command() + * @method int geoadd($key, $longitude, $latitude, $member) + * @method array geohash($key, array $members) + * @method array geopos($key, array $members) + * @method string|null geodist($key, $member1, $member2, $unit = null) + * @method array georadius($key, $longitude, $latitude, $radius, $unit, array $options = null) + * @method array georadiusbymember($key, $member, $radius, $unit, array $options = null) + * + * @author Daniele Alessandri + */ +interface ClientInterface +{ + /** + * Returns the server profile used by the client. + * + * @return ProfileInterface + */ + public function getProfile(); + + /** + * Returns the client options specified upon initialization. + * + * @return OptionsInterface + */ + public function getOptions(); + + /** + * Opens the underlying connection to the server. + */ + public function connect(); + + /** + * Closes the underlying connection from the server. + */ + public function disconnect(); + + /** + * Returns the underlying connection instance. + * + * @return ConnectionInterface + */ + public function getConnection(); + + /** + * Creates a new instance of the specified Redis command. + * + * @param string $method Command ID. + * @param array $arguments Arguments for the command. + * + * @return CommandInterface + */ + public function createCommand($method, $arguments = array()); + + /** + * Executes the specified Redis command. + * + * @param CommandInterface $command Command instance. + * + * @return mixed + */ + public function executeCommand(CommandInterface $command); + + /** + * Creates a Redis command with the specified arguments and sends a request + * to the server. + * + * @param string $method Command ID. + * @param array $arguments Arguments for the command. + * + * @return mixed + */ + public function __call($method, $arguments); +} diff --git a/vendor/predis/predis/src/Cluster/ClusterStrategy.php b/vendor/predis/predis/src/Cluster/ClusterStrategy.php new file mode 100644 index 000000000..1891907f7 --- /dev/null +++ b/vendor/predis/predis/src/Cluster/ClusterStrategy.php @@ -0,0 +1,469 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Cluster; + +use Predis\Command\CommandInterface; +use Predis\Command\ScriptCommand; + +/** + * Common class implementing the logic needed to support clustering strategies. + * + * @author Daniele Alessandri + */ +abstract class ClusterStrategy implements StrategyInterface +{ + protected $commands; + + /** + * + */ + public function __construct() + { + $this->commands = $this->getDefaultCommands(); + } + + /** + * Returns the default map of supported commands with their handlers. + * + * @return array + */ + protected function getDefaultCommands() + { + $getKeyFromFirstArgument = array($this, 'getKeyFromFirstArgument'); + $getKeyFromAllArguments = array($this, 'getKeyFromAllArguments'); + + return array( + /* commands operating on the key space */ + 'EXISTS' => $getKeyFromAllArguments, + 'DEL' => $getKeyFromAllArguments, + 'TYPE' => $getKeyFromFirstArgument, + 'EXPIRE' => $getKeyFromFirstArgument, + 'EXPIREAT' => $getKeyFromFirstArgument, + 'PERSIST' => $getKeyFromFirstArgument, + 'PEXPIRE' => $getKeyFromFirstArgument, + 'PEXPIREAT' => $getKeyFromFirstArgument, + 'TTL' => $getKeyFromFirstArgument, + 'PTTL' => $getKeyFromFirstArgument, + 'SORT' => array($this, 'getKeyFromSortCommand'), + 'DUMP' => $getKeyFromFirstArgument, + 'RESTORE' => $getKeyFromFirstArgument, + + /* commands operating on string values */ + 'APPEND' => $getKeyFromFirstArgument, + 'DECR' => $getKeyFromFirstArgument, + 'DECRBY' => $getKeyFromFirstArgument, + 'GET' => $getKeyFromFirstArgument, + 'GETBIT' => $getKeyFromFirstArgument, + 'MGET' => $getKeyFromAllArguments, + 'SET' => $getKeyFromFirstArgument, + 'GETRANGE' => $getKeyFromFirstArgument, + 'GETSET' => $getKeyFromFirstArgument, + 'INCR' => $getKeyFromFirstArgument, + 'INCRBY' => $getKeyFromFirstArgument, + 'INCRBYFLOAT' => $getKeyFromFirstArgument, + 'SETBIT' => $getKeyFromFirstArgument, + 'SETEX' => $getKeyFromFirstArgument, + 'MSET' => array($this, 'getKeyFromInterleavedArguments'), + 'MSETNX' => array($this, 'getKeyFromInterleavedArguments'), + 'SETNX' => $getKeyFromFirstArgument, + 'SETRANGE' => $getKeyFromFirstArgument, + 'STRLEN' => $getKeyFromFirstArgument, + 'SUBSTR' => $getKeyFromFirstArgument, + 'BITOP' => array($this, 'getKeyFromBitOp'), + 'BITCOUNT' => $getKeyFromFirstArgument, + 'BITFIELD' => $getKeyFromFirstArgument, + + /* commands operating on lists */ + 'LINSERT' => $getKeyFromFirstArgument, + 'LINDEX' => $getKeyFromFirstArgument, + 'LLEN' => $getKeyFromFirstArgument, + 'LPOP' => $getKeyFromFirstArgument, + 'RPOP' => $getKeyFromFirstArgument, + 'RPOPLPUSH' => $getKeyFromAllArguments, + 'BLPOP' => array($this, 'getKeyFromBlockingListCommands'), + 'BRPOP' => array($this, 'getKeyFromBlockingListCommands'), + 'BRPOPLPUSH' => array($this, 'getKeyFromBlockingListCommands'), + 'LPUSH' => $getKeyFromFirstArgument, + 'LPUSHX' => $getKeyFromFirstArgument, + 'RPUSH' => $getKeyFromFirstArgument, + 'RPUSHX' => $getKeyFromFirstArgument, + 'LRANGE' => $getKeyFromFirstArgument, + 'LREM' => $getKeyFromFirstArgument, + 'LSET' => $getKeyFromFirstArgument, + 'LTRIM' => $getKeyFromFirstArgument, + + /* commands operating on sets */ + 'SADD' => $getKeyFromFirstArgument, + 'SCARD' => $getKeyFromFirstArgument, + 'SDIFF' => $getKeyFromAllArguments, + 'SDIFFSTORE' => $getKeyFromAllArguments, + 'SINTER' => $getKeyFromAllArguments, + 'SINTERSTORE' => $getKeyFromAllArguments, + 'SUNION' => $getKeyFromAllArguments, + 'SUNIONSTORE' => $getKeyFromAllArguments, + 'SISMEMBER' => $getKeyFromFirstArgument, + 'SMEMBERS' => $getKeyFromFirstArgument, + 'SSCAN' => $getKeyFromFirstArgument, + 'SPOP' => $getKeyFromFirstArgument, + 'SRANDMEMBER' => $getKeyFromFirstArgument, + 'SREM' => $getKeyFromFirstArgument, + + /* commands operating on sorted sets */ + 'ZADD' => $getKeyFromFirstArgument, + 'ZCARD' => $getKeyFromFirstArgument, + 'ZCOUNT' => $getKeyFromFirstArgument, + 'ZINCRBY' => $getKeyFromFirstArgument, + 'ZINTERSTORE' => array($this, 'getKeyFromZsetAggregationCommands'), + 'ZRANGE' => $getKeyFromFirstArgument, + 'ZRANGEBYSCORE' => $getKeyFromFirstArgument, + 'ZRANK' => $getKeyFromFirstArgument, + 'ZREM' => $getKeyFromFirstArgument, + 'ZREMRANGEBYRANK' => $getKeyFromFirstArgument, + 'ZREMRANGEBYSCORE' => $getKeyFromFirstArgument, + 'ZREVRANGE' => $getKeyFromFirstArgument, + 'ZREVRANGEBYSCORE' => $getKeyFromFirstArgument, + 'ZREVRANK' => $getKeyFromFirstArgument, + 'ZSCORE' => $getKeyFromFirstArgument, + 'ZUNIONSTORE' => array($this, 'getKeyFromZsetAggregationCommands'), + 'ZSCAN' => $getKeyFromFirstArgument, + 'ZLEXCOUNT' => $getKeyFromFirstArgument, + 'ZRANGEBYLEX' => $getKeyFromFirstArgument, + 'ZREMRANGEBYLEX' => $getKeyFromFirstArgument, + 'ZREVRANGEBYLEX' => $getKeyFromFirstArgument, + + /* commands operating on hashes */ + 'HDEL' => $getKeyFromFirstArgument, + 'HEXISTS' => $getKeyFromFirstArgument, + 'HGET' => $getKeyFromFirstArgument, + 'HGETALL' => $getKeyFromFirstArgument, + 'HMGET' => $getKeyFromFirstArgument, + 'HMSET' => $getKeyFromFirstArgument, + 'HINCRBY' => $getKeyFromFirstArgument, + 'HINCRBYFLOAT' => $getKeyFromFirstArgument, + 'HKEYS' => $getKeyFromFirstArgument, + 'HLEN' => $getKeyFromFirstArgument, + 'HSET' => $getKeyFromFirstArgument, + 'HSETNX' => $getKeyFromFirstArgument, + 'HVALS' => $getKeyFromFirstArgument, + 'HSCAN' => $getKeyFromFirstArgument, + 'HSTRLEN' => $getKeyFromFirstArgument, + + /* commands operating on HyperLogLog */ + 'PFADD' => $getKeyFromFirstArgument, + 'PFCOUNT' => $getKeyFromAllArguments, + 'PFMERGE' => $getKeyFromAllArguments, + + /* scripting */ + 'EVAL' => array($this, 'getKeyFromScriptingCommands'), + 'EVALSHA' => array($this, 'getKeyFromScriptingCommands'), + + /* commands performing geospatial operations */ + 'GEOADD' => $getKeyFromFirstArgument, + 'GEOHASH' => $getKeyFromFirstArgument, + 'GEOPOS' => $getKeyFromFirstArgument, + 'GEODIST' => $getKeyFromFirstArgument, + 'GEORADIUS' => array($this, 'getKeyFromGeoradiusCommands'), + 'GEORADIUSBYMEMBER' => array($this, 'getKeyFromGeoradiusCommands'), + ); + } + + /** + * Returns the list of IDs for the supported commands. + * + * @return array + */ + public function getSupportedCommands() + { + return array_keys($this->commands); + } + + /** + * Sets an handler for the specified command ID. + * + * The signature of the callback must have a single parameter of type + * Predis\Command\CommandInterface. + * + * When the callback argument is omitted or NULL, the previously associated + * handler for the specified command ID is removed. + * + * @param string $commandID Command ID. + * @param mixed $callback A valid callable object, or NULL to unset the handler. + * + * @throws \InvalidArgumentException + */ + public function setCommandHandler($commandID, $callback = null) + { + $commandID = strtoupper($commandID); + + if (!isset($callback)) { + unset($this->commands[$commandID]); + + return; + } + + if (!is_callable($callback)) { + throw new \InvalidArgumentException( + 'The argument must be a callable object or NULL.' + ); + } + + $this->commands[$commandID] = $callback; + } + + /** + * Extracts the key from the first argument of a command instance. + * + * @param CommandInterface $command Command instance. + * + * @return string + */ + protected function getKeyFromFirstArgument(CommandInterface $command) + { + return $command->getArgument(0); + } + + /** + * Extracts the key from a command with multiple keys only when all keys in + * the arguments array produce the same hash. + * + * @param CommandInterface $command Command instance. + * + * @return string|null + */ + protected function getKeyFromAllArguments(CommandInterface $command) + { + $arguments = $command->getArguments(); + + if ($this->checkSameSlotForKeys($arguments)) { + return $arguments[0]; + } + } + + /** + * Extracts the key from a command with multiple keys only when all keys in + * the arguments array produce the same hash. + * + * @param CommandInterface $command Command instance. + * + * @return string|null + */ + protected function getKeyFromInterleavedArguments(CommandInterface $command) + { + $arguments = $command->getArguments(); + $keys = array(); + + for ($i = 0; $i < count($arguments); $i += 2) { + $keys[] = $arguments[$i]; + } + + if ($this->checkSameSlotForKeys($keys)) { + return $arguments[0]; + } + } + + /** + * Extracts the key from SORT command. + * + * @param CommandInterface $command Command instance. + * + * @return string|null + */ + protected function getKeyFromSortCommand(CommandInterface $command) + { + $arguments = $command->getArguments(); + $firstKey = $arguments[0]; + + if (1 === $argc = count($arguments)) { + return $firstKey; + } + + $keys = array($firstKey); + + for ($i = 1; $i < $argc; ++$i) { + if (strtoupper($arguments[$i]) === 'STORE') { + $keys[] = $arguments[++$i]; + } + } + + if ($this->checkSameSlotForKeys($keys)) { + return $firstKey; + } + } + + /** + * Extracts the key from BLPOP and BRPOP commands. + * + * @param CommandInterface $command Command instance. + * + * @return string|null + */ + protected function getKeyFromBlockingListCommands(CommandInterface $command) + { + $arguments = $command->getArguments(); + + if ($this->checkSameSlotForKeys(array_slice($arguments, 0, count($arguments) - 1))) { + return $arguments[0]; + } + } + + /** + * Extracts the key from BITOP command. + * + * @param CommandInterface $command Command instance. + * + * @return string|null + */ + protected function getKeyFromBitOp(CommandInterface $command) + { + $arguments = $command->getArguments(); + + if ($this->checkSameSlotForKeys(array_slice($arguments, 1, count($arguments)))) { + return $arguments[1]; + } + } + + /** + * Extracts the key from GEORADIUS and GEORADIUSBYMEMBER commands. + * + * @param CommandInterface $command Command instance. + * + * @return string|null + */ + protected function getKeyFromGeoradiusCommands(CommandInterface $command) + { + $arguments = $command->getArguments(); + $argc = count($arguments); + $startIndex = $command->getId() === 'GEORADIUS' ? 5 : 4; + + if ($argc > $startIndex) { + $keys = array($arguments[0]); + + for ($i = $startIndex; $i < $argc; ++$i) { + $argument = strtoupper($arguments[$i]); + if ($argument === 'STORE' || $argument === 'STOREDIST') { + $keys[] = $arguments[++$i]; + } + } + + if ($this->checkSameSlotForKeys($keys)) { + return $arguments[0]; + } else { + return; + } + } + + return $arguments[0]; + } + + /** + * Extracts the key from ZINTERSTORE and ZUNIONSTORE commands. + * + * @param CommandInterface $command Command instance. + * + * @return string|null + */ + protected function getKeyFromZsetAggregationCommands(CommandInterface $command) + { + $arguments = $command->getArguments(); + $keys = array_merge(array($arguments[0]), array_slice($arguments, 2, $arguments[1])); + + if ($this->checkSameSlotForKeys($keys)) { + return $arguments[0]; + } + } + + /** + * Extracts the key from EVAL and EVALSHA commands. + * + * @param CommandInterface $command Command instance. + * + * @return string|null + */ + protected function getKeyFromScriptingCommands(CommandInterface $command) + { + if ($command instanceof ScriptCommand) { + $keys = $command->getKeys(); + } else { + $keys = array_slice($args = $command->getArguments(), 2, $args[1]); + } + + if ($keys && $this->checkSameSlotForKeys($keys)) { + return $keys[0]; + } + } + + /** + * {@inheritdoc} + */ + public function getSlot(CommandInterface $command) + { + $slot = $command->getSlot(); + + if (!isset($slot) && isset($this->commands[$cmdID = $command->getId()])) { + $key = call_user_func($this->commands[$cmdID], $command); + + if (isset($key)) { + $slot = $this->getSlotByKey($key); + $command->setSlot($slot); + } + } + + return $slot; + } + + /** + * Checks if the specified array of keys will generate the same hash. + * + * @param array $keys Array of keys. + * + * @return bool + */ + protected function checkSameSlotForKeys(array $keys) + { + if (!$count = count($keys)) { + return false; + } + + $currentSlot = $this->getSlotByKey($keys[0]); + + for ($i = 1; $i < $count; ++$i) { + $nextSlot = $this->getSlotByKey($keys[$i]); + + if ($currentSlot !== $nextSlot) { + return false; + } + + $currentSlot = $nextSlot; + } + + return true; + } + + /** + * Returns only the hashable part of a key (delimited by "{...}"), or the + * whole key if a key tag is not found in the string. + * + * @param string $key A key. + * + * @return string + */ + protected function extractKeyTag($key) + { + if (false !== $start = strpos($key, '{')) { + if (false !== ($end = strpos($key, '}', $start)) && $end !== ++$start) { + $key = substr($key, $start, $end - $start); + } + } + + return $key; + } +} diff --git a/vendor/predis/predis/src/Cluster/Distributor/DistributorInterface.php b/vendor/predis/predis/src/Cluster/Distributor/DistributorInterface.php new file mode 100644 index 000000000..831f52c52 --- /dev/null +++ b/vendor/predis/predis/src/Cluster/Distributor/DistributorInterface.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Cluster\Distributor; + +use Predis\Cluster\Hash\HashGeneratorInterface; + +/** + * A distributor implements the logic to automatically distribute keys among + * several nodes for client-side sharding. + * + * @author Daniele Alessandri + */ +interface DistributorInterface +{ + /** + * Adds a node to the distributor with an optional weight. + * + * @param mixed $node Node object. + * @param int $weight Weight for the node. + */ + public function add($node, $weight = null); + + /** + * Removes a node from the distributor. + * + * @param mixed $node Node object. + */ + public function remove($node); + + /** + * Returns the corresponding slot of a node from the distributor using the + * computed hash of a key. + * + * @param mixed $hash + * + * @return mixed + */ + public function getSlot($hash); + + /** + * Returns a node from the distributor using its assigned slot ID. + * + * @param mixed $slot + * + * @return mixed|null + */ + public function getBySlot($slot); + + /** + * Returns a node from the distributor using the computed hash of a key. + * + * @param mixed $hash + * + * @return mixed + */ + public function getByHash($hash); + + /** + * Returns a node from the distributor mapping to the specified value. + * + * @param string $value + * + * @return mixed + */ + public function get($value); + + /** + * Returns the underlying hash generator instance. + * + * @return HashGeneratorInterface + */ + public function getHashGenerator(); +} diff --git a/vendor/predis/predis/src/Cluster/Distributor/EmptyRingException.php b/vendor/predis/predis/src/Cluster/Distributor/EmptyRingException.php new file mode 100644 index 000000000..039f2f2e8 --- /dev/null +++ b/vendor/predis/predis/src/Cluster/Distributor/EmptyRingException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Cluster\Distributor; + +/** + * Exception class that identifies empty rings. + * + * @author Daniele Alessandri + */ +class EmptyRingException extends \Exception +{ +} diff --git a/vendor/predis/predis/src/Cluster/Distributor/HashRing.php b/vendor/predis/predis/src/Cluster/Distributor/HashRing.php new file mode 100644 index 000000000..a0c7ae80f --- /dev/null +++ b/vendor/predis/predis/src/Cluster/Distributor/HashRing.php @@ -0,0 +1,270 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Cluster\Distributor; + +use Predis\Cluster\Hash\HashGeneratorInterface; + +/** + * This class implements an hashring-based distributor that uses the same + * algorithm of memcache to distribute keys in a cluster using client-side + * sharding. + * + * @author Daniele Alessandri + * @author Lorenzo Castelli + */ +class HashRing implements DistributorInterface, HashGeneratorInterface +{ + const DEFAULT_REPLICAS = 128; + const DEFAULT_WEIGHT = 100; + + private $ring; + private $ringKeys; + private $ringKeysCount; + private $replicas; + private $nodeHashCallback; + private $nodes = array(); + + /** + * @param int $replicas Number of replicas in the ring. + * @param mixed $nodeHashCallback Callback returning a string used to calculate the hash of nodes. + */ + public function __construct($replicas = self::DEFAULT_REPLICAS, $nodeHashCallback = null) + { + $this->replicas = $replicas; + $this->nodeHashCallback = $nodeHashCallback; + } + + /** + * Adds a node to the ring with an optional weight. + * + * @param mixed $node Node object. + * @param int $weight Weight for the node. + */ + public function add($node, $weight = null) + { + // In case of collisions in the hashes of the nodes, the node added + // last wins, thus the order in which nodes are added is significant. + $this->nodes[] = array( + 'object' => $node, + 'weight' => (int) $weight ?: $this::DEFAULT_WEIGHT, + ); + + $this->reset(); + } + + /** + * {@inheritdoc} + */ + public function remove($node) + { + // A node is removed by resetting the ring so that it's recreated from + // scratch, in order to reassign possible hashes with collisions to the + // right node according to the order in which they were added in the + // first place. + for ($i = 0; $i < count($this->nodes); ++$i) { + if ($this->nodes[$i]['object'] === $node) { + array_splice($this->nodes, $i, 1); + $this->reset(); + + break; + } + } + } + + /** + * Resets the distributor. + */ + private function reset() + { + unset( + $this->ring, + $this->ringKeys, + $this->ringKeysCount + ); + } + + /** + * Returns the initialization status of the distributor. + * + * @return bool + */ + private function isInitialized() + { + return isset($this->ringKeys); + } + + /** + * Calculates the total weight of all the nodes in the distributor. + * + * @return int + */ + private function computeTotalWeight() + { + $totalWeight = 0; + + foreach ($this->nodes as $node) { + $totalWeight += $node['weight']; + } + + return $totalWeight; + } + + /** + * Initializes the distributor. + */ + private function initialize() + { + if ($this->isInitialized()) { + return; + } + + if (!$this->nodes) { + throw new EmptyRingException('Cannot initialize an empty hashring.'); + } + + $this->ring = array(); + $totalWeight = $this->computeTotalWeight(); + $nodesCount = count($this->nodes); + + foreach ($this->nodes as $node) { + $weightRatio = $node['weight'] / $totalWeight; + $this->addNodeToRing($this->ring, $node, $nodesCount, $this->replicas, $weightRatio); + } + + ksort($this->ring, SORT_NUMERIC); + $this->ringKeys = array_keys($this->ring); + $this->ringKeysCount = count($this->ringKeys); + } + + /** + * Implements the logic needed to add a node to the hashring. + * + * @param array $ring Source hashring. + * @param mixed $node Node object to be added. + * @param int $totalNodes Total number of nodes. + * @param int $replicas Number of replicas in the ring. + * @param float $weightRatio Weight ratio for the node. + */ + protected function addNodeToRing(&$ring, $node, $totalNodes, $replicas, $weightRatio) + { + $nodeObject = $node['object']; + $nodeHash = $this->getNodeHash($nodeObject); + $replicas = (int) round($weightRatio * $totalNodes * $replicas); + + for ($i = 0; $i < $replicas; ++$i) { + $key = $this->hash("$nodeHash:$i"); + $ring[$key] = $nodeObject; + } + } + + /** + * {@inheritdoc} + */ + protected function getNodeHash($nodeObject) + { + if (!isset($this->nodeHashCallback)) { + return (string) $nodeObject; + } + + return call_user_func($this->nodeHashCallback, $nodeObject); + } + + /** + * {@inheritdoc} + */ + public function hash($value) + { + return crc32($value); + } + + /** + * {@inheritdoc} + */ + public function getByHash($hash) + { + return $this->ring[$this->getSlot($hash)]; + } + + /** + * {@inheritdoc} + */ + public function getBySlot($slot) + { + $this->initialize(); + + if (isset($this->ring[$slot])) { + return $this->ring[$slot]; + } + } + + /** + * {@inheritdoc} + */ + public function getSlot($hash) + { + $this->initialize(); + + $ringKeys = $this->ringKeys; + $upper = $this->ringKeysCount - 1; + $lower = 0; + + while ($lower <= $upper) { + $index = ($lower + $upper) >> 1; + $item = $ringKeys[$index]; + + if ($item > $hash) { + $upper = $index - 1; + } elseif ($item < $hash) { + $lower = $index + 1; + } else { + return $item; + } + } + + return $ringKeys[$this->wrapAroundStrategy($upper, $lower, $this->ringKeysCount)]; + } + + /** + * {@inheritdoc} + */ + public function get($value) + { + $hash = $this->hash($value); + $node = $this->getByHash($hash); + + return $node; + } + + /** + * Implements a strategy to deal with wrap-around errors during binary searches. + * + * @param int $upper + * @param int $lower + * @param int $ringKeysCount + * + * @return int + */ + protected function wrapAroundStrategy($upper, $lower, $ringKeysCount) + { + // Binary search for the last item in ringkeys with a value less or + // equal to the key. If no such item exists, return the last item. + return $upper >= 0 ? $upper : $ringKeysCount - 1; + } + + /** + * {@inheritdoc} + */ + public function getHashGenerator() + { + return $this; + } +} diff --git a/vendor/predis/predis/src/Cluster/Distributor/KetamaRing.php b/vendor/predis/predis/src/Cluster/Distributor/KetamaRing.php new file mode 100644 index 000000000..dc77f320f --- /dev/null +++ b/vendor/predis/predis/src/Cluster/Distributor/KetamaRing.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Cluster\Distributor; + +/** + * This class implements an hashring-based distributor that uses the same + * algorithm of libketama to distribute keys in a cluster using client-side + * sharding. + * + * @author Daniele Alessandri + * @author Lorenzo Castelli + */ +class KetamaRing extends HashRing +{ + const DEFAULT_REPLICAS = 160; + + /** + * @param mixed $nodeHashCallback Callback returning a string used to calculate the hash of nodes. + */ + public function __construct($nodeHashCallback = null) + { + parent::__construct($this::DEFAULT_REPLICAS, $nodeHashCallback); + } + + /** + * {@inheritdoc} + */ + protected function addNodeToRing(&$ring, $node, $totalNodes, $replicas, $weightRatio) + { + $nodeObject = $node['object']; + $nodeHash = $this->getNodeHash($nodeObject); + $replicas = (int) floor($weightRatio * $totalNodes * ($replicas / 4)); + + for ($i = 0; $i < $replicas; ++$i) { + $unpackedDigest = unpack('V4', md5("$nodeHash-$i", true)); + + foreach ($unpackedDigest as $key) { + $ring[$key] = $nodeObject; + } + } + } + + /** + * {@inheritdoc} + */ + public function hash($value) + { + $hash = unpack('V', md5($value, true)); + + return $hash[1]; + } + + /** + * {@inheritdoc} + */ + protected function wrapAroundStrategy($upper, $lower, $ringKeysCount) + { + // Binary search for the first item in ringkeys with a value greater + // or equal to the key. If no such item exists, return the first item. + return $lower < $ringKeysCount ? $lower : 0; + } +} diff --git a/vendor/predis/predis/src/Cluster/Hash/CRC16.php b/vendor/predis/predis/src/Cluster/Hash/CRC16.php new file mode 100644 index 000000000..c52ed2aa8 --- /dev/null +++ b/vendor/predis/predis/src/Cluster/Hash/CRC16.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Cluster\Hash; + +/** + * Hash generator implementing the CRC-CCITT-16 algorithm used by redis-cluster. + * + * @author Daniele Alessandri + */ +class CRC16 implements HashGeneratorInterface +{ + private static $CCITT_16 = array( + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0, + ); + + /** + * {@inheritdoc} + */ + public function hash($value) + { + // CRC-CCITT-16 algorithm + $crc = 0; + $CCITT_16 = self::$CCITT_16; + + $value = (string) $value; + $strlen = strlen($value); + + for ($i = 0; $i < $strlen; ++$i) { + $crc = (($crc << 8) ^ $CCITT_16[($crc >> 8) ^ ord($value[$i])]) & 0xFFFF; + } + + return $crc; + } +} diff --git a/vendor/predis/predis/src/Cluster/Hash/HashGeneratorInterface.php b/vendor/predis/predis/src/Cluster/Hash/HashGeneratorInterface.php new file mode 100644 index 000000000..271b9e720 --- /dev/null +++ b/vendor/predis/predis/src/Cluster/Hash/HashGeneratorInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Cluster\Hash; + +/** + * An hash generator implements the logic used to calculate the hash of a key to + * distribute operations among Redis nodes. + * + * @author Daniele Alessandri + */ +interface HashGeneratorInterface +{ + /** + * Generates an hash from a string to be used for distribution. + * + * @param string $value String value. + * + * @return int + */ + public function hash($value); +} diff --git a/vendor/predis/predis/src/Cluster/PredisStrategy.php b/vendor/predis/predis/src/Cluster/PredisStrategy.php new file mode 100644 index 000000000..206684279 --- /dev/null +++ b/vendor/predis/predis/src/Cluster/PredisStrategy.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Cluster; + +use Predis\Cluster\Distributor\DistributorInterface; +use Predis\Cluster\Distributor\HashRing; + +/** + * Default cluster strategy used by Predis to handle client-side sharding. + * + * @author Daniele Alessandri + */ +class PredisStrategy extends ClusterStrategy +{ + protected $distributor; + + /** + * @param DistributorInterface $distributor Optional distributor instance. + */ + public function __construct(DistributorInterface $distributor = null) + { + parent::__construct(); + + $this->distributor = $distributor ?: new HashRing(); + } + + /** + * {@inheritdoc} + */ + public function getSlotByKey($key) + { + $key = $this->extractKeyTag($key); + $hash = $this->distributor->hash($key); + $slot = $this->distributor->getSlot($hash); + + return $slot; + } + + /** + * {@inheritdoc} + */ + protected function checkSameSlotForKeys(array $keys) + { + if (!$count = count($keys)) { + return false; + } + + $currentKey = $this->extractKeyTag($keys[0]); + + for ($i = 1; $i < $count; ++$i) { + $nextKey = $this->extractKeyTag($keys[$i]); + + if ($currentKey !== $nextKey) { + return false; + } + + $currentKey = $nextKey; + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function getDistributor() + { + return $this->distributor; + } +} diff --git a/vendor/predis/predis/src/Cluster/RedisStrategy.php b/vendor/predis/predis/src/Cluster/RedisStrategy.php new file mode 100644 index 000000000..df0bdb49b --- /dev/null +++ b/vendor/predis/predis/src/Cluster/RedisStrategy.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Cluster; + +use Predis\Cluster\Hash\CRC16; +use Predis\Cluster\Hash\HashGeneratorInterface; +use Predis\NotSupportedException; + +/** + * Default class used by Predis to calculate hashes out of keys of + * commands supported by redis-cluster. + * + * @author Daniele Alessandri + */ +class RedisStrategy extends ClusterStrategy +{ + protected $hashGenerator; + + /** + * @param HashGeneratorInterface $hashGenerator Hash generator instance. + */ + public function __construct(HashGeneratorInterface $hashGenerator = null) + { + parent::__construct(); + + $this->hashGenerator = $hashGenerator ?: new CRC16(); + } + + /** + * {@inheritdoc} + */ + public function getSlotByKey($key) + { + $key = $this->extractKeyTag($key); + $slot = $this->hashGenerator->hash($key) & 0x3FFF; + + return $slot; + } + + /** + * {@inheritdoc} + */ + public function getDistributor() + { + throw new NotSupportedException( + 'This cluster strategy does not provide an external distributor' + ); + } +} diff --git a/vendor/predis/predis/src/Cluster/StrategyInterface.php b/vendor/predis/predis/src/Cluster/StrategyInterface.php new file mode 100644 index 000000000..cdf7d09fa --- /dev/null +++ b/vendor/predis/predis/src/Cluster/StrategyInterface.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Cluster; + +use Predis\Cluster\Distributor\DistributorInterface; +use Predis\Command\CommandInterface; + +/** + * Interface for classes defining the strategy used to calculate an hash out of + * keys extracted from supported commands. + * + * This is mostly useful to support clustering via client-side sharding. + * + * @author Daniele Alessandri + */ +interface StrategyInterface +{ + /** + * Returns a slot for the given command used for clustering distribution or + * NULL when this is not possible. + * + * @param CommandInterface $command Command instance. + * + * @return int + */ + public function getSlot(CommandInterface $command); + + /** + * Returns a slot for the given key used for clustering distribution or NULL + * when this is not possible. + * + * @param string $key Key string. + * + * @return int + */ + public function getSlotByKey($key); + + /** + * Returns a distributor instance to be used by the cluster. + * + * @return DistributorInterface + */ + public function getDistributor(); +} diff --git a/vendor/predis/predis/src/Collection/Iterator/CursorBasedIterator.php b/vendor/predis/predis/src/Collection/Iterator/CursorBasedIterator.php new file mode 100644 index 000000000..922883f05 --- /dev/null +++ b/vendor/predis/predis/src/Collection/Iterator/CursorBasedIterator.php @@ -0,0 +1,191 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Collection\Iterator; + +use Predis\ClientInterface; +use Predis\NotSupportedException; + +/** + * Provides the base implementation for a fully-rewindable PHP iterator that can + * incrementally iterate over cursor-based collections stored on Redis using the + * commands in the `SCAN` family. + * + * Given their incremental nature with multiple fetches, these kind of iterators + * offer limited guarantees about the returned elements because the collection + * can change several times during the iteration process. + * + * @see http://redis.io/commands/scan + * + * @author Daniele Alessandri + */ +abstract class CursorBasedIterator implements \Iterator +{ + protected $client; + protected $match; + protected $count; + + protected $valid; + protected $fetchmore; + protected $elements; + protected $cursor; + protected $position; + protected $current; + + /** + * @param ClientInterface $client Client connected to Redis. + * @param string $match Pattern to match during the server-side iteration. + * @param int $count Hint used by Redis to compute the number of results per iteration. + */ + public function __construct(ClientInterface $client, $match = null, $count = null) + { + $this->client = $client; + $this->match = $match; + $this->count = $count; + + $this->reset(); + } + + /** + * Ensures that the client supports the specified Redis command required to + * fetch elements from the server to perform the iteration. + * + * @param ClientInterface $client Client connected to Redis. + * @param string $commandID Command ID. + * + * @throws NotSupportedException + */ + protected function requiredCommand(ClientInterface $client, $commandID) + { + if (!$client->getProfile()->supportsCommand($commandID)) { + throw new NotSupportedException("The current profile does not support '$commandID'."); + } + } + + /** + * Resets the inner state of the iterator. + */ + protected function reset() + { + $this->valid = true; + $this->fetchmore = true; + $this->elements = array(); + $this->cursor = 0; + $this->position = -1; + $this->current = null; + } + + /** + * Returns an array of options for the `SCAN` command. + * + * @return array + */ + protected function getScanOptions() + { + $options = array(); + + if (strlen($this->match) > 0) { + $options['MATCH'] = $this->match; + } + + if ($this->count > 0) { + $options['COUNT'] = $this->count; + } + + return $options; + } + + /** + * Fetches a new set of elements from the remote collection, effectively + * advancing the iteration process. + * + * @return array + */ + abstract protected function executeCommand(); + + /** + * Populates the local buffer of elements fetched from the server during + * the iteration. + */ + protected function fetch() + { + list($cursor, $elements) = $this->executeCommand(); + + if (!$cursor) { + $this->fetchmore = false; + } + + $this->cursor = $cursor; + $this->elements = $elements; + } + + /** + * Extracts next values for key() and current(). + */ + protected function extractNext() + { + ++$this->position; + $this->current = array_shift($this->elements); + } + + /** + * {@inheritdoc} + */ + public function rewind() + { + $this->reset(); + $this->next(); + } + + /** + * {@inheritdoc} + */ + public function current() + { + return $this->current; + } + + /** + * {@inheritdoc} + */ + public function key() + { + return $this->position; + } + + /** + * {@inheritdoc} + */ + public function next() + { + tryFetch: { + if (!$this->elements && $this->fetchmore) { + $this->fetch(); + } + + if ($this->elements) { + $this->extractNext(); + } elseif ($this->cursor) { + goto tryFetch; + } else { + $this->valid = false; + } + } + } + + /** + * {@inheritdoc} + */ + public function valid() + { + return $this->valid; + } +} diff --git a/vendor/predis/predis/src/Collection/Iterator/HashKey.php b/vendor/predis/predis/src/Collection/Iterator/HashKey.php new file mode 100644 index 000000000..5bd07c2d8 --- /dev/null +++ b/vendor/predis/predis/src/Collection/Iterator/HashKey.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Collection\Iterator; + +use Predis\ClientInterface; + +/** + * Abstracts the iteration of fields and values of an hash by leveraging the + * HSCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator. + * + * @author Daniele Alessandri + * + * @link http://redis.io/commands/scan + */ +class HashKey extends CursorBasedIterator +{ + protected $key; + + /** + * {@inheritdoc} + */ + public function __construct(ClientInterface $client, $key, $match = null, $count = null) + { + $this->requiredCommand($client, 'HSCAN'); + + parent::__construct($client, $match, $count); + + $this->key = $key; + } + + /** + * {@inheritdoc} + */ + protected function executeCommand() + { + return $this->client->hscan($this->key, $this->cursor, $this->getScanOptions()); + } + + /** + * {@inheritdoc} + */ + protected function extractNext() + { + $this->position = key($this->elements); + $this->current = current($this->elements); + + unset($this->elements[$this->position]); + } +} diff --git a/vendor/predis/predis/src/Collection/Iterator/Keyspace.php b/vendor/predis/predis/src/Collection/Iterator/Keyspace.php new file mode 100644 index 000000000..5d985b9bc --- /dev/null +++ b/vendor/predis/predis/src/Collection/Iterator/Keyspace.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Collection\Iterator; + +use Predis\ClientInterface; + +/** + * Abstracts the iteration of the keyspace on a Redis instance by leveraging the + * SCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator. + * + * @author Daniele Alessandri + * + * @link http://redis.io/commands/scan + */ +class Keyspace extends CursorBasedIterator +{ + /** + * {@inheritdoc} + */ + public function __construct(ClientInterface $client, $match = null, $count = null) + { + $this->requiredCommand($client, 'SCAN'); + + parent::__construct($client, $match, $count); + } + + /** + * {@inheritdoc} + */ + protected function executeCommand() + { + return $this->client->scan($this->cursor, $this->getScanOptions()); + } +} diff --git a/vendor/predis/predis/src/Collection/Iterator/ListKey.php b/vendor/predis/predis/src/Collection/Iterator/ListKey.php new file mode 100644 index 000000000..7a6eb479e --- /dev/null +++ b/vendor/predis/predis/src/Collection/Iterator/ListKey.php @@ -0,0 +1,176 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Collection\Iterator; + +use Predis\ClientInterface; +use Predis\NotSupportedException; + +/** + * Abstracts the iteration of items stored in a list by leveraging the LRANGE + * command wrapped in a fully-rewindable PHP iterator. + * + * This iterator tries to emulate the behaviour of cursor-based iterators based + * on the SCAN-family of commands introduced in Redis <= 2.8, meaning that due + * to its incremental nature with multiple fetches it can only offer limited + * guarantees on the returned elements because the collection can change several + * times (trimmed, deleted, overwritten) during the iteration process. + * + * @author Daniele Alessandri + * + * @link http://redis.io/commands/lrange + */ +class ListKey implements \Iterator +{ + protected $client; + protected $count; + protected $key; + + protected $valid; + protected $fetchmore; + protected $elements; + protected $position; + protected $current; + + /** + * @param ClientInterface $client Client connected to Redis. + * @param string $key Redis list key. + * @param int $count Number of items retrieved on each fetch operation. + * + * @throws \InvalidArgumentException + */ + public function __construct(ClientInterface $client, $key, $count = 10) + { + $this->requiredCommand($client, 'LRANGE'); + + if ((false === $count = filter_var($count, FILTER_VALIDATE_INT)) || $count < 0) { + throw new \InvalidArgumentException('The $count argument must be a positive integer.'); + } + + $this->client = $client; + $this->key = $key; + $this->count = $count; + + $this->reset(); + } + + /** + * Ensures that the client instance supports the specified Redis command + * required to fetch elements from the server to perform the iteration. + * + * @param ClientInterface $client Client connected to Redis. + * @param string $commandID Command ID. + * + * @throws NotSupportedException + */ + protected function requiredCommand(ClientInterface $client, $commandID) + { + if (!$client->getProfile()->supportsCommand($commandID)) { + throw new NotSupportedException("The current profile does not support '$commandID'."); + } + } + + /** + * Resets the inner state of the iterator. + */ + protected function reset() + { + $this->valid = true; + $this->fetchmore = true; + $this->elements = array(); + $this->position = -1; + $this->current = null; + } + + /** + * Fetches a new set of elements from the remote collection, effectively + * advancing the iteration process. + * + * @return array + */ + protected function executeCommand() + { + return $this->client->lrange($this->key, $this->position + 1, $this->position + $this->count); + } + + /** + * Populates the local buffer of elements fetched from the server during the + * iteration. + */ + protected function fetch() + { + $elements = $this->executeCommand(); + + if (count($elements) < $this->count) { + $this->fetchmore = false; + } + + $this->elements = $elements; + } + + /** + * Extracts next values for key() and current(). + */ + protected function extractNext() + { + ++$this->position; + $this->current = array_shift($this->elements); + } + + /** + * {@inheritdoc} + */ + public function rewind() + { + $this->reset(); + $this->next(); + } + + /** + * {@inheritdoc} + */ + public function current() + { + return $this->current; + } + + /** + * {@inheritdoc} + */ + public function key() + { + return $this->position; + } + + /** + * {@inheritdoc} + */ + public function next() + { + if (!$this->elements && $this->fetchmore) { + $this->fetch(); + } + + if ($this->elements) { + $this->extractNext(); + } else { + $this->valid = false; + } + } + + /** + * {@inheritdoc} + */ + public function valid() + { + return $this->valid; + } +} diff --git a/vendor/predis/predis/src/Collection/Iterator/SetKey.php b/vendor/predis/predis/src/Collection/Iterator/SetKey.php new file mode 100644 index 000000000..bf2543975 --- /dev/null +++ b/vendor/predis/predis/src/Collection/Iterator/SetKey.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Collection\Iterator; + +use Predis\ClientInterface; + +/** + * Abstracts the iteration of members stored in a set by leveraging the SSCAN + * command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator. + * + * @author Daniele Alessandri + * + * @link http://redis.io/commands/scan + */ +class SetKey extends CursorBasedIterator +{ + protected $key; + + /** + * {@inheritdoc} + */ + public function __construct(ClientInterface $client, $key, $match = null, $count = null) + { + $this->requiredCommand($client, 'SSCAN'); + + parent::__construct($client, $match, $count); + + $this->key = $key; + } + + /** + * {@inheritdoc} + */ + protected function executeCommand() + { + return $this->client->sscan($this->key, $this->cursor, $this->getScanOptions()); + } +} diff --git a/vendor/predis/predis/src/Collection/Iterator/SortedSetKey.php b/vendor/predis/predis/src/Collection/Iterator/SortedSetKey.php new file mode 100644 index 000000000..d96122a86 --- /dev/null +++ b/vendor/predis/predis/src/Collection/Iterator/SortedSetKey.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Collection\Iterator; + +use Predis\ClientInterface; + +/** + * Abstracts the iteration of members stored in a sorted set by leveraging the + * ZSCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator. + * + * @author Daniele Alessandri + * + * @link http://redis.io/commands/scan + */ +class SortedSetKey extends CursorBasedIterator +{ + protected $key; + + /** + * {@inheritdoc} + */ + public function __construct(ClientInterface $client, $key, $match = null, $count = null) + { + $this->requiredCommand($client, 'ZSCAN'); + + parent::__construct($client, $match, $count); + + $this->key = $key; + } + + /** + * {@inheritdoc} + */ + protected function executeCommand() + { + return $this->client->zscan($this->key, $this->cursor, $this->getScanOptions()); + } + + /** + * {@inheritdoc} + */ + protected function extractNext() + { + $this->position = key($this->elements); + $this->current = current($this->elements); + + unset($this->elements[$this->position]); + } +} diff --git a/vendor/predis/predis/src/Command/Command.php b/vendor/predis/predis/src/Command/Command.php new file mode 100644 index 000000000..bb538e7c5 --- /dev/null +++ b/vendor/predis/predis/src/Command/Command.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * Base class for Redis commands. + * + * @author Daniele Alessandri + */ +abstract class Command implements CommandInterface +{ + private $slot; + private $arguments = array(); + + /** + * Returns a filtered array of the arguments. + * + * @param array $arguments List of arguments. + * + * @return array + */ + protected function filterArguments(array $arguments) + { + return $arguments; + } + + /** + * {@inheritdoc} + */ + public function setArguments(array $arguments) + { + $this->arguments = $this->filterArguments($arguments); + unset($this->slot); + } + + /** + * {@inheritdoc} + */ + public function setRawArguments(array $arguments) + { + $this->arguments = $arguments; + unset($this->slot); + } + + /** + * {@inheritdoc} + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * {@inheritdoc} + */ + public function getArgument($index) + { + if (isset($this->arguments[$index])) { + return $this->arguments[$index]; + } + } + + /** + * {@inheritdoc} + */ + public function setSlot($slot) + { + $this->slot = $slot; + } + + /** + * {@inheritdoc} + */ + public function getSlot() + { + if (isset($this->slot)) { + return $this->slot; + } + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return $data; + } + + /** + * Normalizes the arguments array passed to a Redis command. + * + * @param array $arguments Arguments for a command. + * + * @return array + */ + public static function normalizeArguments(array $arguments) + { + if (count($arguments) === 1 && is_array($arguments[0])) { + return $arguments[0]; + } + + return $arguments; + } + + /** + * Normalizes the arguments array passed to a variadic Redis command. + * + * @param array $arguments Arguments for a command. + * + * @return array + */ + public static function normalizeVariadic(array $arguments) + { + if (count($arguments) === 2 && is_array($arguments[1])) { + return array_merge(array($arguments[0]), $arguments[1]); + } + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/CommandInterface.php b/vendor/predis/predis/src/Command/CommandInterface.php new file mode 100644 index 000000000..9f349e1df --- /dev/null +++ b/vendor/predis/predis/src/Command/CommandInterface.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * Defines an abstraction representing a Redis command. + * + * @author Daniele Alessandri + */ +interface CommandInterface +{ + /** + * Returns the ID of the Redis command. By convention, command identifiers + * must always be uppercase. + * + * @return string + */ + public function getId(); + + /** + * Assign the specified slot to the command for clustering distribution. + * + * @param int $slot Slot ID. + */ + public function setSlot($slot); + + /** + * Returns the assigned slot of the command for clustering distribution. + * + * @return int|null + */ + public function getSlot(); + + /** + * Sets the arguments for the command. + * + * @param array $arguments List of arguments. + */ + public function setArguments(array $arguments); + + /** + * Sets the raw arguments for the command without processing them. + * + * @param array $arguments List of arguments. + */ + public function setRawArguments(array $arguments); + + /** + * Gets the arguments of the command. + * + * @return array + */ + public function getArguments(); + + /** + * Gets the argument of the command at the specified index. + * + * @param int $index Index of the desired argument. + * + * @return mixed|null + */ + public function getArgument($index); + + /** + * Parses a raw response and returns a PHP object. + * + * @param string $data Binary string containing the whole response. + * + * @return mixed + */ + public function parseResponse($data); +} diff --git a/vendor/predis/predis/src/Command/ConnectionAuth.php b/vendor/predis/predis/src/Command/ConnectionAuth.php new file mode 100644 index 000000000..c8c9dedce --- /dev/null +++ b/vendor/predis/predis/src/Command/ConnectionAuth.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/auth + * + * @author Daniele Alessandri + */ +class ConnectionAuth extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'AUTH'; + } +} diff --git a/vendor/predis/predis/src/Command/ConnectionEcho.php b/vendor/predis/predis/src/Command/ConnectionEcho.php new file mode 100644 index 000000000..fd4960971 --- /dev/null +++ b/vendor/predis/predis/src/Command/ConnectionEcho.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/echo + * + * @author Daniele Alessandri + */ +class ConnectionEcho extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ECHO'; + } +} diff --git a/vendor/predis/predis/src/Command/ConnectionPing.php b/vendor/predis/predis/src/Command/ConnectionPing.php new file mode 100644 index 000000000..fa9d7346f --- /dev/null +++ b/vendor/predis/predis/src/Command/ConnectionPing.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/ping + * + * @author Daniele Alessandri + */ +class ConnectionPing extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PING'; + } +} diff --git a/vendor/predis/predis/src/Command/ConnectionQuit.php b/vendor/predis/predis/src/Command/ConnectionQuit.php new file mode 100644 index 000000000..e59e31e3e --- /dev/null +++ b/vendor/predis/predis/src/Command/ConnectionQuit.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/quit + * + * @author Daniele Alessandri + */ +class ConnectionQuit extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'QUIT'; + } +} diff --git a/vendor/predis/predis/src/Command/ConnectionSelect.php b/vendor/predis/predis/src/Command/ConnectionSelect.php new file mode 100644 index 000000000..1da825677 --- /dev/null +++ b/vendor/predis/predis/src/Command/ConnectionSelect.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/select + * + * @author Daniele Alessandri + */ +class ConnectionSelect extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SELECT'; + } +} diff --git a/vendor/predis/predis/src/Command/GeospatialGeoAdd.php b/vendor/predis/predis/src/Command/GeospatialGeoAdd.php new file mode 100644 index 000000000..adca2ca53 --- /dev/null +++ b/vendor/predis/predis/src/Command/GeospatialGeoAdd.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/geoadd + * + * @author Daniele Alessandri + */ +class GeospatialGeoAdd extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'GEOADD'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 2 && is_array($arguments[1])) { + foreach (array_pop($arguments) as $item) { + $arguments = array_merge($arguments, $item); + } + } + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/GeospatialGeoDist.php b/vendor/predis/predis/src/Command/GeospatialGeoDist.php new file mode 100644 index 000000000..17c5f5496 --- /dev/null +++ b/vendor/predis/predis/src/Command/GeospatialGeoDist.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/geodist + * + * @author Daniele Alessandri + */ +class GeospatialGeoDist extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'GEODIST'; + } +} diff --git a/vendor/predis/predis/src/Command/GeospatialGeoHash.php b/vendor/predis/predis/src/Command/GeospatialGeoHash.php new file mode 100644 index 000000000..2eccaf4f9 --- /dev/null +++ b/vendor/predis/predis/src/Command/GeospatialGeoHash.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/geohash + * + * @author Daniele Alessandri + */ +class GeospatialGeoHash extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'GEOHASH'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 2 && is_array($arguments[1])) { + $members = array_pop($arguments); + $arguments = array_merge($arguments, $members); + } + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/GeospatialGeoPos.php b/vendor/predis/predis/src/Command/GeospatialGeoPos.php new file mode 100644 index 000000000..6b7a9a3d0 --- /dev/null +++ b/vendor/predis/predis/src/Command/GeospatialGeoPos.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/geopos + * + * @author Daniele Alessandri + */ +class GeospatialGeoPos extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'GEOPOS'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 2 && is_array($arguments[1])) { + $members = array_pop($arguments); + $arguments = array_merge($arguments, $members); + } + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/GeospatialGeoRadius.php b/vendor/predis/predis/src/Command/GeospatialGeoRadius.php new file mode 100644 index 000000000..f20521487 --- /dev/null +++ b/vendor/predis/predis/src/Command/GeospatialGeoRadius.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/georadius + * + * @author Daniele Alessandri + */ +class GeospatialGeoRadius extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'GEORADIUS'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if ($arguments && is_array(end($arguments))) { + $options = array_change_key_case(array_pop($arguments), CASE_UPPER); + + if (isset($options['WITHCOORD']) && $options['WITHCOORD'] == true) { + $arguments[] = 'WITHCOORD'; + } + + if (isset($options['WITHDIST']) && $options['WITHDIST'] == true) { + $arguments[] = 'WITHDIST'; + } + + if (isset($options['WITHHASH']) && $options['WITHHASH'] == true) { + $arguments[] = 'WITHHASH'; + } + + if (isset($options['COUNT'])) { + $arguments[] = 'COUNT'; + $arguments[] = $options['COUNT']; + } + + if (isset($options['SORT'])) { + $arguments[] = strtoupper($options['SORT']); + } + + if (isset($options['STORE'])) { + $arguments[] = 'STORE'; + $arguments[] = $options['STORE']; + } + + if (isset($options['STOREDIST'])) { + $arguments[] = 'STOREDIST'; + $arguments[] = $options['STOREDIST']; + } + } + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/GeospatialGeoRadiusByMember.php b/vendor/predis/predis/src/Command/GeospatialGeoRadiusByMember.php new file mode 100644 index 000000000..abfff7b4a --- /dev/null +++ b/vendor/predis/predis/src/Command/GeospatialGeoRadiusByMember.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/georadiusbymember + * + * @author Daniele Alessandri + */ +class GeospatialGeoRadiusByMember extends GeospatialGeoRadius +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'GEORADIUSBYMEMBER'; + } +} diff --git a/vendor/predis/predis/src/Command/HashDelete.php b/vendor/predis/predis/src/Command/HashDelete.php new file mode 100644 index 000000000..d5d4c38c0 --- /dev/null +++ b/vendor/predis/predis/src/Command/HashDelete.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/hdel + * + * @author Daniele Alessandri + */ +class HashDelete extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HDEL'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeVariadic($arguments); + } +} diff --git a/vendor/predis/predis/src/Command/HashExists.php b/vendor/predis/predis/src/Command/HashExists.php new file mode 100644 index 000000000..ed8dc89f0 --- /dev/null +++ b/vendor/predis/predis/src/Command/HashExists.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/hexists + * + * @author Daniele Alessandri + */ +class HashExists extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HEXISTS'; + } +} diff --git a/vendor/predis/predis/src/Command/HashGet.php b/vendor/predis/predis/src/Command/HashGet.php new file mode 100644 index 000000000..20f33da54 --- /dev/null +++ b/vendor/predis/predis/src/Command/HashGet.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/hget + * + * @author Daniele Alessandri + */ +class HashGet extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HGET'; + } +} diff --git a/vendor/predis/predis/src/Command/HashGetAll.php b/vendor/predis/predis/src/Command/HashGetAll.php new file mode 100644 index 000000000..d69867521 --- /dev/null +++ b/vendor/predis/predis/src/Command/HashGetAll.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/hgetall + * + * @author Daniele Alessandri + */ +class HashGetAll extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HGETALL'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + $result = array(); + + for ($i = 0; $i < count($data); ++$i) { + $result[$data[$i]] = $data[++$i]; + } + + return $result; + } +} diff --git a/vendor/predis/predis/src/Command/HashGetMultiple.php b/vendor/predis/predis/src/Command/HashGetMultiple.php new file mode 100644 index 000000000..820ce958e --- /dev/null +++ b/vendor/predis/predis/src/Command/HashGetMultiple.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/hmget + * + * @author Daniele Alessandri + */ +class HashGetMultiple extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HMGET'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeVariadic($arguments); + } +} diff --git a/vendor/predis/predis/src/Command/HashIncrementBy.php b/vendor/predis/predis/src/Command/HashIncrementBy.php new file mode 100644 index 000000000..a37359ffb --- /dev/null +++ b/vendor/predis/predis/src/Command/HashIncrementBy.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/hincrby + * + * @author Daniele Alessandri + */ +class HashIncrementBy extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HINCRBY'; + } +} diff --git a/vendor/predis/predis/src/Command/HashIncrementByFloat.php b/vendor/predis/predis/src/Command/HashIncrementByFloat.php new file mode 100644 index 000000000..bce9714fc --- /dev/null +++ b/vendor/predis/predis/src/Command/HashIncrementByFloat.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/hincrbyfloat + * + * @author Daniele Alessandri + */ +class HashIncrementByFloat extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HINCRBYFLOAT'; + } +} diff --git a/vendor/predis/predis/src/Command/HashKeys.php b/vendor/predis/predis/src/Command/HashKeys.php new file mode 100644 index 000000000..28266020a --- /dev/null +++ b/vendor/predis/predis/src/Command/HashKeys.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/hkeys + * + * @author Daniele Alessandri + */ +class HashKeys extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HKEYS'; + } +} diff --git a/vendor/predis/predis/src/Command/HashLength.php b/vendor/predis/predis/src/Command/HashLength.php new file mode 100644 index 000000000..d70926f1c --- /dev/null +++ b/vendor/predis/predis/src/Command/HashLength.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/hlen + * + * @author Daniele Alessandri + */ +class HashLength extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HLEN'; + } +} diff --git a/vendor/predis/predis/src/Command/HashScan.php b/vendor/predis/predis/src/Command/HashScan.php new file mode 100644 index 000000000..afde74eb9 --- /dev/null +++ b/vendor/predis/predis/src/Command/HashScan.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/hscan + * + * @author Daniele Alessandri + */ +class HashScan extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HSCAN'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 3 && is_array($arguments[2])) { + $options = $this->prepareOptions(array_pop($arguments)); + $arguments = array_merge($arguments, $options); + } + + return $arguments; + } + + /** + * Returns a list of options and modifiers compatible with Redis. + * + * @param array $options List of options. + * + * @return array + */ + protected function prepareOptions($options) + { + $options = array_change_key_case($options, CASE_UPPER); + $normalized = array(); + + if (!empty($options['MATCH'])) { + $normalized[] = 'MATCH'; + $normalized[] = $options['MATCH']; + } + + if (!empty($options['COUNT'])) { + $normalized[] = 'COUNT'; + $normalized[] = $options['COUNT']; + } + + return $normalized; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + if (is_array($data)) { + $fields = $data[1]; + $result = array(); + + for ($i = 0; $i < count($fields); ++$i) { + $result[$fields[$i]] = $fields[++$i]; + } + + $data[1] = $result; + } + + return $data; + } +} diff --git a/vendor/predis/predis/src/Command/HashSet.php b/vendor/predis/predis/src/Command/HashSet.php new file mode 100644 index 000000000..cfff3c2d4 --- /dev/null +++ b/vendor/predis/predis/src/Command/HashSet.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/hset + * + * @author Daniele Alessandri + */ +class HashSet extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HSET'; + } +} diff --git a/vendor/predis/predis/src/Command/HashSetMultiple.php b/vendor/predis/predis/src/Command/HashSetMultiple.php new file mode 100644 index 000000000..6069e2ad9 --- /dev/null +++ b/vendor/predis/predis/src/Command/HashSetMultiple.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/hmset + * + * @author Daniele Alessandri + */ +class HashSetMultiple extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HMSET'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 2 && is_array($arguments[1])) { + $flattenedKVs = array($arguments[0]); + $args = $arguments[1]; + + foreach ($args as $k => $v) { + $flattenedKVs[] = $k; + $flattenedKVs[] = $v; + } + + return $flattenedKVs; + } + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/HashSetPreserve.php b/vendor/predis/predis/src/Command/HashSetPreserve.php new file mode 100644 index 000000000..7a291164d --- /dev/null +++ b/vendor/predis/predis/src/Command/HashSetPreserve.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/hsetnx + * + * @author Daniele Alessandri + */ +class HashSetPreserve extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HSETNX'; + } +} diff --git a/vendor/predis/predis/src/Command/HashStringLength.php b/vendor/predis/predis/src/Command/HashStringLength.php new file mode 100644 index 000000000..7cfda80df --- /dev/null +++ b/vendor/predis/predis/src/Command/HashStringLength.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/hstrlen + * + * @author Daniele Alessandri + */ +class HashStringLength extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HSTRLEN'; + } +} diff --git a/vendor/predis/predis/src/Command/HashValues.php b/vendor/predis/predis/src/Command/HashValues.php new file mode 100644 index 000000000..0a5ea5f61 --- /dev/null +++ b/vendor/predis/predis/src/Command/HashValues.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/hvals + * + * @author Daniele Alessandri + */ +class HashValues extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HVALS'; + } +} diff --git a/vendor/predis/predis/src/Command/HyperLogLogAdd.php b/vendor/predis/predis/src/Command/HyperLogLogAdd.php new file mode 100644 index 000000000..8fe49fc9b --- /dev/null +++ b/vendor/predis/predis/src/Command/HyperLogLogAdd.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/pfadd + * + * @author Daniele Alessandri + */ +class HyperLogLogAdd extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PFADD'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeVariadic($arguments); + } +} diff --git a/vendor/predis/predis/src/Command/HyperLogLogCount.php b/vendor/predis/predis/src/Command/HyperLogLogCount.php new file mode 100644 index 000000000..0afe54270 --- /dev/null +++ b/vendor/predis/predis/src/Command/HyperLogLogCount.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/pfcount + * + * @author Daniele Alessandri + */ +class HyperLogLogCount extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PFCOUNT'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeArguments($arguments); + } +} diff --git a/vendor/predis/predis/src/Command/HyperLogLogMerge.php b/vendor/predis/predis/src/Command/HyperLogLogMerge.php new file mode 100644 index 000000000..c160be5b2 --- /dev/null +++ b/vendor/predis/predis/src/Command/HyperLogLogMerge.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/pfmerge + * + * @author Daniele Alessandri + */ +class HyperLogLogMerge extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PFMERGE'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeArguments($arguments); + } +} diff --git a/vendor/predis/predis/src/Command/KeyDelete.php b/vendor/predis/predis/src/Command/KeyDelete.php new file mode 100644 index 000000000..89bdfdb7f --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyDelete.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/del + * + * @author Daniele Alessandri + */ +class KeyDelete extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'DEL'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeArguments($arguments); + } +} diff --git a/vendor/predis/predis/src/Command/KeyDump.php b/vendor/predis/predis/src/Command/KeyDump.php new file mode 100644 index 000000000..6d9c48800 --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyDump.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/dump + * + * @author Daniele Alessandri + */ +class KeyDump extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'DUMP'; + } +} diff --git a/vendor/predis/predis/src/Command/KeyExists.php b/vendor/predis/predis/src/Command/KeyExists.php new file mode 100644 index 000000000..29e064804 --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyExists.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/exists + * + * @author Daniele Alessandri + */ +class KeyExists extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'EXISTS'; + } +} diff --git a/vendor/predis/predis/src/Command/KeyExpire.php b/vendor/predis/predis/src/Command/KeyExpire.php new file mode 100644 index 000000000..66f440663 --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyExpire.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/expire + * + * @author Daniele Alessandri + */ +class KeyExpire extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'EXPIRE'; + } +} diff --git a/vendor/predis/predis/src/Command/KeyExpireAt.php b/vendor/predis/predis/src/Command/KeyExpireAt.php new file mode 100644 index 000000000..0ae1b2d4e --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyExpireAt.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/expireat + * + * @author Daniele Alessandri + */ +class KeyExpireAt extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'EXPIREAT'; + } +} diff --git a/vendor/predis/predis/src/Command/KeyKeys.php b/vendor/predis/predis/src/Command/KeyKeys.php new file mode 100644 index 000000000..6d74c40d2 --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyKeys.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/keys + * + * @author Daniele Alessandri + */ +class KeyKeys extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'KEYS'; + } +} diff --git a/vendor/predis/predis/src/Command/KeyMigrate.php b/vendor/predis/predis/src/Command/KeyMigrate.php new file mode 100644 index 000000000..3324ef94c --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyMigrate.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/migrate + * + * @author Daniele Alessandri + */ +class KeyMigrate extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'MIGRATE'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (is_array(end($arguments))) { + foreach (array_pop($arguments) as $modifier => $value) { + $modifier = strtoupper($modifier); + + if ($modifier === 'COPY' && $value == true) { + $arguments[] = $modifier; + } + + if ($modifier === 'REPLACE' && $value == true) { + $arguments[] = $modifier; + } + } + } + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/KeyMove.php b/vendor/predis/predis/src/Command/KeyMove.php new file mode 100644 index 000000000..c849f08d2 --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyMove.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/move + * + * @author Daniele Alessandri + */ +class KeyMove extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'MOVE'; + } +} diff --git a/vendor/predis/predis/src/Command/KeyPersist.php b/vendor/predis/predis/src/Command/KeyPersist.php new file mode 100644 index 000000000..f0cb679b8 --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyPersist.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/persist + * + * @author Daniele Alessandri + */ +class KeyPersist extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PERSIST'; + } +} diff --git a/vendor/predis/predis/src/Command/KeyPreciseExpire.php b/vendor/predis/predis/src/Command/KeyPreciseExpire.php new file mode 100644 index 000000000..258ec4766 --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyPreciseExpire.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/pexpire + * + * @author Daniele Alessandri + */ +class KeyPreciseExpire extends KeyExpire +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PEXPIRE'; + } +} diff --git a/vendor/predis/predis/src/Command/KeyPreciseExpireAt.php b/vendor/predis/predis/src/Command/KeyPreciseExpireAt.php new file mode 100644 index 000000000..e41921870 --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyPreciseExpireAt.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/pexpireat + * + * @author Daniele Alessandri + */ +class KeyPreciseExpireAt extends KeyExpireAt +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PEXPIREAT'; + } +} diff --git a/vendor/predis/predis/src/Command/KeyPreciseTimeToLive.php b/vendor/predis/predis/src/Command/KeyPreciseTimeToLive.php new file mode 100644 index 000000000..bdcd34b92 --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyPreciseTimeToLive.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/pttl + * + * @author Daniele Alessandri + */ +class KeyPreciseTimeToLive extends KeyTimeToLive +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PTTL'; + } +} diff --git a/vendor/predis/predis/src/Command/KeyRandom.php b/vendor/predis/predis/src/Command/KeyRandom.php new file mode 100644 index 000000000..b208b2db4 --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyRandom.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/randomkey + * + * @author Daniele Alessandri + */ +class KeyRandom extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'RANDOMKEY'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return $data !== '' ? $data : null; + } +} diff --git a/vendor/predis/predis/src/Command/KeyRename.php b/vendor/predis/predis/src/Command/KeyRename.php new file mode 100644 index 000000000..82e44fb2e --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyRename.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/rename + * + * @author Daniele Alessandri + */ +class KeyRename extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'RENAME'; + } +} diff --git a/vendor/predis/predis/src/Command/KeyRenamePreserve.php b/vendor/predis/predis/src/Command/KeyRenamePreserve.php new file mode 100644 index 000000000..97933599e --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyRenamePreserve.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/renamenx + * + * @author Daniele Alessandri + */ +class KeyRenamePreserve extends KeyRename +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'RENAMENX'; + } +} diff --git a/vendor/predis/predis/src/Command/KeyRestore.php b/vendor/predis/predis/src/Command/KeyRestore.php new file mode 100644 index 000000000..a5b0b2dbc --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyRestore.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/restore + * + * @author Daniele Alessandri + */ +class KeyRestore extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'RESTORE'; + } +} diff --git a/vendor/predis/predis/src/Command/KeyScan.php b/vendor/predis/predis/src/Command/KeyScan.php new file mode 100644 index 000000000..05f5bb3ac --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyScan.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/scan + * + * @author Daniele Alessandri + */ +class KeyScan extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SCAN'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 2 && is_array($arguments[1])) { + $options = $this->prepareOptions(array_pop($arguments)); + $arguments = array_merge($arguments, $options); + } + + return $arguments; + } + + /** + * Returns a list of options and modifiers compatible with Redis. + * + * @param array $options List of options. + * + * @return array + */ + protected function prepareOptions($options) + { + $options = array_change_key_case($options, CASE_UPPER); + $normalized = array(); + + if (!empty($options['MATCH'])) { + $normalized[] = 'MATCH'; + $normalized[] = $options['MATCH']; + } + + if (!empty($options['COUNT'])) { + $normalized[] = 'COUNT'; + $normalized[] = $options['COUNT']; + } + + return $normalized; + } +} diff --git a/vendor/predis/predis/src/Command/KeySort.php b/vendor/predis/predis/src/Command/KeySort.php new file mode 100644 index 000000000..fd449f13d --- /dev/null +++ b/vendor/predis/predis/src/Command/KeySort.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/sort + * + * @author Daniele Alessandri + */ +class KeySort extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SORT'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 1) { + return $arguments; + } + + $query = array($arguments[0]); + $sortParams = array_change_key_case($arguments[1], CASE_UPPER); + + if (isset($sortParams['BY'])) { + $query[] = 'BY'; + $query[] = $sortParams['BY']; + } + + if (isset($sortParams['GET'])) { + $getargs = $sortParams['GET']; + + if (is_array($getargs)) { + foreach ($getargs as $getarg) { + $query[] = 'GET'; + $query[] = $getarg; + } + } else { + $query[] = 'GET'; + $query[] = $getargs; + } + } + + if (isset($sortParams['LIMIT']) && + is_array($sortParams['LIMIT']) && + count($sortParams['LIMIT']) == 2) { + $query[] = 'LIMIT'; + $query[] = $sortParams['LIMIT'][0]; + $query[] = $sortParams['LIMIT'][1]; + } + + if (isset($sortParams['SORT'])) { + $query[] = strtoupper($sortParams['SORT']); + } + + if (isset($sortParams['ALPHA']) && $sortParams['ALPHA'] == true) { + $query[] = 'ALPHA'; + } + + if (isset($sortParams['STORE'])) { + $query[] = 'STORE'; + $query[] = $sortParams['STORE']; + } + + return $query; + } +} diff --git a/vendor/predis/predis/src/Command/KeyTimeToLive.php b/vendor/predis/predis/src/Command/KeyTimeToLive.php new file mode 100644 index 000000000..67697a6fb --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyTimeToLive.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/ttl + * + * @author Daniele Alessandri + */ +class KeyTimeToLive extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'TTL'; + } +} diff --git a/vendor/predis/predis/src/Command/KeyType.php b/vendor/predis/predis/src/Command/KeyType.php new file mode 100644 index 000000000..f4f06e451 --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyType.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/type + * + * @author Daniele Alessandri + */ +class KeyType extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'TYPE'; + } +} diff --git a/vendor/predis/predis/src/Command/ListIndex.php b/vendor/predis/predis/src/Command/ListIndex.php new file mode 100644 index 000000000..27c64be73 --- /dev/null +++ b/vendor/predis/predis/src/Command/ListIndex.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/lindex + * + * @author Daniele Alessandri + */ +class ListIndex extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LINDEX'; + } +} diff --git a/vendor/predis/predis/src/Command/ListInsert.php b/vendor/predis/predis/src/Command/ListInsert.php new file mode 100644 index 000000000..7d53d11b2 --- /dev/null +++ b/vendor/predis/predis/src/Command/ListInsert.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/linsert + * + * @author Daniele Alessandri + */ +class ListInsert extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LINSERT'; + } +} diff --git a/vendor/predis/predis/src/Command/ListLength.php b/vendor/predis/predis/src/Command/ListLength.php new file mode 100644 index 000000000..6495beb77 --- /dev/null +++ b/vendor/predis/predis/src/Command/ListLength.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/llen + * + * @author Daniele Alessandri + */ +class ListLength extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LLEN'; + } +} diff --git a/vendor/predis/predis/src/Command/ListPopFirst.php b/vendor/predis/predis/src/Command/ListPopFirst.php new file mode 100644 index 000000000..84d5d6734 --- /dev/null +++ b/vendor/predis/predis/src/Command/ListPopFirst.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/lpop + * + * @author Daniele Alessandri + */ +class ListPopFirst extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LPOP'; + } +} diff --git a/vendor/predis/predis/src/Command/ListPopFirstBlocking.php b/vendor/predis/predis/src/Command/ListPopFirstBlocking.php new file mode 100644 index 000000000..7dc7c0002 --- /dev/null +++ b/vendor/predis/predis/src/Command/ListPopFirstBlocking.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/blpop + * + * @author Daniele Alessandri + */ +class ListPopFirstBlocking extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'BLPOP'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 2 && is_array($arguments[0])) { + list($arguments, $timeout) = $arguments; + array_push($arguments, $timeout); + } + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/ListPopLast.php b/vendor/predis/predis/src/Command/ListPopLast.php new file mode 100644 index 000000000..9e92db5f4 --- /dev/null +++ b/vendor/predis/predis/src/Command/ListPopLast.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/rpop + * + * @author Daniele Alessandri + */ +class ListPopLast extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'RPOP'; + } +} diff --git a/vendor/predis/predis/src/Command/ListPopLastBlocking.php b/vendor/predis/predis/src/Command/ListPopLastBlocking.php new file mode 100644 index 000000000..781eb9192 --- /dev/null +++ b/vendor/predis/predis/src/Command/ListPopLastBlocking.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/brpop + * + * @author Daniele Alessandri + */ +class ListPopLastBlocking extends ListPopFirstBlocking +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'BRPOP'; + } +} diff --git a/vendor/predis/predis/src/Command/ListPopLastPushHead.php b/vendor/predis/predis/src/Command/ListPopLastPushHead.php new file mode 100644 index 000000000..f430eb227 --- /dev/null +++ b/vendor/predis/predis/src/Command/ListPopLastPushHead.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/rpoplpush + * + * @author Daniele Alessandri + */ +class ListPopLastPushHead extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'RPOPLPUSH'; + } +} diff --git a/vendor/predis/predis/src/Command/ListPopLastPushHeadBlocking.php b/vendor/predis/predis/src/Command/ListPopLastPushHeadBlocking.php new file mode 100644 index 000000000..ee9c93c85 --- /dev/null +++ b/vendor/predis/predis/src/Command/ListPopLastPushHeadBlocking.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/brpoplpush + * + * @author Daniele Alessandri + */ +class ListPopLastPushHeadBlocking extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'BRPOPLPUSH'; + } +} diff --git a/vendor/predis/predis/src/Command/ListPushHead.php b/vendor/predis/predis/src/Command/ListPushHead.php new file mode 100644 index 000000000..74bf7c491 --- /dev/null +++ b/vendor/predis/predis/src/Command/ListPushHead.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/lpush + * + * @author Daniele Alessandri + */ +class ListPushHead extends ListPushTail +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LPUSH'; + } +} diff --git a/vendor/predis/predis/src/Command/ListPushHeadX.php b/vendor/predis/predis/src/Command/ListPushHeadX.php new file mode 100644 index 000000000..8e136b880 --- /dev/null +++ b/vendor/predis/predis/src/Command/ListPushHeadX.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/lpushx + * + * @author Daniele Alessandri + */ +class ListPushHeadX extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LPUSHX'; + } +} diff --git a/vendor/predis/predis/src/Command/ListPushTail.php b/vendor/predis/predis/src/Command/ListPushTail.php new file mode 100644 index 000000000..f2a057c0b --- /dev/null +++ b/vendor/predis/predis/src/Command/ListPushTail.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/rpush + * + * @author Daniele Alessandri + */ +class ListPushTail extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'RPUSH'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeVariadic($arguments); + } +} diff --git a/vendor/predis/predis/src/Command/ListPushTailX.php b/vendor/predis/predis/src/Command/ListPushTailX.php new file mode 100644 index 000000000..1af3645b4 --- /dev/null +++ b/vendor/predis/predis/src/Command/ListPushTailX.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/rpushx + * + * @author Daniele Alessandri + */ +class ListPushTailX extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'RPUSHX'; + } +} diff --git a/vendor/predis/predis/src/Command/ListRange.php b/vendor/predis/predis/src/Command/ListRange.php new file mode 100644 index 000000000..32a21a6e6 --- /dev/null +++ b/vendor/predis/predis/src/Command/ListRange.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/lrange + * + * @author Daniele Alessandri + */ +class ListRange extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LRANGE'; + } +} diff --git a/vendor/predis/predis/src/Command/ListRemove.php b/vendor/predis/predis/src/Command/ListRemove.php new file mode 100644 index 000000000..c5800899b --- /dev/null +++ b/vendor/predis/predis/src/Command/ListRemove.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/lrem + * + * @author Daniele Alessandri + */ +class ListRemove extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LREM'; + } +} diff --git a/vendor/predis/predis/src/Command/ListSet.php b/vendor/predis/predis/src/Command/ListSet.php new file mode 100644 index 000000000..5e59864de --- /dev/null +++ b/vendor/predis/predis/src/Command/ListSet.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/lset + * + * @author Daniele Alessandri + */ +class ListSet extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LSET'; + } +} diff --git a/vendor/predis/predis/src/Command/ListTrim.php b/vendor/predis/predis/src/Command/ListTrim.php new file mode 100644 index 000000000..193141809 --- /dev/null +++ b/vendor/predis/predis/src/Command/ListTrim.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/ltrim + * + * @author Daniele Alessandri + */ +class ListTrim extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LTRIM'; + } +} diff --git a/vendor/predis/predis/src/Command/PrefixableCommandInterface.php b/vendor/predis/predis/src/Command/PrefixableCommandInterface.php new file mode 100644 index 000000000..6d54554fa --- /dev/null +++ b/vendor/predis/predis/src/Command/PrefixableCommandInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * Defines a command whose keys can be prefixed. + * + * @author Daniele Alessandri + */ +interface PrefixableCommandInterface extends CommandInterface +{ + /** + * Prefixes all the keys found in the arguments of the command. + * + * @param string $prefix String used to prefix the keys. + */ + public function prefixKeys($prefix); +} diff --git a/vendor/predis/predis/src/Command/Processor/KeyPrefixProcessor.php b/vendor/predis/predis/src/Command/Processor/KeyPrefixProcessor.php new file mode 100644 index 000000000..752012766 --- /dev/null +++ b/vendor/predis/predis/src/Command/Processor/KeyPrefixProcessor.php @@ -0,0 +1,450 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command\Processor; + +use Predis\Command\CommandInterface; +use Predis\Command\PrefixableCommandInterface; + +/** + * Command processor capable of prefixing keys stored in the arguments of Redis + * commands supported. + * + * @author Daniele Alessandri + */ +class KeyPrefixProcessor implements ProcessorInterface +{ + private $prefix; + private $commands; + + /** + * @param string $prefix Prefix for the keys. + */ + public function __construct($prefix) + { + $this->prefix = $prefix; + $this->commands = array( + /* ---------------- Redis 1.2 ---------------- */ + 'EXISTS' => 'static::all', + 'DEL' => 'static::all', + 'TYPE' => 'static::first', + 'KEYS' => 'static::first', + 'RENAME' => 'static::all', + 'RENAMENX' => 'static::all', + 'EXPIRE' => 'static::first', + 'EXPIREAT' => 'static::first', + 'TTL' => 'static::first', + 'MOVE' => 'static::first', + 'SORT' => 'static::sort', + 'DUMP' => 'static::first', + 'RESTORE' => 'static::first', + 'SET' => 'static::first', + 'SETNX' => 'static::first', + 'MSET' => 'static::interleaved', + 'MSETNX' => 'static::interleaved', + 'GET' => 'static::first', + 'MGET' => 'static::all', + 'GETSET' => 'static::first', + 'INCR' => 'static::first', + 'INCRBY' => 'static::first', + 'DECR' => 'static::first', + 'DECRBY' => 'static::first', + 'RPUSH' => 'static::first', + 'LPUSH' => 'static::first', + 'LLEN' => 'static::first', + 'LRANGE' => 'static::first', + 'LTRIM' => 'static::first', + 'LINDEX' => 'static::first', + 'LSET' => 'static::first', + 'LREM' => 'static::first', + 'LPOP' => 'static::first', + 'RPOP' => 'static::first', + 'RPOPLPUSH' => 'static::all', + 'SADD' => 'static::first', + 'SREM' => 'static::first', + 'SPOP' => 'static::first', + 'SMOVE' => 'static::skipLast', + 'SCARD' => 'static::first', + 'SISMEMBER' => 'static::first', + 'SINTER' => 'static::all', + 'SINTERSTORE' => 'static::all', + 'SUNION' => 'static::all', + 'SUNIONSTORE' => 'static::all', + 'SDIFF' => 'static::all', + 'SDIFFSTORE' => 'static::all', + 'SMEMBERS' => 'static::first', + 'SRANDMEMBER' => 'static::first', + 'ZADD' => 'static::first', + 'ZINCRBY' => 'static::first', + 'ZREM' => 'static::first', + 'ZRANGE' => 'static::first', + 'ZREVRANGE' => 'static::first', + 'ZRANGEBYSCORE' => 'static::first', + 'ZCARD' => 'static::first', + 'ZSCORE' => 'static::first', + 'ZREMRANGEBYSCORE' => 'static::first', + /* ---------------- Redis 2.0 ---------------- */ + 'SETEX' => 'static::first', + 'APPEND' => 'static::first', + 'SUBSTR' => 'static::first', + 'BLPOP' => 'static::skipLast', + 'BRPOP' => 'static::skipLast', + 'ZUNIONSTORE' => 'static::zsetStore', + 'ZINTERSTORE' => 'static::zsetStore', + 'ZCOUNT' => 'static::first', + 'ZRANK' => 'static::first', + 'ZREVRANK' => 'static::first', + 'ZREMRANGEBYRANK' => 'static::first', + 'HSET' => 'static::first', + 'HSETNX' => 'static::first', + 'HMSET' => 'static::first', + 'HINCRBY' => 'static::first', + 'HGET' => 'static::first', + 'HMGET' => 'static::first', + 'HDEL' => 'static::first', + 'HEXISTS' => 'static::first', + 'HLEN' => 'static::first', + 'HKEYS' => 'static::first', + 'HVALS' => 'static::first', + 'HGETALL' => 'static::first', + 'SUBSCRIBE' => 'static::all', + 'UNSUBSCRIBE' => 'static::all', + 'PSUBSCRIBE' => 'static::all', + 'PUNSUBSCRIBE' => 'static::all', + 'PUBLISH' => 'static::first', + /* ---------------- Redis 2.2 ---------------- */ + 'PERSIST' => 'static::first', + 'STRLEN' => 'static::first', + 'SETRANGE' => 'static::first', + 'GETRANGE' => 'static::first', + 'SETBIT' => 'static::first', + 'GETBIT' => 'static::first', + 'RPUSHX' => 'static::first', + 'LPUSHX' => 'static::first', + 'LINSERT' => 'static::first', + 'BRPOPLPUSH' => 'static::skipLast', + 'ZREVRANGEBYSCORE' => 'static::first', + 'WATCH' => 'static::all', + /* ---------------- Redis 2.6 ---------------- */ + 'PTTL' => 'static::first', + 'PEXPIRE' => 'static::first', + 'PEXPIREAT' => 'static::first', + 'PSETEX' => 'static::first', + 'INCRBYFLOAT' => 'static::first', + 'BITOP' => 'static::skipFirst', + 'BITCOUNT' => 'static::first', + 'HINCRBYFLOAT' => 'static::first', + 'EVAL' => 'static::evalKeys', + 'EVALSHA' => 'static::evalKeys', + 'MIGRATE' => 'static::migrate', + /* ---------------- Redis 2.8 ---------------- */ + 'SSCAN' => 'static::first', + 'ZSCAN' => 'static::first', + 'HSCAN' => 'static::first', + 'PFADD' => 'static::first', + 'PFCOUNT' => 'static::all', + 'PFMERGE' => 'static::all', + 'ZLEXCOUNT' => 'static::first', + 'ZRANGEBYLEX' => 'static::first', + 'ZREMRANGEBYLEX' => 'static::first', + 'ZREVRANGEBYLEX' => 'static::first', + 'BITPOS' => 'static::first', + /* ---------------- Redis 3.2 ---------------- */ + 'HSTRLEN' => 'static::first', + 'BITFIELD' => 'static::first', + 'GEOADD' => 'static::first', + 'GEOHASH' => 'static::first', + 'GEOPOS' => 'static::first', + 'GEODIST' => 'static::first', + 'GEORADIUS' => 'static::georadius', + 'GEORADIUSBYMEMBER' => 'static::georadius', + ); + } + + /** + * Sets a prefix that is applied to all the keys. + * + * @param string $prefix Prefix for the keys. + */ + public function setPrefix($prefix) + { + $this->prefix = $prefix; + } + + /** + * Gets the current prefix. + * + * @return string + */ + public function getPrefix() + { + return $this->prefix; + } + + /** + * {@inheritdoc} + */ + public function process(CommandInterface $command) + { + if ($command instanceof PrefixableCommandInterface) { + $command->prefixKeys($this->prefix); + } elseif (isset($this->commands[$commandID = strtoupper($command->getId())])) { + call_user_func($this->commands[$commandID], $command, $this->prefix); + } + } + + /** + * Sets an handler for the specified command ID. + * + * The callback signature must have 2 parameters of the following types: + * + * - Predis\Command\CommandInterface (command instance) + * - String (prefix) + * + * When the callback argument is omitted or NULL, the previously + * associated handler for the specified command ID is removed. + * + * @param string $commandID The ID of the command to be handled. + * @param mixed $callback A valid callable object or NULL. + * + * @throws \InvalidArgumentException + */ + public function setCommandHandler($commandID, $callback = null) + { + $commandID = strtoupper($commandID); + + if (!isset($callback)) { + unset($this->commands[$commandID]); + + return; + } + + if (!is_callable($callback)) { + throw new \InvalidArgumentException( + 'Callback must be a valid callable object or NULL' + ); + } + + $this->commands[$commandID] = $callback; + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return $this->getPrefix(); + } + + /** + * Applies the specified prefix only the first argument. + * + * @param CommandInterface $command Command instance. + * @param string $prefix Prefix string. + */ + public static function first(CommandInterface $command, $prefix) + { + if ($arguments = $command->getArguments()) { + $arguments[0] = "$prefix{$arguments[0]}"; + $command->setRawArguments($arguments); + } + } + + /** + * Applies the specified prefix to all the arguments. + * + * @param CommandInterface $command Command instance. + * @param string $prefix Prefix string. + */ + public static function all(CommandInterface $command, $prefix) + { + if ($arguments = $command->getArguments()) { + foreach ($arguments as &$key) { + $key = "$prefix$key"; + } + + $command->setRawArguments($arguments); + } + } + + /** + * Applies the specified prefix only to even arguments in the list. + * + * @param CommandInterface $command Command instance. + * @param string $prefix Prefix string. + */ + public static function interleaved(CommandInterface $command, $prefix) + { + if ($arguments = $command->getArguments()) { + $length = count($arguments); + + for ($i = 0; $i < $length; $i += 2) { + $arguments[$i] = "$prefix{$arguments[$i]}"; + } + + $command->setRawArguments($arguments); + } + } + + /** + * Applies the specified prefix to all the arguments but the first one. + * + * @param CommandInterface $command Command instance. + * @param string $prefix Prefix string. + */ + public static function skipFirst(CommandInterface $command, $prefix) + { + if ($arguments = $command->getArguments()) { + $length = count($arguments); + + for ($i = 1; $i < $length; ++$i) { + $arguments[$i] = "$prefix{$arguments[$i]}"; + } + + $command->setRawArguments($arguments); + } + } + + /** + * Applies the specified prefix to all the arguments but the last one. + * + * @param CommandInterface $command Command instance. + * @param string $prefix Prefix string. + */ + public static function skipLast(CommandInterface $command, $prefix) + { + if ($arguments = $command->getArguments()) { + $length = count($arguments); + + for ($i = 0; $i < $length - 1; ++$i) { + $arguments[$i] = "$prefix{$arguments[$i]}"; + } + + $command->setRawArguments($arguments); + } + } + + /** + * Applies the specified prefix to the keys of a SORT command. + * + * @param CommandInterface $command Command instance. + * @param string $prefix Prefix string. + */ + public static function sort(CommandInterface $command, $prefix) + { + if ($arguments = $command->getArguments()) { + $arguments[0] = "$prefix{$arguments[0]}"; + + if (($count = count($arguments)) > 1) { + for ($i = 1; $i < $count; ++$i) { + switch (strtoupper($arguments[$i])) { + case 'BY': + case 'STORE': + $arguments[$i] = "$prefix{$arguments[++$i]}"; + break; + + case 'GET': + $value = $arguments[++$i]; + if ($value !== '#') { + $arguments[$i] = "$prefix$value"; + } + break; + + case 'LIMIT'; + $i += 2; + break; + } + } + } + + $command->setRawArguments($arguments); + } + } + + /** + * Applies the specified prefix to the keys of an EVAL-based command. + * + * @param CommandInterface $command Command instance. + * @param string $prefix Prefix string. + */ + public static function evalKeys(CommandInterface $command, $prefix) + { + if ($arguments = $command->getArguments()) { + for ($i = 2; $i < $arguments[1] + 2; ++$i) { + $arguments[$i] = "$prefix{$arguments[$i]}"; + } + + $command->setRawArguments($arguments); + } + } + + /** + * Applies the specified prefix to the keys of Z[INTERSECTION|UNION]STORE. + * + * @param CommandInterface $command Command instance. + * @param string $prefix Prefix string. + */ + public static function zsetStore(CommandInterface $command, $prefix) + { + if ($arguments = $command->getArguments()) { + $arguments[0] = "$prefix{$arguments[0]}"; + $length = ((int) $arguments[1]) + 2; + + for ($i = 2; $i < $length; ++$i) { + $arguments[$i] = "$prefix{$arguments[$i]}"; + } + + $command->setRawArguments($arguments); + } + } + + /** + * Applies the specified prefix to the key of a MIGRATE command. + * + * @param CommandInterface $command Command instance. + * @param string $prefix Prefix string. + */ + public static function migrate(CommandInterface $command, $prefix) + { + if ($arguments = $command->getArguments()) { + $arguments[2] = "$prefix{$arguments[2]}"; + $command->setRawArguments($arguments); + } + } + + /** + * Applies the specified prefix to the key of a GEORADIUS command. + * + * @param CommandInterface $command Command instance. + * @param string $prefix Prefix string. + */ + public static function georadius(CommandInterface $command, $prefix) + { + if ($arguments = $command->getArguments()) { + $arguments[0] = "$prefix{$arguments[0]}"; + $startIndex = $command->getId() === 'GEORADIUS' ? 5 : 4; + + if (($count = count($arguments)) > $startIndex) { + for ($i = $startIndex; $i < $count; ++$i) { + switch (strtoupper($arguments[$i])) { + case 'STORE': + case 'STOREDIST': + $arguments[$i] = "$prefix{$arguments[++$i]}"; + break; + + } + } + } + + $command->setRawArguments($arguments); + } + } +} diff --git a/vendor/predis/predis/src/Command/Processor/ProcessorChain.php b/vendor/predis/predis/src/Command/Processor/ProcessorChain.php new file mode 100644 index 000000000..0a4768b0a --- /dev/null +++ b/vendor/predis/predis/src/Command/Processor/ProcessorChain.php @@ -0,0 +1,130 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command\Processor; + +use Predis\Command\CommandInterface; + +/** + * Default implementation of a command processors chain. + * + * @author Daniele Alessandri + */ +class ProcessorChain implements \ArrayAccess, ProcessorInterface +{ + private $processors = array(); + + /** + * @param array $processors List of instances of ProcessorInterface. + */ + public function __construct($processors = array()) + { + foreach ($processors as $processor) { + $this->add($processor); + } + } + + /** + * {@inheritdoc} + */ + public function add(ProcessorInterface $processor) + { + $this->processors[] = $processor; + } + + /** + * {@inheritdoc} + */ + public function remove(ProcessorInterface $processor) + { + if (false !== $index = array_search($processor, $this->processors, true)) { + unset($this[$index]); + } + } + + /** + * {@inheritdoc} + */ + public function process(CommandInterface $command) + { + for ($i = 0; $i < $count = count($this->processors); ++$i) { + $this->processors[$i]->process($command); + } + } + + /** + * {@inheritdoc} + */ + public function getProcessors() + { + return $this->processors; + } + + /** + * Returns an iterator over the list of command processor in the chain. + * + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->processors); + } + + /** + * Returns the number of command processors in the chain. + * + * @return int + */ + public function count() + { + return count($this->processors); + } + + /** + * {@inheritdoc} + */ + public function offsetExists($index) + { + return isset($this->processors[$index]); + } + + /** + * {@inheritdoc} + */ + public function offsetGet($index) + { + return $this->processors[$index]; + } + + /** + * {@inheritdoc} + */ + public function offsetSet($index, $processor) + { + if (!$processor instanceof ProcessorInterface) { + throw new \InvalidArgumentException( + 'A processor chain accepts only instances of '. + "'Predis\Command\Processor\ProcessorInterface'." + ); + } + + $this->processors[$index] = $processor; + } + + /** + * {@inheritdoc} + */ + public function offsetUnset($index) + { + unset($this->processors[$index]); + $this->processors = array_values($this->processors); + } +} diff --git a/vendor/predis/predis/src/Command/Processor/ProcessorInterface.php b/vendor/predis/predis/src/Command/Processor/ProcessorInterface.php new file mode 100644 index 000000000..2f9105802 --- /dev/null +++ b/vendor/predis/predis/src/Command/Processor/ProcessorInterface.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command\Processor; + +use Predis\Command\CommandInterface; + +/** + * A command processor processes Redis commands before they are sent to Redis. + * + * @author Daniele Alessandri + */ +interface ProcessorInterface +{ + /** + * Processes the given Redis command. + * + * @param CommandInterface $command Command instance. + */ + public function process(CommandInterface $command); +} diff --git a/vendor/predis/predis/src/Command/PubSubPublish.php b/vendor/predis/predis/src/Command/PubSubPublish.php new file mode 100644 index 000000000..55508f8d9 --- /dev/null +++ b/vendor/predis/predis/src/Command/PubSubPublish.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/publish + * + * @author Daniele Alessandri + */ +class PubSubPublish extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PUBLISH'; + } +} diff --git a/vendor/predis/predis/src/Command/PubSubPubsub.php b/vendor/predis/predis/src/Command/PubSubPubsub.php new file mode 100644 index 000000000..8cf812973 --- /dev/null +++ b/vendor/predis/predis/src/Command/PubSubPubsub.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/pubsub + * + * @author Daniele Alessandri + */ +class PubSubPubsub extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PUBSUB'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + switch (strtolower($this->getArgument(0))) { + case 'numsub': + return self::processNumsub($data); + + default: + return $data; + } + } + + /** + * Returns the processed response to PUBSUB NUMSUB. + * + * @param array $channels List of channels + * + * @return array + */ + protected static function processNumsub(array $channels) + { + $processed = array(); + $count = count($channels); + + for ($i = 0; $i < $count; ++$i) { + $processed[$channels[$i]] = $channels[++$i]; + } + + return $processed; + } +} diff --git a/vendor/predis/predis/src/Command/PubSubSubscribe.php b/vendor/predis/predis/src/Command/PubSubSubscribe.php new file mode 100644 index 000000000..e477b313b --- /dev/null +++ b/vendor/predis/predis/src/Command/PubSubSubscribe.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/subscribe + * + * @author Daniele Alessandri + */ +class PubSubSubscribe extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SUBSCRIBE'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeArguments($arguments); + } +} diff --git a/vendor/predis/predis/src/Command/PubSubSubscribeByPattern.php b/vendor/predis/predis/src/Command/PubSubSubscribeByPattern.php new file mode 100644 index 000000000..011828066 --- /dev/null +++ b/vendor/predis/predis/src/Command/PubSubSubscribeByPattern.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/psubscribe + * + * @author Daniele Alessandri + */ +class PubSubSubscribeByPattern extends PubSubSubscribe +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PSUBSCRIBE'; + } +} diff --git a/vendor/predis/predis/src/Command/PubSubUnsubscribe.php b/vendor/predis/predis/src/Command/PubSubUnsubscribe.php new file mode 100644 index 000000000..d57c3ac6d --- /dev/null +++ b/vendor/predis/predis/src/Command/PubSubUnsubscribe.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/unsubscribe + * + * @author Daniele Alessandri + */ +class PubSubUnsubscribe extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'UNSUBSCRIBE'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeArguments($arguments); + } +} diff --git a/vendor/predis/predis/src/Command/PubSubUnsubscribeByPattern.php b/vendor/predis/predis/src/Command/PubSubUnsubscribeByPattern.php new file mode 100644 index 000000000..4d76508b2 --- /dev/null +++ b/vendor/predis/predis/src/Command/PubSubUnsubscribeByPattern.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/punsubscribe + * + * @author Daniele Alessandri + */ +class PubSubUnsubscribeByPattern extends PubSubUnsubscribe +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PUNSUBSCRIBE'; + } +} diff --git a/vendor/predis/predis/src/Command/RawCommand.php b/vendor/predis/predis/src/Command/RawCommand.php new file mode 100644 index 000000000..2dd48ca17 --- /dev/null +++ b/vendor/predis/predis/src/Command/RawCommand.php @@ -0,0 +1,131 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * Class for generic "anonymous" Redis commands. + * + * This command class does not filter input arguments or parse responses, but + * can be used to leverage the standard Predis API to execute any command simply + * by providing the needed arguments following the command signature as defined + * by Redis in its documentation. + * + * @author Daniele Alessandri + */ +class RawCommand implements CommandInterface +{ + private $slot; + private $commandID; + private $arguments; + + /** + * @param array $arguments Command ID and its arguments. + * + * @throws \InvalidArgumentException + */ + public function __construct(array $arguments) + { + if (!$arguments) { + throw new \InvalidArgumentException( + 'The arguments array must contain at least the command ID.' + ); + } + + $this->commandID = strtoupper(array_shift($arguments)); + $this->arguments = $arguments; + } + + /** + * Creates a new raw command using a variadic method. + * + * @param string $commandID Redis command ID. + * @param string ... Arguments list for the command. + * + * @return CommandInterface + */ + public static function create($commandID /* [ $arg, ... */) + { + $arguments = func_get_args(); + $command = new self($arguments); + + return $command; + } + + /** + * {@inheritdoc} + */ + public function getId() + { + return $this->commandID; + } + + /** + * {@inheritdoc} + */ + public function setArguments(array $arguments) + { + $this->arguments = $arguments; + unset($this->slot); + } + + /** + * {@inheritdoc} + */ + public function setRawArguments(array $arguments) + { + $this->setArguments($arguments); + } + + /** + * {@inheritdoc} + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * {@inheritdoc} + */ + public function getArgument($index) + { + if (isset($this->arguments[$index])) { + return $this->arguments[$index]; + } + } + + /** + * {@inheritdoc} + */ + public function setSlot($slot) + { + $this->slot = $slot; + } + + /** + * {@inheritdoc} + */ + public function getSlot() + { + if (isset($this->slot)) { + return $this->slot; + } + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return $data; + } +} diff --git a/vendor/predis/predis/src/Command/ScriptCommand.php b/vendor/predis/predis/src/Command/ScriptCommand.php new file mode 100644 index 000000000..a30bc1d28 --- /dev/null +++ b/vendor/predis/predis/src/Command/ScriptCommand.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * Base class used to implement an higher level abstraction for commands based + * on Lua scripting with EVAL and EVALSHA. + * + * @link http://redis.io/commands/eval + * + * @author Daniele Alessandri + */ +abstract class ScriptCommand extends ServerEvalSHA +{ + /** + * Gets the body of a Lua script. + * + * @return string + */ + abstract public function getScript(); + + /** + * Specifies the number of arguments that should be considered as keys. + * + * The default behaviour for the base class is to return 0 to indicate that + * all the elements of the arguments array should be considered as keys, but + * subclasses can enforce a static number of keys. + * + * @return int + */ + protected function getKeysCount() + { + return 0; + } + + /** + * Returns the elements from the arguments that are identified as keys. + * + * @return array + */ + public function getKeys() + { + return array_slice($this->getArguments(), 2, $this->getKeysCount()); + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (($numkeys = $this->getKeysCount()) && $numkeys < 0) { + $numkeys = count($arguments) + $numkeys; + } + + return array_merge(array(sha1($this->getScript()), (int) $numkeys), $arguments); + } + + /** + * @return array + */ + public function getEvalArguments() + { + $arguments = $this->getArguments(); + $arguments[0] = $this->getScript(); + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/ServerBackgroundRewriteAOF.php b/vendor/predis/predis/src/Command/ServerBackgroundRewriteAOF.php new file mode 100644 index 000000000..c66a294e5 --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerBackgroundRewriteAOF.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/bgrewriteaof + * + * @author Daniele Alessandri + */ +class ServerBackgroundRewriteAOF extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'BGREWRITEAOF'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return $data == 'Background append only file rewriting started'; + } +} diff --git a/vendor/predis/predis/src/Command/ServerBackgroundSave.php b/vendor/predis/predis/src/Command/ServerBackgroundSave.php new file mode 100644 index 000000000..4bf67ef30 --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerBackgroundSave.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/bgsave + * + * @author Daniele Alessandri + */ +class ServerBackgroundSave extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'BGSAVE'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return $data === 'Background saving started' ? true : $data; + } +} diff --git a/vendor/predis/predis/src/Command/ServerClient.php b/vendor/predis/predis/src/Command/ServerClient.php new file mode 100644 index 000000000..d00ebbfff --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerClient.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/client-list + * @link http://redis.io/commands/client-kill + * @link http://redis.io/commands/client-getname + * @link http://redis.io/commands/client-setname + * + * @author Daniele Alessandri + */ +class ServerClient extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'CLIENT'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + $args = array_change_key_case($this->getArguments(), CASE_UPPER); + + switch (strtoupper($args[0])) { + case 'LIST': + return $this->parseClientList($data); + case 'KILL': + case 'GETNAME': + case 'SETNAME': + default: + return $data; + } + } + + /** + * Parses the response to CLIENT LIST and returns a structured list. + * + * @param string $data Response buffer. + * + * @return array + */ + protected function parseClientList($data) + { + $clients = array(); + + foreach (explode("\n", $data, -1) as $clientData) { + $client = array(); + + foreach (explode(' ', $clientData) as $kv) { + @list($k, $v) = explode('=', $kv); + $client[$k] = $v; + } + + $clients[] = $client; + } + + return $clients; + } +} diff --git a/vendor/predis/predis/src/Command/ServerCommand.php b/vendor/predis/predis/src/Command/ServerCommand.php new file mode 100644 index 000000000..e9b3393c7 --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerCommand.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/command + * + * @author Daniele Alessandri + */ +class ServerCommand extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'COMMAND'; + } +} diff --git a/vendor/predis/predis/src/Command/ServerConfig.php b/vendor/predis/predis/src/Command/ServerConfig.php new file mode 100644 index 000000000..81e497aea --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerConfig.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/config-set + * @link http://redis.io/commands/config-get + * @link http://redis.io/commands/config-resetstat + * @link http://redis.io/commands/config-rewrite + * + * @author Daniele Alessandri + */ +class ServerConfig extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'CONFIG'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + if (is_array($data)) { + $result = array(); + + for ($i = 0; $i < count($data); ++$i) { + $result[$data[$i]] = $data[++$i]; + } + + return $result; + } + + return $data; + } +} diff --git a/vendor/predis/predis/src/Command/ServerDatabaseSize.php b/vendor/predis/predis/src/Command/ServerDatabaseSize.php new file mode 100644 index 000000000..6bc89724d --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerDatabaseSize.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/dbsize + * + * @author Daniele Alessandri + */ +class ServerDatabaseSize extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'DBSIZE'; + } +} diff --git a/vendor/predis/predis/src/Command/ServerEval.php b/vendor/predis/predis/src/Command/ServerEval.php new file mode 100644 index 000000000..f5eefd811 --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerEval.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/eval + * + * @author Daniele Alessandri + */ +class ServerEval extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'EVAL'; + } + + /** + * Calculates the SHA1 hash of the body of the script. + * + * @return string SHA1 hash. + */ + public function getScriptHash() + { + return sha1($this->getArgument(0)); + } +} diff --git a/vendor/predis/predis/src/Command/ServerEvalSHA.php b/vendor/predis/predis/src/Command/ServerEvalSHA.php new file mode 100644 index 000000000..520a8e985 --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerEvalSHA.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/evalsha + * + * @author Daniele Alessandri + */ +class ServerEvalSHA extends ServerEval +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'EVALSHA'; + } + + /** + * Returns the SHA1 hash of the body of the script. + * + * @return string SHA1 hash. + */ + public function getScriptHash() + { + return $this->getArgument(0); + } +} diff --git a/vendor/predis/predis/src/Command/ServerFlushAll.php b/vendor/predis/predis/src/Command/ServerFlushAll.php new file mode 100644 index 000000000..c35b2ad6a --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerFlushAll.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/flushall + * + * @author Daniele Alessandri + */ +class ServerFlushAll extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'FLUSHALL'; + } +} diff --git a/vendor/predis/predis/src/Command/ServerFlushDatabase.php b/vendor/predis/predis/src/Command/ServerFlushDatabase.php new file mode 100644 index 000000000..3da6b320d --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerFlushDatabase.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/flushdb + * + * @author Daniele Alessandri + */ +class ServerFlushDatabase extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'FLUSHDB'; + } +} diff --git a/vendor/predis/predis/src/Command/ServerInfo.php b/vendor/predis/predis/src/Command/ServerInfo.php new file mode 100644 index 000000000..96d6adad9 --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerInfo.php @@ -0,0 +1,111 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/info + * + * @author Daniele Alessandri + */ +class ServerInfo extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'INFO'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + $info = array(); + $infoLines = preg_split('/\r?\n/', $data); + + foreach ($infoLines as $row) { + if (strpos($row, ':') === false) { + continue; + } + + list($k, $v) = $this->parseRow($row); + $info[$k] = $v; + } + + return $info; + } + + /** + * Parses a single row of the response and returns the key-value pair. + * + * @param string $row Single row of the response. + * + * @return array + */ + protected function parseRow($row) + { + list($k, $v) = explode(':', $row, 2); + + if (preg_match('/^db\d+$/', $k)) { + $v = $this->parseDatabaseStats($v); + } + + return array($k, $v); + } + + /** + * Extracts the statistics of each logical DB from the string buffer. + * + * @param string $str Response buffer. + * + * @return array + */ + protected function parseDatabaseStats($str) + { + $db = array(); + + foreach (explode(',', $str) as $dbvar) { + list($dbvk, $dbvv) = explode('=', $dbvar); + $db[trim($dbvk)] = $dbvv; + } + + return $db; + } + + /** + * Parses the response and extracts the allocation statistics. + * + * @param string $str Response buffer. + * + * @return array + */ + protected function parseAllocationStats($str) + { + $stats = array(); + + foreach (explode(',', $str) as $kv) { + @list($size, $objects, $extra) = explode('=', $kv); + + // hack to prevent incorrect values when parsing the >=256 key + if (isset($extra)) { + $size = ">=$objects"; + $objects = $extra; + } + + $stats[$size] = $objects; + } + + return $stats; + } +} diff --git a/vendor/predis/predis/src/Command/ServerInfoV26x.php b/vendor/predis/predis/src/Command/ServerInfoV26x.php new file mode 100644 index 000000000..90c9b7163 --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerInfoV26x.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/info + * + * @author Daniele Alessandri + */ +class ServerInfoV26x extends ServerInfo +{ + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + if ($data === '') { + return array(); + } + + $info = array(); + + $current = null; + $infoLines = preg_split('/\r?\n/', $data); + + if (isset($infoLines[0]) && $infoLines[0][0] !== '#') { + return parent::parseResponse($data); + } + + foreach ($infoLines as $row) { + if ($row === '') { + continue; + } + + if (preg_match('/^# (\w+)$/', $row, $matches)) { + $info[$matches[1]] = array(); + $current = &$info[$matches[1]]; + continue; + } + + list($k, $v) = $this->parseRow($row); + $current[$k] = $v; + } + + return $info; + } +} diff --git a/vendor/predis/predis/src/Command/ServerLastSave.php b/vendor/predis/predis/src/Command/ServerLastSave.php new file mode 100644 index 000000000..feeb19a8a --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerLastSave.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/lastsave + * + * @author Daniele Alessandri + */ +class ServerLastSave extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LASTSAVE'; + } +} diff --git a/vendor/predis/predis/src/Command/ServerMonitor.php b/vendor/predis/predis/src/Command/ServerMonitor.php new file mode 100644 index 000000000..1c3d33095 --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerMonitor.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/monitor + * + * @author Daniele Alessandri + */ +class ServerMonitor extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'MONITOR'; + } +} diff --git a/vendor/predis/predis/src/Command/ServerObject.php b/vendor/predis/predis/src/Command/ServerObject.php new file mode 100644 index 000000000..f921701c2 --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerObject.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/object + * + * @author Daniele Alessandri + */ +class ServerObject extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'OBJECT'; + } +} diff --git a/vendor/predis/predis/src/Command/ServerSave.php b/vendor/predis/predis/src/Command/ServerSave.php new file mode 100644 index 000000000..addefe20f --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerSave.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/save + * + * @author Daniele Alessandri + */ +class ServerSave extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SAVE'; + } +} diff --git a/vendor/predis/predis/src/Command/ServerScript.php b/vendor/predis/predis/src/Command/ServerScript.php new file mode 100644 index 000000000..7a01018d9 --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerScript.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/script + * + * @author Daniele Alessandri + */ +class ServerScript extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SCRIPT'; + } +} diff --git a/vendor/predis/predis/src/Command/ServerSentinel.php b/vendor/predis/predis/src/Command/ServerSentinel.php new file mode 100644 index 000000000..c0962db3d --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerSentinel.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/topics/sentinel + * + * @author Daniele Alessandri + */ +class ServerSentinel extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SENTINEL'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + switch (strtolower($this->getArgument(0))) { + case 'masters': + case 'slaves': + return self::processMastersOrSlaves($data); + + default: + return $data; + } + } + + /** + * Returns a processed response to SENTINEL MASTERS or SENTINEL SLAVES. + * + * @param array $servers List of Redis servers. + * + * @return array + */ + protected static function processMastersOrSlaves(array $servers) + { + foreach ($servers as $idx => $node) { + $processed = array(); + $count = count($node); + + for ($i = 0; $i < $count; ++$i) { + $processed[$node[$i]] = $node[++$i]; + } + + $servers[$idx] = $processed; + } + + return $servers; + } +} diff --git a/vendor/predis/predis/src/Command/ServerShutdown.php b/vendor/predis/predis/src/Command/ServerShutdown.php new file mode 100644 index 000000000..f5b745a21 --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerShutdown.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/shutdown + * + * @author Daniele Alessandri + */ +class ServerShutdown extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SHUTDOWN'; + } +} diff --git a/vendor/predis/predis/src/Command/ServerSlaveOf.php b/vendor/predis/predis/src/Command/ServerSlaveOf.php new file mode 100644 index 000000000..4ff44556a --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerSlaveOf.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/slaveof + * + * @author Daniele Alessandri + */ +class ServerSlaveOf extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SLAVEOF'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 0 || $arguments[0] === 'NO ONE') { + return array('NO', 'ONE'); + } + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/ServerSlowlog.php b/vendor/predis/predis/src/Command/ServerSlowlog.php new file mode 100644 index 000000000..137ff59e7 --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerSlowlog.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/slowlog + * + * @author Daniele Alessandri + */ +class ServerSlowlog extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SLOWLOG'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + if (is_array($data)) { + $log = array(); + + foreach ($data as $index => $entry) { + $log[$index] = array( + 'id' => $entry[0], + 'timestamp' => $entry[1], + 'duration' => $entry[2], + 'command' => $entry[3], + ); + } + + return $log; + } + + return $data; + } +} diff --git a/vendor/predis/predis/src/Command/ServerTime.php b/vendor/predis/predis/src/Command/ServerTime.php new file mode 100644 index 000000000..589f92c55 --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerTime.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/time + * + * @author Daniele Alessandri + */ +class ServerTime extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'TIME'; + } +} diff --git a/vendor/predis/predis/src/Command/SetAdd.php b/vendor/predis/predis/src/Command/SetAdd.php new file mode 100644 index 000000000..c11881816 --- /dev/null +++ b/vendor/predis/predis/src/Command/SetAdd.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/sadd + * + * @author Daniele Alessandri + */ +class SetAdd extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SADD'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeVariadic($arguments); + } +} diff --git a/vendor/predis/predis/src/Command/SetCardinality.php b/vendor/predis/predis/src/Command/SetCardinality.php new file mode 100644 index 000000000..a9f959b78 --- /dev/null +++ b/vendor/predis/predis/src/Command/SetCardinality.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/scard + * + * @author Daniele Alessandri + */ +class SetCardinality extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SCARD'; + } +} diff --git a/vendor/predis/predis/src/Command/SetDifference.php b/vendor/predis/predis/src/Command/SetDifference.php new file mode 100644 index 000000000..35f23f98a --- /dev/null +++ b/vendor/predis/predis/src/Command/SetDifference.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/sdiff + * + * @author Daniele Alessandri + */ +class SetDifference extends SetIntersection +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SDIFF'; + } +} diff --git a/vendor/predis/predis/src/Command/SetDifferenceStore.php b/vendor/predis/predis/src/Command/SetDifferenceStore.php new file mode 100644 index 000000000..0cb78155f --- /dev/null +++ b/vendor/predis/predis/src/Command/SetDifferenceStore.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/sdiffstore + * + * @author Daniele Alessandri + */ +class SetDifferenceStore extends SetIntersectionStore +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SDIFFSTORE'; + } +} diff --git a/vendor/predis/predis/src/Command/SetIntersection.php b/vendor/predis/predis/src/Command/SetIntersection.php new file mode 100644 index 000000000..d18258fd7 --- /dev/null +++ b/vendor/predis/predis/src/Command/SetIntersection.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/sinter + * + * @author Daniele Alessandri + */ +class SetIntersection extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SINTER'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeArguments($arguments); + } +} diff --git a/vendor/predis/predis/src/Command/SetIntersectionStore.php b/vendor/predis/predis/src/Command/SetIntersectionStore.php new file mode 100644 index 000000000..b748618aa --- /dev/null +++ b/vendor/predis/predis/src/Command/SetIntersectionStore.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/sinterstore + * + * @author Daniele Alessandri + */ +class SetIntersectionStore extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SINTERSTORE'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 2 && is_array($arguments[1])) { + return array_merge(array($arguments[0]), $arguments[1]); + } + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/SetIsMember.php b/vendor/predis/predis/src/Command/SetIsMember.php new file mode 100644 index 000000000..77425224d --- /dev/null +++ b/vendor/predis/predis/src/Command/SetIsMember.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/sismember + * + * @author Daniele Alessandri + */ +class SetIsMember extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SISMEMBER'; + } +} diff --git a/vendor/predis/predis/src/Command/SetMembers.php b/vendor/predis/predis/src/Command/SetMembers.php new file mode 100644 index 000000000..f4076ae8b --- /dev/null +++ b/vendor/predis/predis/src/Command/SetMembers.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/smembers + * + * @author Daniele Alessandri + */ +class SetMembers extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SMEMBERS'; + } +} diff --git a/vendor/predis/predis/src/Command/SetMove.php b/vendor/predis/predis/src/Command/SetMove.php new file mode 100644 index 000000000..edd4e5151 --- /dev/null +++ b/vendor/predis/predis/src/Command/SetMove.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/smove + * + * @author Daniele Alessandri + */ +class SetMove extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SMOVE'; + } +} diff --git a/vendor/predis/predis/src/Command/SetPop.php b/vendor/predis/predis/src/Command/SetPop.php new file mode 100644 index 000000000..b78d3f33b --- /dev/null +++ b/vendor/predis/predis/src/Command/SetPop.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/spop + * + * @author Daniele Alessandri + */ +class SetPop extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SPOP'; + } +} diff --git a/vendor/predis/predis/src/Command/SetRandomMember.php b/vendor/predis/predis/src/Command/SetRandomMember.php new file mode 100644 index 000000000..2cb79a049 --- /dev/null +++ b/vendor/predis/predis/src/Command/SetRandomMember.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/srandmember + * + * @author Daniele Alessandri + */ +class SetRandomMember extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SRANDMEMBER'; + } +} diff --git a/vendor/predis/predis/src/Command/SetRemove.php b/vendor/predis/predis/src/Command/SetRemove.php new file mode 100644 index 000000000..b34710c62 --- /dev/null +++ b/vendor/predis/predis/src/Command/SetRemove.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/srem + * + * @author Daniele Alessandri + */ +class SetRemove extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SREM'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeVariadic($arguments); + } +} diff --git a/vendor/predis/predis/src/Command/SetScan.php b/vendor/predis/predis/src/Command/SetScan.php new file mode 100644 index 000000000..d42b28dfb --- /dev/null +++ b/vendor/predis/predis/src/Command/SetScan.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/sscan + * + * @author Daniele Alessandri + */ +class SetScan extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SSCAN'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 3 && is_array($arguments[2])) { + $options = $this->prepareOptions(array_pop($arguments)); + $arguments = array_merge($arguments, $options); + } + + return $arguments; + } + + /** + * Returns a list of options and modifiers compatible with Redis. + * + * @param array $options List of options. + * + * @return array + */ + protected function prepareOptions($options) + { + $options = array_change_key_case($options, CASE_UPPER); + $normalized = array(); + + if (!empty($options['MATCH'])) { + $normalized[] = 'MATCH'; + $normalized[] = $options['MATCH']; + } + + if (!empty($options['COUNT'])) { + $normalized[] = 'COUNT'; + $normalized[] = $options['COUNT']; + } + + return $normalized; + } +} diff --git a/vendor/predis/predis/src/Command/SetUnion.php b/vendor/predis/predis/src/Command/SetUnion.php new file mode 100644 index 000000000..7da842b45 --- /dev/null +++ b/vendor/predis/predis/src/Command/SetUnion.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/sunion + * + * @author Daniele Alessandri + */ +class SetUnion extends SetIntersection +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SUNION'; + } +} diff --git a/vendor/predis/predis/src/Command/SetUnionStore.php b/vendor/predis/predis/src/Command/SetUnionStore.php new file mode 100644 index 000000000..eac821ad7 --- /dev/null +++ b/vendor/predis/predis/src/Command/SetUnionStore.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/sunionstore + * + * @author Daniele Alessandri + */ +class SetUnionStore extends SetIntersectionStore +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SUNIONSTORE'; + } +} diff --git a/vendor/predis/predis/src/Command/StringAppend.php b/vendor/predis/predis/src/Command/StringAppend.php new file mode 100644 index 000000000..dac8b8479 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringAppend.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/append + * + * @author Daniele Alessandri + */ +class StringAppend extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'APPEND'; + } +} diff --git a/vendor/predis/predis/src/Command/StringBitCount.php b/vendor/predis/predis/src/Command/StringBitCount.php new file mode 100644 index 000000000..193cce916 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringBitCount.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/bitcount + * + * @author Daniele Alessandri + */ +class StringBitCount extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'BITCOUNT'; + } +} diff --git a/vendor/predis/predis/src/Command/StringBitField.php b/vendor/predis/predis/src/Command/StringBitField.php new file mode 100644 index 000000000..9f4deaa68 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringBitField.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/bitfield + * + * @author Daniele Alessandri + */ +class StringBitField extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'BITFIELD'; + } +} diff --git a/vendor/predis/predis/src/Command/StringBitOp.php b/vendor/predis/predis/src/Command/StringBitOp.php new file mode 100644 index 000000000..e04ee79c2 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringBitOp.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/bitop + * + * @author Daniele Alessandri + */ +class StringBitOp extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'BITOP'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 3 && is_array($arguments[2])) { + list($operation, $destination) = $arguments; + $arguments = $arguments[2]; + array_unshift($arguments, $operation, $destination); + } + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/StringBitPos.php b/vendor/predis/predis/src/Command/StringBitPos.php new file mode 100644 index 000000000..42957665d --- /dev/null +++ b/vendor/predis/predis/src/Command/StringBitPos.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/bitpos + * + * @author Daniele Alessandri + */ +class StringBitPos extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'BITPOS'; + } +} diff --git a/vendor/predis/predis/src/Command/StringDecrement.php b/vendor/predis/predis/src/Command/StringDecrement.php new file mode 100644 index 000000000..aa5808cd0 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringDecrement.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/decr + * + * @author Daniele Alessandri + */ +class StringDecrement extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'DECR'; + } +} diff --git a/vendor/predis/predis/src/Command/StringDecrementBy.php b/vendor/predis/predis/src/Command/StringDecrementBy.php new file mode 100644 index 000000000..cbf3e1124 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringDecrementBy.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/decrby + * + * @author Daniele Alessandri + */ +class StringDecrementBy extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'DECRBY'; + } +} diff --git a/vendor/predis/predis/src/Command/StringGet.php b/vendor/predis/predis/src/Command/StringGet.php new file mode 100644 index 000000000..138e915c2 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringGet.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/get + * + * @author Daniele Alessandri + */ +class StringGet extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'GET'; + } +} diff --git a/vendor/predis/predis/src/Command/StringGetBit.php b/vendor/predis/predis/src/Command/StringGetBit.php new file mode 100644 index 000000000..3c5b4f9b7 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringGetBit.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/getbit + * + * @author Daniele Alessandri + */ +class StringGetBit extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'GETBIT'; + } +} diff --git a/vendor/predis/predis/src/Command/StringGetMultiple.php b/vendor/predis/predis/src/Command/StringGetMultiple.php new file mode 100644 index 000000000..e340f9cfc --- /dev/null +++ b/vendor/predis/predis/src/Command/StringGetMultiple.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/mget + * + * @author Daniele Alessandri + */ +class StringGetMultiple extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'MGET'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeArguments($arguments); + } +} diff --git a/vendor/predis/predis/src/Command/StringGetRange.php b/vendor/predis/predis/src/Command/StringGetRange.php new file mode 100644 index 000000000..bb10565b5 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringGetRange.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/getrange + * + * @author Daniele Alessandri + */ +class StringGetRange extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'GETRANGE'; + } +} diff --git a/vendor/predis/predis/src/Command/StringGetSet.php b/vendor/predis/predis/src/Command/StringGetSet.php new file mode 100644 index 000000000..b68870d4c --- /dev/null +++ b/vendor/predis/predis/src/Command/StringGetSet.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/getset + * + * @author Daniele Alessandri + */ +class StringGetSet extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'GETSET'; + } +} diff --git a/vendor/predis/predis/src/Command/StringIncrement.php b/vendor/predis/predis/src/Command/StringIncrement.php new file mode 100644 index 000000000..fa1846e2e --- /dev/null +++ b/vendor/predis/predis/src/Command/StringIncrement.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/incr + * + * @author Daniele Alessandri + */ +class StringIncrement extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'INCR'; + } +} diff --git a/vendor/predis/predis/src/Command/StringIncrementBy.php b/vendor/predis/predis/src/Command/StringIncrementBy.php new file mode 100644 index 000000000..9d8241a25 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringIncrementBy.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/incrby + * + * @author Daniele Alessandri + */ +class StringIncrementBy extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'INCRBY'; + } +} diff --git a/vendor/predis/predis/src/Command/StringIncrementByFloat.php b/vendor/predis/predis/src/Command/StringIncrementByFloat.php new file mode 100644 index 000000000..164a0869b --- /dev/null +++ b/vendor/predis/predis/src/Command/StringIncrementByFloat.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/incrbyfloat + * + * @author Daniele Alessandri + */ +class StringIncrementByFloat extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'INCRBYFLOAT'; + } +} diff --git a/vendor/predis/predis/src/Command/StringPreciseSetExpire.php b/vendor/predis/predis/src/Command/StringPreciseSetExpire.php new file mode 100644 index 000000000..2faa954d0 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringPreciseSetExpire.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/psetex + * + * @author Daniele Alessandri + */ +class StringPreciseSetExpire extends StringSetExpire +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PSETEX'; + } +} diff --git a/vendor/predis/predis/src/Command/StringSet.php b/vendor/predis/predis/src/Command/StringSet.php new file mode 100644 index 000000000..b1469945c --- /dev/null +++ b/vendor/predis/predis/src/Command/StringSet.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/set + * + * @author Daniele Alessandri + */ +class StringSet extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SET'; + } +} diff --git a/vendor/predis/predis/src/Command/StringSetBit.php b/vendor/predis/predis/src/Command/StringSetBit.php new file mode 100644 index 000000000..7933b6be3 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringSetBit.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/setbit + * + * @author Daniele Alessandri + */ +class StringSetBit extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SETBIT'; + } +} diff --git a/vendor/predis/predis/src/Command/StringSetExpire.php b/vendor/predis/predis/src/Command/StringSetExpire.php new file mode 100644 index 000000000..f08817085 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringSetExpire.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/setex + * + * @author Daniele Alessandri + */ +class StringSetExpire extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SETEX'; + } +} diff --git a/vendor/predis/predis/src/Command/StringSetMultiple.php b/vendor/predis/predis/src/Command/StringSetMultiple.php new file mode 100644 index 000000000..a3c5324dc --- /dev/null +++ b/vendor/predis/predis/src/Command/StringSetMultiple.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/mset + * + * @author Daniele Alessandri + */ +class StringSetMultiple extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'MSET'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 1 && is_array($arguments[0])) { + $flattenedKVs = array(); + $args = $arguments[0]; + + foreach ($args as $k => $v) { + $flattenedKVs[] = $k; + $flattenedKVs[] = $v; + } + + return $flattenedKVs; + } + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/StringSetMultiplePreserve.php b/vendor/predis/predis/src/Command/StringSetMultiplePreserve.php new file mode 100644 index 000000000..b46a88c35 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringSetMultiplePreserve.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/msetnx + * + * @author Daniele Alessandri + */ +class StringSetMultiplePreserve extends StringSetMultiple +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'MSETNX'; + } +} diff --git a/vendor/predis/predis/src/Command/StringSetPreserve.php b/vendor/predis/predis/src/Command/StringSetPreserve.php new file mode 100644 index 000000000..e89c9749d --- /dev/null +++ b/vendor/predis/predis/src/Command/StringSetPreserve.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/setnx + * + * @author Daniele Alessandri + */ +class StringSetPreserve extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SETNX'; + } +} diff --git a/vendor/predis/predis/src/Command/StringSetRange.php b/vendor/predis/predis/src/Command/StringSetRange.php new file mode 100644 index 000000000..4d9389f48 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringSetRange.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/setrange + * + * @author Daniele Alessandri + */ +class StringSetRange extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SETRANGE'; + } +} diff --git a/vendor/predis/predis/src/Command/StringStrlen.php b/vendor/predis/predis/src/Command/StringStrlen.php new file mode 100644 index 000000000..10f492fd9 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringStrlen.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/strlen + * + * @author Daniele Alessandri + */ +class StringStrlen extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'STRLEN'; + } +} diff --git a/vendor/predis/predis/src/Command/StringSubstr.php b/vendor/predis/predis/src/Command/StringSubstr.php new file mode 100644 index 000000000..3aab7ade8 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringSubstr.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/substr + * + * @author Daniele Alessandri + */ +class StringSubstr extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SUBSTR'; + } +} diff --git a/vendor/predis/predis/src/Command/TransactionDiscard.php b/vendor/predis/predis/src/Command/TransactionDiscard.php new file mode 100644 index 000000000..44aca2b11 --- /dev/null +++ b/vendor/predis/predis/src/Command/TransactionDiscard.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/discard + * + * @author Daniele Alessandri + */ +class TransactionDiscard extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'DISCARD'; + } +} diff --git a/vendor/predis/predis/src/Command/TransactionExec.php b/vendor/predis/predis/src/Command/TransactionExec.php new file mode 100644 index 000000000..dbd81aae9 --- /dev/null +++ b/vendor/predis/predis/src/Command/TransactionExec.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/exec + * + * @author Daniele Alessandri + */ +class TransactionExec extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'EXEC'; + } +} diff --git a/vendor/predis/predis/src/Command/TransactionMulti.php b/vendor/predis/predis/src/Command/TransactionMulti.php new file mode 100644 index 000000000..673bf55da --- /dev/null +++ b/vendor/predis/predis/src/Command/TransactionMulti.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/multi + * + * @author Daniele Alessandri + */ +class TransactionMulti extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'MULTI'; + } +} diff --git a/vendor/predis/predis/src/Command/TransactionUnwatch.php b/vendor/predis/predis/src/Command/TransactionUnwatch.php new file mode 100644 index 000000000..792555449 --- /dev/null +++ b/vendor/predis/predis/src/Command/TransactionUnwatch.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/unwatch + * + * @author Daniele Alessandri + */ +class TransactionUnwatch extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'UNWATCH'; + } +} diff --git a/vendor/predis/predis/src/Command/TransactionWatch.php b/vendor/predis/predis/src/Command/TransactionWatch.php new file mode 100644 index 000000000..d3607801b --- /dev/null +++ b/vendor/predis/predis/src/Command/TransactionWatch.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/watch + * + * @author Daniele Alessandri + */ +class TransactionWatch extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'WATCH'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (isset($arguments[0]) && is_array($arguments[0])) { + return $arguments[0]; + } + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetAdd.php b/vendor/predis/predis/src/Command/ZSetAdd.php new file mode 100644 index 000000000..55e4729e5 --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetAdd.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zadd + * + * @author Daniele Alessandri + */ +class ZSetAdd extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZADD'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (is_array(end($arguments))) { + foreach (array_pop($arguments) as $member => $score) { + $arguments[] = $score; + $arguments[] = $member; + } + } + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetCardinality.php b/vendor/predis/predis/src/Command/ZSetCardinality.php new file mode 100644 index 000000000..10332009a --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetCardinality.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zcard + * + * @author Daniele Alessandri + */ +class ZSetCardinality extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZCARD'; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetCount.php b/vendor/predis/predis/src/Command/ZSetCount.php new file mode 100644 index 000000000..918bd2b80 --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetCount.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zcount + * + * @author Daniele Alessandri + */ +class ZSetCount extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZCOUNT'; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetIncrementBy.php b/vendor/predis/predis/src/Command/ZSetIncrementBy.php new file mode 100644 index 000000000..245a8e0f5 --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetIncrementBy.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zincrby + * + * @author Daniele Alessandri + */ +class ZSetIncrementBy extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZINCRBY'; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetIntersectionStore.php b/vendor/predis/predis/src/Command/ZSetIntersectionStore.php new file mode 100644 index 000000000..572a7a324 --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetIntersectionStore.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zinterstore + * + * @author Daniele Alessandri + */ +class ZSetIntersectionStore extends ZSetUnionStore +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZINTERSTORE'; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetLexCount.php b/vendor/predis/predis/src/Command/ZSetLexCount.php new file mode 100644 index 000000000..447b8eb32 --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetLexCount.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zlexcount + * + * @author Daniele Alessandri + */ +class ZSetLexCount extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZLEXCOUNT'; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetRange.php b/vendor/predis/predis/src/Command/ZSetRange.php new file mode 100644 index 000000000..ce72c7c07 --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetRange.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zrange + * + * @author Daniele Alessandri + */ +class ZSetRange extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZRANGE'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 4) { + $lastType = gettype($arguments[3]); + + if ($lastType === 'string' && strtoupper($arguments[3]) === 'WITHSCORES') { + // Used for compatibility with older versions + $arguments[3] = array('WITHSCORES' => true); + $lastType = 'array'; + } + + if ($lastType === 'array') { + $options = $this->prepareOptions(array_pop($arguments)); + + return array_merge($arguments, $options); + } + } + + return $arguments; + } + + /** + * Returns a list of options and modifiers compatible with Redis. + * + * @param array $options List of options. + * + * @return array + */ + protected function prepareOptions($options) + { + $opts = array_change_key_case($options, CASE_UPPER); + $finalizedOpts = array(); + + if (!empty($opts['WITHSCORES'])) { + $finalizedOpts[] = 'WITHSCORES'; + } + + return $finalizedOpts; + } + + /** + * Checks for the presence of the WITHSCORES modifier. + * + * @return bool + */ + protected function withScores() + { + $arguments = $this->getArguments(); + + if (count($arguments) < 4) { + return false; + } + + return strtoupper($arguments[3]) === 'WITHSCORES'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + if ($this->withScores()) { + $result = array(); + + for ($i = 0; $i < count($data); ++$i) { + $result[$data[$i]] = $data[++$i]; + } + + return $result; + } + + return $data; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetRangeByLex.php b/vendor/predis/predis/src/Command/ZSetRangeByLex.php new file mode 100644 index 000000000..9b2991a81 --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetRangeByLex.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zrangebylex + * + * @author Daniele Alessandri + */ +class ZSetRangeByLex extends ZSetRange +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZRANGEBYLEX'; + } + + /** + * {@inheritdoc} + */ + protected function prepareOptions($options) + { + $opts = array_change_key_case($options, CASE_UPPER); + $finalizedOpts = array(); + + if (isset($opts['LIMIT']) && is_array($opts['LIMIT'])) { + $limit = array_change_key_case($opts['LIMIT'], CASE_UPPER); + + $finalizedOpts[] = 'LIMIT'; + $finalizedOpts[] = isset($limit['OFFSET']) ? $limit['OFFSET'] : $limit[0]; + $finalizedOpts[] = isset($limit['COUNT']) ? $limit['COUNT'] : $limit[1]; + } + + return $finalizedOpts; + } + + /** + * {@inheritdoc} + */ + protected function withScores() + { + return false; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetRangeByScore.php b/vendor/predis/predis/src/Command/ZSetRangeByScore.php new file mode 100644 index 000000000..961a5bc2e --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetRangeByScore.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zrangebyscore + * + * @author Daniele Alessandri + */ +class ZSetRangeByScore extends ZSetRange +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZRANGEBYSCORE'; + } + + /** + * {@inheritdoc} + */ + protected function prepareOptions($options) + { + $opts = array_change_key_case($options, CASE_UPPER); + $finalizedOpts = array(); + + if (isset($opts['LIMIT']) && is_array($opts['LIMIT'])) { + $limit = array_change_key_case($opts['LIMIT'], CASE_UPPER); + + $finalizedOpts[] = 'LIMIT'; + $finalizedOpts[] = isset($limit['OFFSET']) ? $limit['OFFSET'] : $limit[0]; + $finalizedOpts[] = isset($limit['COUNT']) ? $limit['COUNT'] : $limit[1]; + } + + return array_merge($finalizedOpts, parent::prepareOptions($options)); + } + + /** + * {@inheritdoc} + */ + protected function withScores() + { + $arguments = $this->getArguments(); + + for ($i = 3; $i < count($arguments); ++$i) { + switch (strtoupper($arguments[$i])) { + case 'WITHSCORES': + return true; + + case 'LIMIT': + $i += 2; + break; + } + } + + return false; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetRank.php b/vendor/predis/predis/src/Command/ZSetRank.php new file mode 100644 index 000000000..d0c9c536e --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetRank.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zrank + * + * @author Daniele Alessandri + */ +class ZSetRank extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZRANK'; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetRemove.php b/vendor/predis/predis/src/Command/ZSetRemove.php new file mode 100644 index 000000000..cd8ada05c --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetRemove.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zrem + * + * @author Daniele Alessandri + */ +class ZSetRemove extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZREM'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeVariadic($arguments); + } +} diff --git a/vendor/predis/predis/src/Command/ZSetRemoveRangeByLex.php b/vendor/predis/predis/src/Command/ZSetRemoveRangeByLex.php new file mode 100644 index 000000000..9ea2d9e5b --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetRemoveRangeByLex.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zremrangebylex + * + * @author Daniele Alessandri + */ +class ZSetRemoveRangeByLex extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZREMRANGEBYLEX'; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetRemoveRangeByRank.php b/vendor/predis/predis/src/Command/ZSetRemoveRangeByRank.php new file mode 100644 index 000000000..89cd5baff --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetRemoveRangeByRank.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zremrangebyrank + * + * @author Daniele Alessandri + */ +class ZSetRemoveRangeByRank extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZREMRANGEBYRANK'; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetRemoveRangeByScore.php b/vendor/predis/predis/src/Command/ZSetRemoveRangeByScore.php new file mode 100644 index 000000000..a7c30814b --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetRemoveRangeByScore.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zremrangebyscore + * + * @author Daniele Alessandri + */ +class ZSetRemoveRangeByScore extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZREMRANGEBYSCORE'; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetReverseRange.php b/vendor/predis/predis/src/Command/ZSetReverseRange.php new file mode 100644 index 000000000..6a46a7a5a --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetReverseRange.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zrevrange + * + * @author Daniele Alessandri + */ +class ZSetReverseRange extends ZSetRange +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZREVRANGE'; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetReverseRangeByLex.php b/vendor/predis/predis/src/Command/ZSetReverseRangeByLex.php new file mode 100644 index 000000000..5dd611d91 --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetReverseRangeByLex.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zrevrangebylex + * + * @author Daniele Alessandri + */ +class ZSetReverseRangeByLex extends ZSetRangeByLex +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZREVRANGEBYLEX'; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetReverseRangeByScore.php b/vendor/predis/predis/src/Command/ZSetReverseRangeByScore.php new file mode 100644 index 000000000..1078eb72b --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetReverseRangeByScore.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zrevrangebyscore + * + * @author Daniele Alessandri + */ +class ZSetReverseRangeByScore extends ZSetRangeByScore +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZREVRANGEBYSCORE'; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetReverseRank.php b/vendor/predis/predis/src/Command/ZSetReverseRank.php new file mode 100644 index 000000000..33fb81584 --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetReverseRank.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zrevrank + * + * @author Daniele Alessandri + */ +class ZSetReverseRank extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZREVRANK'; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetScan.php b/vendor/predis/predis/src/Command/ZSetScan.php new file mode 100644 index 000000000..1dc2352ed --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetScan.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zscan + * + * @author Daniele Alessandri + */ +class ZSetScan extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZSCAN'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 3 && is_array($arguments[2])) { + $options = $this->prepareOptions(array_pop($arguments)); + $arguments = array_merge($arguments, $options); + } + + return $arguments; + } + + /** + * Returns a list of options and modifiers compatible with Redis. + * + * @param array $options List of options. + * + * @return array + */ + protected function prepareOptions($options) + { + $options = array_change_key_case($options, CASE_UPPER); + $normalized = array(); + + if (!empty($options['MATCH'])) { + $normalized[] = 'MATCH'; + $normalized[] = $options['MATCH']; + } + + if (!empty($options['COUNT'])) { + $normalized[] = 'COUNT'; + $normalized[] = $options['COUNT']; + } + + return $normalized; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + if (is_array($data)) { + $members = $data[1]; + $result = array(); + + for ($i = 0; $i < count($members); ++$i) { + $result[$members[$i]] = (float) $members[++$i]; + } + + $data[1] = $result; + } + + return $data; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetScore.php b/vendor/predis/predis/src/Command/ZSetScore.php new file mode 100644 index 000000000..2e7fce8ed --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetScore.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zscore + * + * @author Daniele Alessandri + */ +class ZSetScore extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZSCORE'; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetUnionStore.php b/vendor/predis/predis/src/Command/ZSetUnionStore.php new file mode 100644 index 000000000..befc5ce7c --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetUnionStore.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zunionstore + * + * @author Daniele Alessandri + */ +class ZSetUnionStore extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZUNIONSTORE'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + $options = array(); + $argc = count($arguments); + + if ($argc > 2 && is_array($arguments[$argc - 1])) { + $options = $this->prepareOptions(array_pop($arguments)); + } + + if (is_array($arguments[1])) { + $arguments = array_merge( + array($arguments[0], count($arguments[1])), + $arguments[1] + ); + } + + return array_merge($arguments, $options); + } + + /** + * Returns a list of options and modifiers compatible with Redis. + * + * @param array $options List of options. + * + * @return array + */ + private function prepareOptions($options) + { + $opts = array_change_key_case($options, CASE_UPPER); + $finalizedOpts = array(); + + if (isset($opts['WEIGHTS']) && is_array($opts['WEIGHTS'])) { + $finalizedOpts[] = 'WEIGHTS'; + + foreach ($opts['WEIGHTS'] as $weight) { + $finalizedOpts[] = $weight; + } + } + + if (isset($opts['AGGREGATE'])) { + $finalizedOpts[] = 'AGGREGATE'; + $finalizedOpts[] = $opts['AGGREGATE']; + } + + return $finalizedOpts; + } +} diff --git a/vendor/predis/predis/src/CommunicationException.php b/vendor/predis/predis/src/CommunicationException.php new file mode 100644 index 000000000..13fe357c3 --- /dev/null +++ b/vendor/predis/predis/src/CommunicationException.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis; + +use Predis\Connection\NodeConnectionInterface; + +/** + * Base exception class for network-related errors. + * + * @author Daniele Alessandri + */ +abstract class CommunicationException extends PredisException +{ + private $connection; + + /** + * @param NodeConnectionInterface $connection Connection that generated the exception. + * @param string $message Error message. + * @param int $code Error code. + * @param \Exception $innerException Inner exception for wrapping the original error. + */ + public function __construct( + NodeConnectionInterface $connection, + $message = null, + $code = null, + \Exception $innerException = null + ) { + parent::__construct($message, $code, $innerException); + $this->connection = $connection; + } + + /** + * Gets the connection that generated the exception. + * + * @return NodeConnectionInterface + */ + public function getConnection() + { + return $this->connection; + } + + /** + * Indicates if the receiver should reset the underlying connection. + * + * @return bool + */ + public function shouldResetConnection() + { + return true; + } + + /** + * Helper method to handle exceptions generated by a connection object. + * + * @param CommunicationException $exception Exception. + * + * @throws CommunicationException + */ + public static function handle(CommunicationException $exception) + { + if ($exception->shouldResetConnection()) { + $connection = $exception->getConnection(); + + if ($connection->isConnected()) { + $connection->disconnect(); + } + } + + throw $exception; + } +} diff --git a/vendor/predis/predis/src/Configuration/ClusterOption.php b/vendor/predis/predis/src/Configuration/ClusterOption.php new file mode 100644 index 000000000..69e36de77 --- /dev/null +++ b/vendor/predis/predis/src/Configuration/ClusterOption.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Configuration; + +use Predis\Connection\Aggregate\ClusterInterface; +use Predis\Connection\Aggregate\PredisCluster; +use Predis\Connection\Aggregate\RedisCluster; + +/** + * Configures an aggregate connection used for clustering + * multiple Redis nodes using various implementations with + * different algorithms or strategies. + * + * @author Daniele Alessandri + */ +class ClusterOption implements OptionInterface +{ + /** + * Creates a new cluster connection from on a known descriptive name. + * + * @param OptionsInterface $options Instance of the client options. + * @param string $id Descriptive identifier of the cluster type (`predis`, `redis-cluster`) + * + * @return ClusterInterface|null + */ + protected function createByDescription(OptionsInterface $options, $id) + { + switch ($id) { + case 'predis': + case 'predis-cluster': + return new PredisCluster(); + + case 'redis': + case 'redis-cluster': + return new RedisCluster($options->connections); + + default: + return; + } + } + + /** + * {@inheritdoc} + */ + public function filter(OptionsInterface $options, $value) + { + if (is_string($value)) { + $value = $this->createByDescription($options, $value); + } + + if (!$value instanceof ClusterInterface) { + throw new \InvalidArgumentException( + "An instance of type 'Predis\Connection\Aggregate\ClusterInterface' was expected." + ); + } + + return $value; + } + + /** + * {@inheritdoc} + */ + public function getDefault(OptionsInterface $options) + { + return new PredisCluster(); + } +} diff --git a/vendor/predis/predis/src/Configuration/ConnectionFactoryOption.php b/vendor/predis/predis/src/Configuration/ConnectionFactoryOption.php new file mode 100644 index 000000000..bf8479c9a --- /dev/null +++ b/vendor/predis/predis/src/Configuration/ConnectionFactoryOption.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Configuration; + +use Predis\Connection\Factory; +use Predis\Connection\FactoryInterface; + +/** + * Configures a connection factory used by the client to create new connection + * instances for single Redis nodes. + * + * @author Daniele Alessandri + */ +class ConnectionFactoryOption implements OptionInterface +{ + /** + * {@inheritdoc} + */ + public function filter(OptionsInterface $options, $value) + { + if ($value instanceof FactoryInterface) { + return $value; + } elseif (is_array($value)) { + $factory = $this->getDefault($options); + + foreach ($value as $scheme => $initializer) { + $factory->define($scheme, $initializer); + } + + return $factory; + } else { + throw new \InvalidArgumentException( + 'Invalid value provided for the connections option.' + ); + } + } + + /** + * {@inheritdoc} + */ + public function getDefault(OptionsInterface $options) + { + $factory = new Factory(); + + if ($options->defined('parameters')) { + $factory->setDefaultParameters($options->parameters); + } + + return $factory; + } +} diff --git a/vendor/predis/predis/src/Configuration/ExceptionsOption.php b/vendor/predis/predis/src/Configuration/ExceptionsOption.php new file mode 100644 index 000000000..337733e4b --- /dev/null +++ b/vendor/predis/predis/src/Configuration/ExceptionsOption.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Configuration; + +/** + * Configures whether consumers (such as the client) should throw exceptions on + * Redis errors (-ERR responses) or just return instances of error responses. + * + * @author Daniele Alessandri + */ +class ExceptionsOption implements OptionInterface +{ + /** + * {@inheritdoc} + */ + public function filter(OptionsInterface $options, $value) + { + return filter_var($value, FILTER_VALIDATE_BOOLEAN); + } + + /** + * {@inheritdoc} + */ + public function getDefault(OptionsInterface $options) + { + return true; + } +} diff --git a/vendor/predis/predis/src/Configuration/OptionInterface.php b/vendor/predis/predis/src/Configuration/OptionInterface.php new file mode 100644 index 000000000..b31e0c98f --- /dev/null +++ b/vendor/predis/predis/src/Configuration/OptionInterface.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Configuration; + +/** + * Defines an handler used by Predis\Configuration\Options to filter, validate + * or return default values for a given option. + * + * @author Daniele Alessandri + */ +interface OptionInterface +{ + /** + * Filters and validates the passed value. + * + * @param OptionsInterface $options Options container. + * @param mixed $value Input value. + * + * @return mixed + */ + public function filter(OptionsInterface $options, $value); + + /** + * Returns the default value for the option. + * + * @param OptionsInterface $options Options container. + * + * @return mixed + */ + public function getDefault(OptionsInterface $options); +} diff --git a/vendor/predis/predis/src/Configuration/Options.php b/vendor/predis/predis/src/Configuration/Options.php new file mode 100644 index 000000000..c17dd5466 --- /dev/null +++ b/vendor/predis/predis/src/Configuration/Options.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Configuration; + +/** + * Manages Predis options with filtering, conversion and lazy initialization of + * values using a mini-DI container approach. + * + * {@inheritdoc} + * + * @author Daniele Alessandri + */ +class Options implements OptionsInterface +{ + protected $input; + protected $options; + protected $handlers; + + /** + * @param array $options Array of options with their values + */ + public function __construct(array $options = array()) + { + $this->input = $options; + $this->options = array(); + $this->handlers = $this->getHandlers(); + } + + /** + * Ensures that the default options are initialized. + * + * @return array + */ + protected function getHandlers() + { + return array( + 'cluster' => 'Predis\Configuration\ClusterOption', + 'connections' => 'Predis\Configuration\ConnectionFactoryOption', + 'exceptions' => 'Predis\Configuration\ExceptionsOption', + 'prefix' => 'Predis\Configuration\PrefixOption', + 'profile' => 'Predis\Configuration\ProfileOption', + 'replication' => 'Predis\Configuration\ReplicationOption', + ); + } + + /** + * {@inheritdoc} + */ + public function getDefault($option) + { + if (isset($this->handlers[$option])) { + $handler = $this->handlers[$option]; + $handler = new $handler(); + + return $handler->getDefault($this); + } + } + + /** + * {@inheritdoc} + */ + public function defined($option) + { + return + array_key_exists($option, $this->options) || + array_key_exists($option, $this->input) + ; + } + + /** + * {@inheritdoc} + */ + public function __isset($option) + { + return ( + array_key_exists($option, $this->options) || + array_key_exists($option, $this->input) + ) && $this->__get($option) !== null; + } + + /** + * {@inheritdoc} + */ + public function __get($option) + { + if (isset($this->options[$option]) || array_key_exists($option, $this->options)) { + return $this->options[$option]; + } + + if (isset($this->input[$option]) || array_key_exists($option, $this->input)) { + $value = $this->input[$option]; + unset($this->input[$option]); + + if (is_object($value) && method_exists($value, '__invoke')) { + $value = $value($this, $option); + } + + if (isset($this->handlers[$option])) { + $handler = $this->handlers[$option]; + $handler = new $handler(); + $value = $handler->filter($this, $value); + } + + return $this->options[$option] = $value; + } + + if (isset($this->handlers[$option])) { + return $this->options[$option] = $this->getDefault($option); + } + + return; + } +} diff --git a/vendor/predis/predis/src/Configuration/OptionsInterface.php b/vendor/predis/predis/src/Configuration/OptionsInterface.php new file mode 100644 index 000000000..e0b30a492 --- /dev/null +++ b/vendor/predis/predis/src/Configuration/OptionsInterface.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Configuration; + +use Predis\Command\Processor\ProcessorInterface; +use Predis\Connection\Aggregate\ClusterInterface; +use Predis\Connection\Aggregate\ReplicationInterface; +use Predis\Connection\FactoryInterface; +use Predis\Profile\ProfileInterface; + +/** + * Interface defining a container for client options. + * + * @property-read callable $aggregate Custom connection aggregator. + * @property-read ClusterInterface $cluster Aggregate connection for clustering. + * @property-read FactoryInterface $connections Connection factory. + * @property-read bool $exceptions Toggles exceptions in client for -ERR responses. + * @property-read ProcessorInterface $prefix Key prefixing strategy using the given prefix. + * @property-read ProfileInterface $profile Server profile. + * @property-read ReplicationInterface $replication Aggregate connection for replication. + * + * @author Daniele Alessandri + */ +interface OptionsInterface +{ + /** + * Returns the default value for the given option. + * + * @param string $option Name of the option. + * + * @return mixed|null + */ + public function getDefault($option); + + /** + * Checks if the given option has been set by the user upon initialization. + * + * @param string $option Name of the option. + * + * @return bool + */ + public function defined($option); + + /** + * Checks if the given option has been set and does not evaluate to NULL. + * + * @param string $option Name of the option. + * + * @return bool + */ + public function __isset($option); + + /** + * Returns the value of the given option. + * + * @param string $option Name of the option. + * + * @return mixed|null + */ + public function __get($option); +} diff --git a/vendor/predis/predis/src/Configuration/PrefixOption.php b/vendor/predis/predis/src/Configuration/PrefixOption.php new file mode 100644 index 000000000..5827cdc37 --- /dev/null +++ b/vendor/predis/predis/src/Configuration/PrefixOption.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Configuration; + +use Predis\Command\Processor\KeyPrefixProcessor; +use Predis\Command\Processor\ProcessorInterface; + +/** + * Configures a command processor that apply the specified prefix string to a + * series of Redis commands considered prefixable. + * + * @author Daniele Alessandri + */ +class PrefixOption implements OptionInterface +{ + /** + * {@inheritdoc} + */ + public function filter(OptionsInterface $options, $value) + { + if ($value instanceof ProcessorInterface) { + return $value; + } + + return new KeyPrefixProcessor($value); + } + + /** + * {@inheritdoc} + */ + public function getDefault(OptionsInterface $options) + { + // NOOP + } +} diff --git a/vendor/predis/predis/src/Configuration/ProfileOption.php b/vendor/predis/predis/src/Configuration/ProfileOption.php new file mode 100644 index 000000000..864936e0a --- /dev/null +++ b/vendor/predis/predis/src/Configuration/ProfileOption.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Configuration; + +use Predis\Profile\Factory; +use Predis\Profile\ProfileInterface; +use Predis\Profile\RedisProfile; + +/** + * Configures the server profile to be used by the client to create command + * instances depending on the specified version of the Redis server. + * + * @author Daniele Alessandri + */ +class ProfileOption implements OptionInterface +{ + /** + * Sets the commands processors that need to be applied to the profile. + * + * @param OptionsInterface $options Client options. + * @param ProfileInterface $profile Server profile. + */ + protected function setProcessors(OptionsInterface $options, ProfileInterface $profile) + { + if (isset($options->prefix) && $profile instanceof RedisProfile) { + // NOTE: directly using __get('prefix') is actually a workaround for + // HHVM 2.3.0. It's correct and respects the options interface, it's + // just ugly. We will remove this hack when HHVM will fix re-entrant + // calls to __get() once and for all. + + $profile->setProcessor($options->__get('prefix')); + } + } + + /** + * {@inheritdoc} + */ + public function filter(OptionsInterface $options, $value) + { + if (is_string($value)) { + $value = Factory::get($value); + $this->setProcessors($options, $value); + } elseif (!$value instanceof ProfileInterface) { + throw new \InvalidArgumentException('Invalid value for the profile option.'); + } + + return $value; + } + + /** + * {@inheritdoc} + */ + public function getDefault(OptionsInterface $options) + { + $profile = Factory::getDefault(); + $this->setProcessors($options, $profile); + + return $profile; + } +} diff --git a/vendor/predis/predis/src/Configuration/ReplicationOption.php b/vendor/predis/predis/src/Configuration/ReplicationOption.php new file mode 100644 index 000000000..808bcc44e --- /dev/null +++ b/vendor/predis/predis/src/Configuration/ReplicationOption.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Configuration; + +use Predis\Connection\Aggregate\MasterSlaveReplication; +use Predis\Connection\Aggregate\ReplicationInterface; +use Predis\Connection\Aggregate\SentinelReplication; + +/** + * Configures an aggregate connection used for master/slave replication among + * multiple Redis nodes. + * + * @author Daniele Alessandri + */ +class ReplicationOption implements OptionInterface +{ + /** + * {@inheritdoc} + * + * @todo There's more code than needed due to a bug in filter_var() as + * discussed here https://bugs.php.net/bug.php?id=49510 and different + * behaviours when encountering NULL values on PHP 5.3. + */ + public function filter(OptionsInterface $options, $value) + { + if ($value instanceof ReplicationInterface) { + return $value; + } + + if ($value === 'sentinel') { + return function ($sentinels, $options) { + return new SentinelReplication($options->service, $sentinels, $options->connections); + }; + } + + if ( + !is_object($value) && + null !== $asbool = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) + ) { + if (true === $asbool) { + return $this->getDefault($options); + } else { + throw new \InvalidArgumentException( + "Values evaluating to FALSE are not accepted for `replication`" + ); + } + } + + throw new \InvalidArgumentException( + "An instance of type 'Predis\Connection\Aggregate\ReplicationInterface' was expected." + ); + } + + /** + * {@inheritdoc} + */ + public function getDefault(OptionsInterface $options) + { + $replication = new MasterSlaveReplication(); + + if ($options->autodiscovery) { + $replication->setConnectionFactory($options->connections); + $replication->setAutoDiscovery(true); + } + + return $replication; + } +} diff --git a/vendor/predis/predis/src/Connection/AbstractConnection.php b/vendor/predis/predis/src/Connection/AbstractConnection.php new file mode 100644 index 000000000..fb8651326 --- /dev/null +++ b/vendor/predis/predis/src/Connection/AbstractConnection.php @@ -0,0 +1,226 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +use Predis\Command\CommandInterface; +use Predis\CommunicationException; +use Predis\Protocol\ProtocolException; + +/** + * Base class with the common logic used by connection classes to communicate + * with Redis. + * + * @author Daniele Alessandri + */ +abstract class AbstractConnection implements NodeConnectionInterface +{ + private $resource; + private $cachedId; + + protected $parameters; + protected $initCommands = array(); + + /** + * @param ParametersInterface $parameters Initialization parameters for the connection. + */ + public function __construct(ParametersInterface $parameters) + { + $this->parameters = $this->assertParameters($parameters); + } + + /** + * Disconnects from the server and destroys the underlying resource when + * PHP's garbage collector kicks in. + */ + public function __destruct() + { + $this->disconnect(); + } + + /** + * Checks some of the parameters used to initialize the connection. + * + * @param ParametersInterface $parameters Initialization parameters for the connection. + * + * @throws \InvalidArgumentException + * + * @return ParametersInterface + */ + abstract protected function assertParameters(ParametersInterface $parameters); + + /** + * Creates the underlying resource used to communicate with Redis. + * + * @return mixed + */ + abstract protected function createResource(); + + /** + * {@inheritdoc} + */ + public function isConnected() + { + return isset($this->resource); + } + + /** + * {@inheritdoc} + */ + public function connect() + { + if (!$this->isConnected()) { + $this->resource = $this->createResource(); + + return true; + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function disconnect() + { + unset($this->resource); + } + + /** + * {@inheritdoc} + */ + public function addConnectCommand(CommandInterface $command) + { + $this->initCommands[] = $command; + } + + /** + * {@inheritdoc} + */ + public function executeCommand(CommandInterface $command) + { + $this->writeRequest($command); + + return $this->readResponse($command); + } + + /** + * {@inheritdoc} + */ + public function readResponse(CommandInterface $command) + { + return $this->read(); + } + + /** + * Helper method that returns an exception message augmented with useful + * details from the connection parameters. + * + * @param string $message Error message. + * + * @return string + */ + private function createExceptionMessage($message) + { + $parameters = $this->parameters; + + if ($parameters->scheme === 'unix') { + return "$message [$parameters->scheme:$parameters->path]"; + } + + if (filter_var($parameters->host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + return "$message [$parameters->scheme://[$parameters->host]:$parameters->port]"; + } + + return "$message [$parameters->scheme://$parameters->host:$parameters->port]"; + } + + /** + * Helper method to handle connection errors. + * + * @param string $message Error message. + * @param int $code Error code. + */ + protected function onConnectionError($message, $code = null) + { + CommunicationException::handle( + new ConnectionException($this, static::createExceptionMessage($message), $code) + ); + } + + /** + * Helper method to handle protocol errors. + * + * @param string $message Error message. + */ + protected function onProtocolError($message) + { + CommunicationException::handle( + new ProtocolException($this, static::createExceptionMessage($message)) + ); + } + + /** + * {@inheritdoc} + */ + public function getResource() + { + if (isset($this->resource)) { + return $this->resource; + } + + $this->connect(); + + return $this->resource; + } + + /** + * {@inheritdoc} + */ + public function getParameters() + { + return $this->parameters; + } + + /** + * Gets an identifier for the connection. + * + * @return string + */ + protected function getIdentifier() + { + if ($this->parameters->scheme === 'unix') { + return $this->parameters->path; + } + + return "{$this->parameters->host}:{$this->parameters->port}"; + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + if (!isset($this->cachedId)) { + $this->cachedId = $this->getIdentifier(); + } + + return $this->cachedId; + } + + /** + * {@inheritdoc} + */ + public function __sleep() + { + return array('parameters', 'initCommands'); + } +} diff --git a/vendor/predis/predis/src/Connection/Aggregate/ClusterInterface.php b/vendor/predis/predis/src/Connection/Aggregate/ClusterInterface.php new file mode 100644 index 000000000..af0f5aab5 --- /dev/null +++ b/vendor/predis/predis/src/Connection/Aggregate/ClusterInterface.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection\Aggregate; + +use Predis\Connection\AggregateConnectionInterface; + +/** + * Defines a cluster of Redis servers formed by aggregating multiple connection + * instances to single Redis nodes. + * + * @author Daniele Alessandri + */ +interface ClusterInterface extends AggregateConnectionInterface +{ +} diff --git a/vendor/predis/predis/src/Connection/Aggregate/MasterSlaveReplication.php b/vendor/predis/predis/src/Connection/Aggregate/MasterSlaveReplication.php new file mode 100644 index 000000000..238cf2cc4 --- /dev/null +++ b/vendor/predis/predis/src/Connection/Aggregate/MasterSlaveReplication.php @@ -0,0 +1,509 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection\Aggregate; + +use Predis\ClientException; +use Predis\Command\CommandInterface; +use Predis\Command\RawCommand; +use Predis\Connection\ConnectionException; +use Predis\Connection\FactoryInterface; +use Predis\Connection\NodeConnectionInterface; +use Predis\Replication\MissingMasterException; +use Predis\Replication\ReplicationStrategy; +use Predis\Response\ErrorInterface as ResponseErrorInterface; + +/** + * Aggregate connection handling replication of Redis nodes configured in a + * single master / multiple slaves setup. + * + * @author Daniele Alessandri + */ +class MasterSlaveReplication implements ReplicationInterface +{ + /** + * @var ReplicationStrategy + */ + protected $strategy; + + /** + * @var NodeConnectionInterface + */ + protected $master; + + /** + * @var NodeConnectionInterface[] + */ + protected $slaves = array(); + + /** + * @var NodeConnectionInterface + */ + protected $current; + + /** + * @var bool + */ + protected $autoDiscovery = false; + + /** + * @var FactoryInterface + */ + protected $connectionFactory; + + /** + * {@inheritdoc} + */ + public function __construct(ReplicationStrategy $strategy = null) + { + $this->strategy = $strategy ?: new ReplicationStrategy(); + } + + /** + * Configures the automatic discovery of the replication configuration on failure. + * + * @param bool $value Enable or disable auto discovery. + */ + public function setAutoDiscovery($value) + { + if (!$this->connectionFactory) { + throw new ClientException('Automatic discovery requires a connection factory'); + } + + $this->autoDiscovery = (bool) $value; + } + + /** + * Sets the connection factory used to create the connections by the auto + * discovery procedure. + * + * @param FactoryInterface $connectionFactory Connection factory instance. + */ + public function setConnectionFactory(FactoryInterface $connectionFactory) + { + $this->connectionFactory = $connectionFactory; + } + + /** + * Resets the connection state. + */ + protected function reset() + { + $this->current = null; + } + + /** + * {@inheritdoc} + */ + public function add(NodeConnectionInterface $connection) + { + $alias = $connection->getParameters()->alias; + + if ($alias === 'master') { + $this->master = $connection; + } else { + $this->slaves[$alias ?: "slave-$connection"] = $connection; + } + + $this->reset(); + } + + /** + * {@inheritdoc} + */ + public function remove(NodeConnectionInterface $connection) + { + if ($connection->getParameters()->alias === 'master') { + $this->master = null; + $this->reset(); + + return true; + } else { + if (($id = array_search($connection, $this->slaves, true)) !== false) { + unset($this->slaves[$id]); + $this->reset(); + + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function getConnection(CommandInterface $command) + { + if (!$this->current) { + if ($this->strategy->isReadOperation($command) && $slave = $this->pickSlave()) { + $this->current = $slave; + } else { + $this->current = $this->getMasterOrDie(); + } + + return $this->current; + } + + if ($this->current === $master = $this->getMasterOrDie()) { + return $master; + } + + if (!$this->strategy->isReadOperation($command) || !$this->slaves) { + $this->current = $master; + } + + return $this->current; + } + + /** + * {@inheritdoc} + */ + public function getConnectionById($connectionId) + { + if ($connectionId === 'master') { + return $this->master; + } + + if (isset($this->slaves[$connectionId])) { + return $this->slaves[$connectionId]; + } + + return; + } + + /** + * {@inheritdoc} + */ + public function switchTo($connection) + { + if (!$connection instanceof NodeConnectionInterface) { + $connection = $this->getConnectionById($connection); + } + + if (!$connection) { + throw new \InvalidArgumentException('Invalid connection or connection not found.'); + } + + if ($connection !== $this->master && !in_array($connection, $this->slaves, true)) { + throw new \InvalidArgumentException('Invalid connection or connection not found.'); + } + + $this->current = $connection; + } + + /** + * Switches to the master server. + */ + public function switchToMaster() + { + $this->switchTo('master'); + } + + /** + * Switches to a random slave server. + */ + public function switchToSlave() + { + $connection = $this->pickSlave(); + $this->switchTo($connection); + } + + /** + * {@inheritdoc} + */ + public function getCurrent() + { + return $this->current; + } + + /** + * {@inheritdoc} + */ + public function getMaster() + { + return $this->master; + } + + /** + * Returns the connection associated to the master server. + * + * @return NodeConnectionInterface + */ + private function getMasterOrDie() + { + if (!$connection = $this->getMaster()) { + throw new MissingMasterException('No master server available for replication'); + } + + return $connection; + } + + /** + * {@inheritdoc} + */ + public function getSlaves() + { + return array_values($this->slaves); + } + + /** + * Returns the underlying replication strategy. + * + * @return ReplicationStrategy + */ + public function getReplicationStrategy() + { + return $this->strategy; + } + + /** + * Returns a random slave. + * + * @return NodeConnectionInterface + */ + protected function pickSlave() + { + if ($this->slaves) { + return $this->slaves[array_rand($this->slaves)]; + } + } + + /** + * {@inheritdoc} + */ + public function isConnected() + { + return $this->current ? $this->current->isConnected() : false; + } + + /** + * {@inheritdoc} + */ + public function connect() + { + if (!$this->current) { + if (!$this->current = $this->pickSlave()) { + if (!$this->current = $this->getMaster()) { + throw new ClientException('No available connection for replication'); + } + } + } + + $this->current->connect(); + } + + /** + * {@inheritdoc} + */ + public function disconnect() + { + if ($this->master) { + $this->master->disconnect(); + } + + foreach ($this->slaves as $connection) { + $connection->disconnect(); + } + } + + /** + * Handles response from INFO. + * + * @param string $response + * + * @return array + */ + private function handleInfoResponse($response) + { + $info = array(); + + foreach (preg_split('/\r?\n/', $response) as $row) { + if (strpos($row, ':') === false) { + continue; + } + + list($k, $v) = explode(':', $row, 2); + $info[$k] = $v; + } + + return $info; + } + + /** + * Fetches the replication configuration from one of the servers. + */ + public function discover() + { + if (!$this->connectionFactory) { + throw new ClientException('Discovery requires a connection factory'); + } + + RETRY_FETCH: { + try { + if ($connection = $this->getMaster()) { + $this->discoverFromMaster($connection, $this->connectionFactory); + } elseif ($connection = $this->pickSlave()) { + $this->discoverFromSlave($connection, $this->connectionFactory); + } else { + throw new ClientException('No connection available for discovery'); + } + } catch (ConnectionException $exception) { + $this->remove($connection); + goto RETRY_FETCH; + } + } + } + + /** + * Discovers the replication configuration by contacting the master node. + * + * @param NodeConnectionInterface $connection Connection to the master node. + * @param FactoryInterface $connectionFactory Connection factory instance. + */ + protected function discoverFromMaster(NodeConnectionInterface $connection, FactoryInterface $connectionFactory) + { + $response = $connection->executeCommand(RawCommand::create('INFO', 'REPLICATION')); + $replication = $this->handleInfoResponse($response); + + if ($replication['role'] !== 'master') { + throw new ClientException("Role mismatch (expected master, got slave) [$connection]"); + } + + $this->slaves = array(); + + foreach ($replication as $k => $v) { + $parameters = null; + + if (strpos($k, 'slave') === 0 && preg_match('/ip=(?P.*),port=(?P\d+)/', $v, $parameters)) { + $slaveConnection = $connectionFactory->create(array( + 'host' => $parameters['host'], + 'port' => $parameters['port'], + )); + + $this->add($slaveConnection); + } + } + } + + /** + * Discovers the replication configuration by contacting one of the slaves. + * + * @param NodeConnectionInterface $connection Connection to one of the slaves. + * @param FactoryInterface $connectionFactory Connection factory instance. + */ + protected function discoverFromSlave(NodeConnectionInterface $connection, FactoryInterface $connectionFactory) + { + $response = $connection->executeCommand(RawCommand::create('INFO', 'REPLICATION')); + $replication = $this->handleInfoResponse($response); + + if ($replication['role'] !== 'slave') { + throw new ClientException("Role mismatch (expected slave, got master) [$connection]"); + } + + $masterConnection = $connectionFactory->create(array( + 'host' => $replication['master_host'], + 'port' => $replication['master_port'], + 'alias' => 'master', + )); + + $this->add($masterConnection); + + $this->discoverFromMaster($masterConnection, $connectionFactory); + } + + /** + * Retries the execution of a command upon slave failure. + * + * @param CommandInterface $command Command instance. + * @param string $method Actual method. + * + * @return mixed + */ + private function retryCommandOnFailure(CommandInterface $command, $method) + { + RETRY_COMMAND: { + try { + $connection = $this->getConnection($command); + $response = $connection->$method($command); + + if ($response instanceof ResponseErrorInterface && $response->getErrorType() === 'LOADING') { + throw new ConnectionException($connection, "Redis is loading the dataset in memory [$connection]"); + } + } catch (ConnectionException $exception) { + $connection = $exception->getConnection(); + $connection->disconnect(); + + if ($connection === $this->master && !$this->autoDiscovery) { + // Throw immediately when master connection is failing, even + // when the command represents a read-only operation, unless + // automatic discovery has been enabled. + throw $exception; + } else { + // Otherwise remove the failing slave and attempt to execute + // the command again on one of the remaining slaves... + $this->remove($connection); + } + + // ... that is, unless we have no more connections to use. + if (!$this->slaves && !$this->master) { + throw $exception; + } elseif ($this->autoDiscovery) { + $this->discover(); + } + + goto RETRY_COMMAND; + } catch (MissingMasterException $exception) { + if ($this->autoDiscovery) { + $this->discover(); + } else { + throw $exception; + } + + goto RETRY_COMMAND; + } + } + + return $response; + } + + /** + * {@inheritdoc} + */ + public function writeRequest(CommandInterface $command) + { + $this->retryCommandOnFailure($command, __FUNCTION__); + } + + /** + * {@inheritdoc} + */ + public function readResponse(CommandInterface $command) + { + return $this->retryCommandOnFailure($command, __FUNCTION__); + } + + /** + * {@inheritdoc} + */ + public function executeCommand(CommandInterface $command) + { + return $this->retryCommandOnFailure($command, __FUNCTION__); + } + + /** + * {@inheritdoc} + */ + public function __sleep() + { + return array('master', 'slaves', 'strategy'); + } +} diff --git a/vendor/predis/predis/src/Connection/Aggregate/PredisCluster.php b/vendor/predis/predis/src/Connection/Aggregate/PredisCluster.php new file mode 100644 index 000000000..33f98bf2e --- /dev/null +++ b/vendor/predis/predis/src/Connection/Aggregate/PredisCluster.php @@ -0,0 +1,235 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection\Aggregate; + +use Predis\Cluster\PredisStrategy; +use Predis\Cluster\StrategyInterface; +use Predis\Command\CommandInterface; +use Predis\Connection\NodeConnectionInterface; +use Predis\NotSupportedException; + +/** + * Abstraction for a cluster of aggregate connections to various Redis servers + * implementing client-side sharding based on pluggable distribution strategies. + * + * @author Daniele Alessandri + * + * @todo Add the ability to remove connections from pool. + */ +class PredisCluster implements ClusterInterface, \IteratorAggregate, \Countable +{ + private $pool; + private $strategy; + private $distributor; + + /** + * @param StrategyInterface $strategy Optional cluster strategy. + */ + public function __construct(StrategyInterface $strategy = null) + { + $this->pool = array(); + $this->strategy = $strategy ?: new PredisStrategy(); + $this->distributor = $this->strategy->getDistributor(); + } + + /** + * {@inheritdoc} + */ + public function isConnected() + { + foreach ($this->pool as $connection) { + if ($connection->isConnected()) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function connect() + { + foreach ($this->pool as $connection) { + $connection->connect(); + } + } + + /** + * {@inheritdoc} + */ + public function disconnect() + { + foreach ($this->pool as $connection) { + $connection->disconnect(); + } + } + + /** + * {@inheritdoc} + */ + public function add(NodeConnectionInterface $connection) + { + $parameters = $connection->getParameters(); + + if (isset($parameters->alias)) { + $this->pool[$parameters->alias] = $connection; + } else { + $this->pool[] = $connection; + } + + $weight = isset($parameters->weight) ? $parameters->weight : null; + $this->distributor->add($connection, $weight); + } + + /** + * {@inheritdoc} + */ + public function remove(NodeConnectionInterface $connection) + { + if (($id = array_search($connection, $this->pool, true)) !== false) { + unset($this->pool[$id]); + $this->distributor->remove($connection); + + return true; + } + + return false; + } + + /** + * Removes a connection instance using its alias or index. + * + * @param string $connectionID Alias or index of a connection. + * + * @return bool Returns true if the connection was in the pool. + */ + public function removeById($connectionID) + { + if ($connection = $this->getConnectionById($connectionID)) { + return $this->remove($connection); + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function getConnection(CommandInterface $command) + { + $slot = $this->strategy->getSlot($command); + + if (!isset($slot)) { + throw new NotSupportedException( + "Cannot use '{$command->getId()}' over clusters of connections." + ); + } + + $node = $this->distributor->getBySlot($slot); + + return $node; + } + + /** + * {@inheritdoc} + */ + public function getConnectionById($connectionID) + { + return isset($this->pool[$connectionID]) ? $this->pool[$connectionID] : null; + } + + /** + * Retrieves a connection instance from the cluster using a key. + * + * @param string $key Key string. + * + * @return NodeConnectionInterface + */ + public function getConnectionByKey($key) + { + $hash = $this->strategy->getSlotByKey($key); + $node = $this->distributor->getBySlot($hash); + + return $node; + } + + /** + * Returns the underlying command hash strategy used to hash commands by + * using keys found in their arguments. + * + * @return StrategyInterface + */ + public function getClusterStrategy() + { + return $this->strategy; + } + + /** + * {@inheritdoc} + */ + public function count() + { + return count($this->pool); + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + return new \ArrayIterator($this->pool); + } + + /** + * {@inheritdoc} + */ + public function writeRequest(CommandInterface $command) + { + $this->getConnection($command)->writeRequest($command); + } + + /** + * {@inheritdoc} + */ + public function readResponse(CommandInterface $command) + { + return $this->getConnection($command)->readResponse($command); + } + + /** + * {@inheritdoc} + */ + public function executeCommand(CommandInterface $command) + { + return $this->getConnection($command)->executeCommand($command); + } + + /** + * Executes the specified Redis command on all the nodes of a cluster. + * + * @param CommandInterface $command A Redis command. + * + * @return array + */ + public function executeCommandOnNodes(CommandInterface $command) + { + $responses = array(); + + foreach ($this->pool as $connection) { + $responses[] = $connection->executeCommand($command); + } + + return $responses; + } +} diff --git a/vendor/predis/predis/src/Connection/Aggregate/RedisCluster.php b/vendor/predis/predis/src/Connection/Aggregate/RedisCluster.php new file mode 100644 index 000000000..c749cc838 --- /dev/null +++ b/vendor/predis/predis/src/Connection/Aggregate/RedisCluster.php @@ -0,0 +1,673 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection\Aggregate; + +use Predis\ClientException; +use Predis\Cluster\RedisStrategy as RedisClusterStrategy; +use Predis\Cluster\StrategyInterface; +use Predis\Command\CommandInterface; +use Predis\Command\RawCommand; +use Predis\Connection\ConnectionException; +use Predis\Connection\FactoryInterface; +use Predis\Connection\NodeConnectionInterface; +use Predis\NotSupportedException; +use Predis\Response\ErrorInterface as ErrorResponseInterface; + +/** + * Abstraction for a Redis-backed cluster of nodes (Redis >= 3.0.0). + * + * This connection backend offers smart support for redis-cluster by handling + * automatic slots map (re)generation upon -MOVED or -ASK responses returned by + * Redis when redirecting a client to a different node. + * + * The cluster can be pre-initialized using only a subset of the actual nodes in + * the cluster, Predis will do the rest by adjusting the slots map and creating + * the missing underlying connection instances on the fly. + * + * It is possible to pre-associate connections to a slots range with the "slots" + * parameter in the form "$first-$last". This can greatly reduce runtime node + * guessing and redirections. + * + * It is also possible to ask for the full and updated slots map directly to one + * of the nodes and optionally enable such a behaviour upon -MOVED redirections. + * Asking for the cluster configuration to Redis is actually done by issuing a + * CLUSTER SLOTS command to a random node in the pool. + * + * @author Daniele Alessandri + */ +class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable +{ + private $useClusterSlots = true; + private $pool = array(); + private $slots = array(); + private $slotsMap; + private $strategy; + private $connections; + private $retryLimit = 5; + + /** + * @param FactoryInterface $connections Optional connection factory. + * @param StrategyInterface $strategy Optional cluster strategy. + */ + public function __construct( + FactoryInterface $connections, + StrategyInterface $strategy = null + ) { + $this->connections = $connections; + $this->strategy = $strategy ?: new RedisClusterStrategy(); + } + + /** + * Sets the maximum number of retries for commands upon server failure. + * + * -1 = unlimited retry attempts + * 0 = no retry attempts (fails immediatly) + * n = fail only after n retry attempts + * + * @param int $retry Number of retry attempts. + */ + public function setRetryLimit($retry) + { + $this->retryLimit = (int) $retry; + } + + /** + * {@inheritdoc} + */ + public function isConnected() + { + foreach ($this->pool as $connection) { + if ($connection->isConnected()) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function connect() + { + if ($connection = $this->getRandomConnection()) { + $connection->connect(); + } + } + + /** + * {@inheritdoc} + */ + public function disconnect() + { + foreach ($this->pool as $connection) { + $connection->disconnect(); + } + } + + /** + * {@inheritdoc} + */ + public function add(NodeConnectionInterface $connection) + { + $this->pool[(string) $connection] = $connection; + unset($this->slotsMap); + } + + /** + * {@inheritdoc} + */ + public function remove(NodeConnectionInterface $connection) + { + if (false !== $id = array_search($connection, $this->pool, true)) { + unset( + $this->pool[$id], + $this->slotsMap + ); + + $this->slots = array_diff($this->slots, array($connection)); + + return true; + } + + return false; + } + + /** + * Removes a connection instance by using its identifier. + * + * @param string $connectionID Connection identifier. + * + * @return bool True if the connection was in the pool. + */ + public function removeById($connectionID) + { + if (isset($this->pool[$connectionID])) { + unset( + $this->pool[$connectionID], + $this->slotsMap + ); + + return true; + } + + return false; + } + + /** + * Generates the current slots map by guessing the cluster configuration out + * of the connection parameters of the connections in the pool. + * + * Generation is based on the same algorithm used by Redis to generate the + * cluster, so it is most effective when all of the connections supplied on + * initialization have the "slots" parameter properly set accordingly to the + * current cluster configuration. + * + * @return array + */ + public function buildSlotsMap() + { + $this->slotsMap = array(); + + foreach ($this->pool as $connectionID => $connection) { + $parameters = $connection->getParameters(); + + if (!isset($parameters->slots)) { + continue; + } + + foreach (explode(',', $parameters->slots) as $slotRange) { + $slots = explode('-', $slotRange, 2); + + if (!isset($slots[1])) { + $slots[1] = $slots[0]; + } + + $this->setSlots($slots[0], $slots[1], $connectionID); + } + } + + return $this->slotsMap; + } + + /** + * Queries the specified node of the cluster to fetch the updated slots map. + * + * When the connection fails, this method tries to execute the same command + * on a different connection picked at random from the pool of known nodes, + * up until the retry limit is reached. + * + * @param NodeConnectionInterface $connection Connection to a node of the cluster. + * + * @return mixed + */ + private function queryClusterNodeForSlotsMap(NodeConnectionInterface $connection) + { + $retries = 0; + $command = RawCommand::create('CLUSTER', 'SLOTS'); + + RETRY_COMMAND: { + try { + $response = $connection->executeCommand($command); + } catch (ConnectionException $exception) { + $connection = $exception->getConnection(); + $connection->disconnect(); + + $this->remove($connection); + + if ($retries === $this->retryLimit) { + throw $exception; + } + + if (!$connection = $this->getRandomConnection()) { + throw new ClientException('No connections left in the pool for `CLUSTER SLOTS`'); + } + + ++$retries; + goto RETRY_COMMAND; + } + } + + return $response; + } + + /** + * Generates an updated slots map fetching the cluster configuration using + * the CLUSTER SLOTS command against the specified node or a random one from + * the pool. + * + * @param NodeConnectionInterface $connection Optional connection instance. + * + * @return array + */ + public function askSlotsMap(NodeConnectionInterface $connection = null) + { + if (!$connection && !$connection = $this->getRandomConnection()) { + return array(); + } + + $this->resetSlotsMap(); + + $response = $this->queryClusterNodeForSlotsMap($connection); + + foreach ($response as $slots) { + // We only support master servers for now, so we ignore subsequent + // elements in the $slots array identifying slaves. + list($start, $end, $master) = $slots; + + if ($master[0] === '') { + $this->setSlots($start, $end, (string) $connection); + } else { + $this->setSlots($start, $end, "{$master[0]}:{$master[1]}"); + } + } + + return $this->slotsMap; + } + + /** + * Resets the slots map cache. + */ + public function resetSlotsMap() + { + $this->slotsMap = array(); + } + + /** + * Returns the current slots map for the cluster. + * + * The order of the returned $slot => $server dictionary is not guaranteed. + * + * @return array + */ + public function getSlotsMap() + { + if (!isset($this->slotsMap)) { + $this->slotsMap = array(); + } + + return $this->slotsMap; + } + + /** + * Pre-associates a connection to a slots range to avoid runtime guessing. + * + * @param int $first Initial slot of the range. + * @param int $last Last slot of the range. + * @param NodeConnectionInterface|string $connection ID or connection instance. + * + * @throws \OutOfBoundsException + */ + public function setSlots($first, $last, $connection) + { + if ($first < 0x0000 || $first > 0x3FFF || + $last < 0x0000 || $last > 0x3FFF || + $last < $first + ) { + throw new \OutOfBoundsException( + "Invalid slot range for $connection: [$first-$last]." + ); + } + + $slots = array_fill($first, $last - $first + 1, (string) $connection); + $this->slotsMap = $this->getSlotsMap() + $slots; + } + + /** + * Guesses the correct node associated to a given slot using a precalculated + * slots map, falling back to the same logic used by Redis to initialize a + * cluster (best-effort). + * + * @param int $slot Slot index. + * + * @return string Connection ID. + */ + protected function guessNode($slot) + { + if (!$this->pool) { + throw new ClientException('No connections available in the pool'); + } + + if (!isset($this->slotsMap)) { + $this->buildSlotsMap(); + } + + if (isset($this->slotsMap[$slot])) { + return $this->slotsMap[$slot]; + } + + $count = count($this->pool); + $index = min((int) ($slot / (int) (16384 / $count)), $count - 1); + $nodes = array_keys($this->pool); + + return $nodes[$index]; + } + + /** + * Creates a new connection instance from the given connection ID. + * + * @param string $connectionID Identifier for the connection. + * + * @return NodeConnectionInterface + */ + protected function createConnection($connectionID) + { + $separator = strrpos($connectionID, ':'); + + return $this->connections->create(array( + 'host' => substr($connectionID, 0, $separator), + 'port' => substr($connectionID, $separator + 1), + )); + } + + /** + * {@inheritdoc} + */ + public function getConnection(CommandInterface $command) + { + $slot = $this->strategy->getSlot($command); + + if (!isset($slot)) { + throw new NotSupportedException( + "Cannot use '{$command->getId()}' with redis-cluster." + ); + } + + if (isset($this->slots[$slot])) { + return $this->slots[$slot]; + } else { + return $this->getConnectionBySlot($slot); + } + } + + /** + * Returns the connection currently associated to a given slot. + * + * @param int $slot Slot index. + * + * @throws \OutOfBoundsException + * + * @return NodeConnectionInterface + */ + public function getConnectionBySlot($slot) + { + if ($slot < 0x0000 || $slot > 0x3FFF) { + throw new \OutOfBoundsException("Invalid slot [$slot]."); + } + + if (isset($this->slots[$slot])) { + return $this->slots[$slot]; + } + + $connectionID = $this->guessNode($slot); + + if (!$connection = $this->getConnectionById($connectionID)) { + $connection = $this->createConnection($connectionID); + $this->pool[$connectionID] = $connection; + } + + return $this->slots[$slot] = $connection; + } + + /** + * {@inheritdoc} + */ + public function getConnectionById($connectionID) + { + if (isset($this->pool[$connectionID])) { + return $this->pool[$connectionID]; + } + } + + /** + * Returns a random connection from the pool. + * + * @return NodeConnectionInterface|null + */ + protected function getRandomConnection() + { + if ($this->pool) { + return $this->pool[array_rand($this->pool)]; + } + } + + /** + * Permanently associates the connection instance to a new slot. + * The connection is added to the connections pool if not yet included. + * + * @param NodeConnectionInterface $connection Connection instance. + * @param int $slot Target slot index. + */ + protected function move(NodeConnectionInterface $connection, $slot) + { + $this->pool[(string) $connection] = $connection; + $this->slots[(int) $slot] = $connection; + } + + /** + * Handles -ERR responses returned by Redis. + * + * @param CommandInterface $command Command that generated the -ERR response. + * @param ErrorResponseInterface $error Redis error response object. + * + * @return mixed + */ + protected function onErrorResponse(CommandInterface $command, ErrorResponseInterface $error) + { + $details = explode(' ', $error->getMessage(), 2); + + switch ($details[0]) { + case 'MOVED': + return $this->onMovedResponse($command, $details[1]); + + case 'ASK': + return $this->onAskResponse($command, $details[1]); + + default: + return $error; + } + } + + /** + * Handles -MOVED responses by executing again the command against the node + * indicated by the Redis response. + * + * @param CommandInterface $command Command that generated the -MOVED response. + * @param string $details Parameters of the -MOVED response. + * + * @return mixed + */ + protected function onMovedResponse(CommandInterface $command, $details) + { + list($slot, $connectionID) = explode(' ', $details, 2); + + if (!$connection = $this->getConnectionById($connectionID)) { + $connection = $this->createConnection($connectionID); + } + + if ($this->useClusterSlots) { + $this->askSlotsMap($connection); + } + + $this->move($connection, $slot); + $response = $this->executeCommand($command); + + return $response; + } + + /** + * Handles -ASK responses by executing again the command against the node + * indicated by the Redis response. + * + * @param CommandInterface $command Command that generated the -ASK response. + * @param string $details Parameters of the -ASK response. + * + * @return mixed + */ + protected function onAskResponse(CommandInterface $command, $details) + { + list($slot, $connectionID) = explode(' ', $details, 2); + + if (!$connection = $this->getConnectionById($connectionID)) { + $connection = $this->createConnection($connectionID); + } + + $connection->executeCommand(RawCommand::create('ASKING')); + $response = $connection->executeCommand($command); + + return $response; + } + + /** + * Ensures that a command is executed one more time on connection failure. + * + * The connection to the node that generated the error is evicted from the + * pool before trying to fetch an updated slots map from another node. If + * the new slots map points to an unreachable server the client gives up and + * throws the exception as the nodes participating in the cluster may still + * have to agree that something changed in the configuration of the cluster. + * + * @param CommandInterface $command Command instance. + * @param string $method Actual method. + * + * @return mixed + */ + private function retryCommandOnFailure(CommandInterface $command, $method) + { + $failure = false; + + RETRY_COMMAND: { + try { + $response = $this->getConnection($command)->$method($command); + } catch (ConnectionException $exception) { + $connection = $exception->getConnection(); + $connection->disconnect(); + + $this->remove($connection); + + if ($failure) { + throw $exception; + } elseif ($this->useClusterSlots) { + $this->askSlotsMap(); + } + + $failure = true; + + goto RETRY_COMMAND; + } + } + + return $response; + } + + /** + * {@inheritdoc} + */ + public function writeRequest(CommandInterface $command) + { + $this->retryCommandOnFailure($command, __FUNCTION__); + } + + /** + * {@inheritdoc} + */ + public function readResponse(CommandInterface $command) + { + return $this->retryCommandOnFailure($command, __FUNCTION__); + } + + /** + * {@inheritdoc} + */ + public function executeCommand(CommandInterface $command) + { + $response = $this->retryCommandOnFailure($command, __FUNCTION__); + + if ($response instanceof ErrorResponseInterface) { + return $this->onErrorResponse($command, $response); + } + + return $response; + } + + /** + * {@inheritdoc} + */ + public function count() + { + return count($this->pool); + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + if ($this->useClusterSlots) { + $slotsmap = $this->getSlotsMap() ?: $this->askSlotsMap(); + } else { + $slotsmap = $this->getSlotsMap() ?: $this->buildSlotsMap(); + } + + $connections = array(); + + foreach (array_unique($slotsmap) as $node) { + if (!$connection = $this->getConnectionById($node)) { + $this->add($connection = $this->createConnection($node)); + } + + $connections[] = $connection; + } + + return new \ArrayIterator($connections); + } + + /** + * Returns the underlying command hash strategy used to hash commands by + * using keys found in their arguments. + * + * @return StrategyInterface + */ + public function getClusterStrategy() + { + return $this->strategy; + } + + /** + * Returns the underlying connection factory used to create new connection + * instances to Redis nodes indicated by redis-cluster. + * + * @return FactoryInterface + */ + public function getConnectionFactory() + { + return $this->connections; + } + + /** + * Enables automatic fetching of the current slots map from one of the nodes + * using the CLUSTER SLOTS command. This option is enabled by default as + * asking the current slots map to Redis upon -MOVED responses may reduce + * overhead by eliminating the trial-and-error nature of the node guessing + * procedure, mostly when targeting many keys that would end up in a lot of + * redirections. + * + * The slots map can still be manually fetched using the askSlotsMap() + * method whether or not this option is enabled. + * + * @param bool $value Enable or disable the use of CLUSTER SLOTS. + */ + public function useClusterSlots($value) + { + $this->useClusterSlots = (bool) $value; + } +} diff --git a/vendor/predis/predis/src/Connection/Aggregate/ReplicationInterface.php b/vendor/predis/predis/src/Connection/Aggregate/ReplicationInterface.php new file mode 100644 index 000000000..e09e8265c --- /dev/null +++ b/vendor/predis/predis/src/Connection/Aggregate/ReplicationInterface.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection\Aggregate; + +use Predis\Connection\AggregateConnectionInterface; +use Predis\Connection\NodeConnectionInterface; + +/** + * Defines a group of Redis nodes in a master / slave replication setup. + * + * @author Daniele Alessandri + */ +interface ReplicationInterface extends AggregateConnectionInterface +{ + /** + * Switches the internal connection instance in use. + * + * @param string $connection Alias of a connection + */ + public function switchTo($connection); + + /** + * Returns the connection instance currently in use by the aggregate + * connection. + * + * @return NodeConnectionInterface + */ + public function getCurrent(); + + /** + * Returns the connection instance for the master Redis node. + * + * @return NodeConnectionInterface + */ + public function getMaster(); + + /** + * Returns a list of connection instances to slave nodes. + * + * @return NodeConnectionInterface + */ + public function getSlaves(); +} diff --git a/vendor/predis/predis/src/Connection/Aggregate/SentinelReplication.php b/vendor/predis/predis/src/Connection/Aggregate/SentinelReplication.php new file mode 100644 index 000000000..0078fed7f --- /dev/null +++ b/vendor/predis/predis/src/Connection/Aggregate/SentinelReplication.php @@ -0,0 +1,727 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection\Aggregate; + +use Predis\Command\CommandInterface; +use Predis\Command\RawCommand; +use Predis\CommunicationException; +use Predis\Connection\ConnectionException; +use Predis\Connection\FactoryInterface as ConnectionFactoryInterface; +use Predis\Connection\NodeConnectionInterface; +use Predis\Connection\Parameters; +use Predis\Replication\ReplicationStrategy; +use Predis\Replication\RoleException; +use Predis\Response\ErrorInterface as ErrorResponseInterface; +use Predis\Response\ServerException; + +/** + * @author Daniele Alessandri + * @author Ville Mattila + */ +class SentinelReplication implements ReplicationInterface +{ + /** + * @var NodeConnectionInterface + */ + protected $master; + + /** + * @var NodeConnectionInterface[] + */ + protected $slaves = array(); + + /** + * @var NodeConnectionInterface + */ + protected $current; + + /** + * @var string + */ + protected $service; + + /** + * @var ConnectionFactoryInterface + */ + protected $connectionFactory; + + /** + * @var ReplicationStrategy + */ + protected $strategy; + + /** + * @var NodeConnectionInterface[] + */ + protected $sentinels = array(); + + /** + * @var NodeConnectionInterface + */ + protected $sentinelConnection; + + /** + * @var float + */ + protected $sentinelTimeout = 0.100; + + /** + * Max number of automatic retries of commands upon server failure. + * + * -1 = unlimited retry attempts + * 0 = no retry attempts (fails immediatly) + * n = fail only after n retry attempts + * + * @var int + */ + protected $retryLimit = 20; + + /** + * Time to wait in milliseconds before fetching a new configuration from one + * of the sentinel servers. + * + * @var int + */ + protected $retryWait = 1000; + + /** + * Flag for automatic fetching of available sentinels. + * + * @var bool + */ + protected $updateSentinels = false; + + /** + * @param string $service Name of the service for autodiscovery. + * @param array $sentinels Sentinel servers connection parameters. + * @param ConnectionFactoryInterface $connectionFactory Connection factory instance. + * @param ReplicationStrategy $strategy Replication strategy instance. + */ + public function __construct( + $service, + array $sentinels, + ConnectionFactoryInterface $connectionFactory, + ReplicationStrategy $strategy = null + ) { + $this->sentinels = $sentinels; + $this->service = $service; + $this->connectionFactory = $connectionFactory; + $this->strategy = $strategy ?: new ReplicationStrategy(); + } + + /** + * Sets a default timeout for connections to sentinels. + * + * When "timeout" is present in the connection parameters of sentinels, its + * value overrides the default sentinel timeout. + * + * @param float $timeout Timeout value. + */ + public function setSentinelTimeout($timeout) + { + $this->sentinelTimeout = (float) $timeout; + } + + /** + * Sets the maximum number of retries for commands upon server failure. + * + * -1 = unlimited retry attempts + * 0 = no retry attempts (fails immediatly) + * n = fail only after n retry attempts + * + * @param int $retry Number of retry attempts. + */ + public function setRetryLimit($retry) + { + $this->retryLimit = (int) $retry; + } + + /** + * Sets the time to wait (in seconds) before fetching a new configuration + * from one of the sentinels. + * + * @param float $seconds Time to wait before the next attempt. + */ + public function setRetryWait($seconds) + { + $this->retryWait = (float) $seconds; + } + + /** + * Set automatic fetching of available sentinels. + * + * @param bool $update Enable or disable automatic updates. + */ + public function setUpdateSentinels($update) + { + $this->updateSentinels = (bool) $update; + } + + /** + * Resets the current connection. + */ + protected function reset() + { + $this->current = null; + } + + /** + * Wipes the current list of master and slaves nodes. + */ + protected function wipeServerList() + { + $this->reset(); + + $this->master = null; + $this->slaves = array(); + } + + /** + * {@inheritdoc} + */ + public function add(NodeConnectionInterface $connection) + { + $alias = $connection->getParameters()->alias; + + if ($alias === 'master') { + $this->master = $connection; + } else { + $this->slaves[$alias ?: count($this->slaves)] = $connection; + } + + $this->reset(); + } + + /** + * {@inheritdoc} + */ + public function remove(NodeConnectionInterface $connection) + { + if ($connection === $this->master) { + $this->master = null; + $this->reset(); + + return true; + } + + if (false !== $id = array_search($connection, $this->slaves, true)) { + unset($this->slaves[$id]); + $this->reset(); + + return true; + } + + return false; + } + + /** + * Creates a new connection to a sentinel server. + * + * @return NodeConnectionInterface + */ + protected function createSentinelConnection($parameters) + { + if ($parameters instanceof NodeConnectionInterface) { + return $parameters; + } + + if (is_string($parameters)) { + $parameters = Parameters::parse($parameters); + } + + if (is_array($parameters)) { + // NOTE: sentinels do not accept AUTH and SELECT commands so we must + // explicitly set them to NULL to avoid problems when using default + // parameters set via client options. Actually AUTH is supported for + // sentinels starting with Redis 5 but we have to differentiate from + // sentinels passwords and nodes passwords, this will be implemented + // in a later release. + $parameters['database'] = null; + $parameters['username'] = null; + $parameters['password'] = null; + + if (!isset($parameters['timeout'])) { + $parameters['timeout'] = $this->sentinelTimeout; + } + } + + $connection = $this->connectionFactory->create($parameters); + + return $connection; + } + + /** + * Returns the current sentinel connection. + * + * If there is no active sentinel connection, a new connection is created. + * + * @return NodeConnectionInterface + */ + public function getSentinelConnection() + { + if (!$this->sentinelConnection) { + if (!$this->sentinels) { + throw new \Predis\ClientException('No sentinel server available for autodiscovery.'); + } + + $sentinel = array_shift($this->sentinels); + $this->sentinelConnection = $this->createSentinelConnection($sentinel); + } + + return $this->sentinelConnection; + } + + /** + * Fetches an updated list of sentinels from a sentinel. + */ + public function updateSentinels() + { + SENTINEL_QUERY: { + $sentinel = $this->getSentinelConnection(); + + try { + $payload = $sentinel->executeCommand( + RawCommand::create('SENTINEL', 'sentinels', $this->service) + ); + + $this->sentinels = array(); + // NOTE: sentinel server does not return itself, so we add it back. + $this->sentinels[] = $sentinel->getParameters()->toArray(); + + foreach ($payload as $sentinel) { + $this->sentinels[] = array( + 'host' => $sentinel[3], + 'port' => $sentinel[5], + ); + } + } catch (ConnectionException $exception) { + $this->sentinelConnection = null; + + goto SENTINEL_QUERY; + } + } + } + + /** + * Fetches the details for the master and slave servers from a sentinel. + */ + public function querySentinel() + { + $this->wipeServerList(); + + $this->updateSentinels(); + $this->getMaster(); + $this->getSlaves(); + } + + /** + * Handles error responses returned by redis-sentinel. + * + * @param NodeConnectionInterface $sentinel Connection to a sentinel server. + * @param ErrorResponseInterface $error Error response. + */ + private function handleSentinelErrorResponse(NodeConnectionInterface $sentinel, ErrorResponseInterface $error) + { + if ($error->getErrorType() === 'IDONTKNOW') { + throw new ConnectionException($sentinel, $error->getMessage()); + } else { + throw new ServerException($error->getMessage()); + } + } + + /** + * Fetches the details for the master server from a sentinel. + * + * @param NodeConnectionInterface $sentinel Connection to a sentinel server. + * @param string $service Name of the service. + * + * @return array + */ + protected function querySentinelForMaster(NodeConnectionInterface $sentinel, $service) + { + $payload = $sentinel->executeCommand( + RawCommand::create('SENTINEL', 'get-master-addr-by-name', $service) + ); + + if ($payload === null) { + throw new ServerException('ERR No such master with that name'); + } + + if ($payload instanceof ErrorResponseInterface) { + $this->handleSentinelErrorResponse($sentinel, $payload); + } + + return array( + 'host' => $payload[0], + 'port' => $payload[1], + 'alias' => 'master', + ); + } + + /** + * Fetches the details for the slave servers from a sentinel. + * + * @param NodeConnectionInterface $sentinel Connection to a sentinel server. + * @param string $service Name of the service. + * + * @return array + */ + protected function querySentinelForSlaves(NodeConnectionInterface $sentinel, $service) + { + $slaves = array(); + + $payload = $sentinel->executeCommand( + RawCommand::create('SENTINEL', 'slaves', $service) + ); + + if ($payload instanceof ErrorResponseInterface) { + $this->handleSentinelErrorResponse($sentinel, $payload); + } + + foreach ($payload as $slave) { + $flags = explode(',', $slave[9]); + + if (array_intersect($flags, array('s_down', 'o_down', 'disconnected'))) { + continue; + } + + $slaves[] = array( + 'host' => $slave[3], + 'port' => $slave[5], + 'alias' => "slave-$slave[1]", + ); + } + + return $slaves; + } + + /** + * {@inheritdoc} + */ + public function getCurrent() + { + return $this->current; + } + + /** + * {@inheritdoc} + */ + public function getMaster() + { + if ($this->master) { + return $this->master; + } + + if ($this->updateSentinels) { + $this->updateSentinels(); + } + + SENTINEL_QUERY: { + $sentinel = $this->getSentinelConnection(); + + try { + $masterParameters = $this->querySentinelForMaster($sentinel, $this->service); + $masterConnection = $this->connectionFactory->create($masterParameters); + + $this->add($masterConnection); + } catch (ConnectionException $exception) { + $this->sentinelConnection = null; + + goto SENTINEL_QUERY; + } + } + + return $masterConnection; + } + + /** + * {@inheritdoc} + */ + public function getSlaves() + { + if ($this->slaves) { + return array_values($this->slaves); + } + + if ($this->updateSentinels) { + $this->updateSentinels(); + } + + SENTINEL_QUERY: { + $sentinel = $this->getSentinelConnection(); + + try { + $slavesParameters = $this->querySentinelForSlaves($sentinel, $this->service); + + foreach ($slavesParameters as $slaveParameters) { + $this->add($this->connectionFactory->create($slaveParameters)); + } + } catch (ConnectionException $exception) { + $this->sentinelConnection = null; + + goto SENTINEL_QUERY; + } + } + + return array_values($this->slaves ?: array()); + } + + /** + * Returns a random slave. + * + * @return NodeConnectionInterface + */ + protected function pickSlave() + { + if ($slaves = $this->getSlaves()) { + return $slaves[rand(1, count($slaves)) - 1]; + } + } + + /** + * Returns the connection instance in charge for the given command. + * + * @param CommandInterface $command Command instance. + * + * @return NodeConnectionInterface + */ + private function getConnectionInternal(CommandInterface $command) + { + if (!$this->current) { + if ($this->strategy->isReadOperation($command) && $slave = $this->pickSlave()) { + $this->current = $slave; + } else { + $this->current = $this->getMaster(); + } + + return $this->current; + } + + if ($this->current === $this->master) { + return $this->current; + } + + if (!$this->strategy->isReadOperation($command)) { + $this->current = $this->getMaster(); + } + + return $this->current; + } + + /** + * Asserts that the specified connection matches an expected role. + * + * @param NodeConnectionInterface $connection Connection to a redis server. + * @param string $role Expected role of the server ("master", "slave" or "sentinel"). + * + * @throws RoleException + */ + protected function assertConnectionRole(NodeConnectionInterface $connection, $role) + { + $role = strtolower($role); + $actualRole = $connection->executeCommand(RawCommand::create('ROLE')); + + if ($role !== $actualRole[0]) { + throw new RoleException($connection, "Expected $role but got $actualRole[0] [$connection]"); + } + } + + /** + * {@inheritdoc} + */ + public function getConnection(CommandInterface $command) + { + $connection = $this->getConnectionInternal($command); + + if (!$connection->isConnected()) { + // When we do not have any available slave in the pool we can expect + // read-only operations to hit the master server. + $expectedRole = $this->strategy->isReadOperation($command) && $this->slaves ? 'slave' : 'master'; + $this->assertConnectionRole($connection, $expectedRole); + } + + return $connection; + } + + /** + * {@inheritdoc} + */ + public function getConnectionById($connectionId) + { + if ($connectionId === 'master') { + return $this->getMaster(); + } + + $this->getSlaves(); + + if (isset($this->slaves[$connectionId])) { + return $this->slaves[$connectionId]; + } + } + + /** + * {@inheritdoc} + */ + public function switchTo($connection) + { + if (!$connection instanceof NodeConnectionInterface) { + $connection = $this->getConnectionById($connection); + } + + if ($connection && $connection === $this->current) { + return; + } + + if ($connection !== $this->master && !in_array($connection, $this->slaves, true)) { + throw new \InvalidArgumentException('Invalid connection or connection not found.'); + } + + $connection->connect(); + + if ($this->current) { + $this->current->disconnect(); + } + + $this->current = $connection; + } + + /** + * Switches to the master server. + */ + public function switchToMaster() + { + $this->switchTo('master'); + } + + /** + * Switches to a random slave server. + */ + public function switchToSlave() + { + $connection = $this->pickSlave(); + $this->switchTo($connection); + } + + /** + * {@inheritdoc} + */ + public function isConnected() + { + return $this->current ? $this->current->isConnected() : false; + } + + /** + * {@inheritdoc} + */ + public function connect() + { + if (!$this->current) { + if (!$this->current = $this->pickSlave()) { + $this->current = $this->getMaster(); + } + } + + $this->current->connect(); + } + + /** + * {@inheritdoc} + */ + public function disconnect() + { + if ($this->master) { + $this->master->disconnect(); + } + + foreach ($this->slaves as $connection) { + $connection->disconnect(); + } + } + + /** + * Retries the execution of a command upon server failure after asking a new + * configuration to one of the sentinels. + * + * @param CommandInterface $command Command instance. + * @param string $method Actual method. + * + * @return mixed + */ + private function retryCommandOnFailure(CommandInterface $command, $method) + { + $retries = 0; + + SENTINEL_RETRY: { + try { + $response = $this->getConnection($command)->$method($command); + } catch (CommunicationException $exception) { + $this->wipeServerList(); + $exception->getConnection()->disconnect(); + + if ($retries == $this->retryLimit) { + throw $exception; + } + + usleep($this->retryWait * 1000); + + ++$retries; + goto SENTINEL_RETRY; + } + } + + return $response; + } + + /** + * {@inheritdoc} + */ + public function writeRequest(CommandInterface $command) + { + $this->retryCommandOnFailure($command, __FUNCTION__); + } + + /** + * {@inheritdoc} + */ + public function readResponse(CommandInterface $command) + { + return $this->retryCommandOnFailure($command, __FUNCTION__); + } + + /** + * {@inheritdoc} + */ + public function executeCommand(CommandInterface $command) + { + return $this->retryCommandOnFailure($command, __FUNCTION__); + } + + /** + * Returns the underlying replication strategy. + * + * @return ReplicationStrategy + */ + public function getReplicationStrategy() + { + return $this->strategy; + } + + /** + * {@inheritdoc} + */ + public function __sleep() + { + return array( + 'master', 'slaves', 'service', 'sentinels', 'connectionFactory', 'strategy', + ); + } +} diff --git a/vendor/predis/predis/src/Connection/AggregateConnectionInterface.php b/vendor/predis/predis/src/Connection/AggregateConnectionInterface.php new file mode 100644 index 000000000..7eeaede76 --- /dev/null +++ b/vendor/predis/predis/src/Connection/AggregateConnectionInterface.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +use Predis\Command\CommandInterface; + +/** + * Defines a virtual connection composed of multiple connection instances to + * single Redis nodes. + * + * @author Daniele Alessandri + */ +interface AggregateConnectionInterface extends ConnectionInterface +{ + /** + * Adds a connection instance to the aggregate connection. + * + * @param NodeConnectionInterface $connection Connection instance. + */ + public function add(NodeConnectionInterface $connection); + + /** + * Removes the specified connection instance from the aggregate connection. + * + * @param NodeConnectionInterface $connection Connection instance. + * + * @return bool Returns true if the connection was in the pool. + */ + public function remove(NodeConnectionInterface $connection); + + /** + * Returns the connection instance in charge for the given command. + * + * @param CommandInterface $command Command instance. + * + * @return NodeConnectionInterface + */ + public function getConnection(CommandInterface $command); + + /** + * Returns a connection instance from the aggregate connection by its alias. + * + * @param string $connectionID Connection alias. + * + * @return NodeConnectionInterface|null + */ + public function getConnectionById($connectionID); +} diff --git a/vendor/predis/predis/src/Connection/CompositeConnectionInterface.php b/vendor/predis/predis/src/Connection/CompositeConnectionInterface.php new file mode 100644 index 000000000..286e082cc --- /dev/null +++ b/vendor/predis/predis/src/Connection/CompositeConnectionInterface.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +/** + * Defines a connection to communicate with a single Redis server that leverages + * an external protocol processor to handle pluggable protocol handlers. + * + * @author Daniele Alessandri + */ +interface CompositeConnectionInterface extends NodeConnectionInterface +{ + /** + * Returns the protocol processor used by the connection. + */ + public function getProtocol(); + + /** + * Writes the buffer containing over the connection. + * + * @param string $buffer String buffer to be sent over the connection. + */ + public function writeBuffer($buffer); + + /** + * Reads the given number of bytes from the connection. + * + * @param int $length Number of bytes to read from the connection. + * + * @return string + */ + public function readBuffer($length); + + /** + * Reads a line from the connection. + * + * @param string + */ + public function readLine(); +} diff --git a/vendor/predis/predis/src/Connection/CompositeStreamConnection.php b/vendor/predis/predis/src/Connection/CompositeStreamConnection.php new file mode 100644 index 000000000..7a3534054 --- /dev/null +++ b/vendor/predis/predis/src/Connection/CompositeStreamConnection.php @@ -0,0 +1,125 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +use Predis\Command\CommandInterface; +use Predis\Protocol\ProtocolProcessorInterface; +use Predis\Protocol\Text\ProtocolProcessor as TextProtocolProcessor; + +/** + * Connection abstraction to Redis servers based on PHP's stream that uses an + * external protocol processor defining the protocol used for the communication. + * + * @author Daniele Alessandri + */ +class CompositeStreamConnection extends StreamConnection implements CompositeConnectionInterface +{ + protected $protocol; + + /** + * @param ParametersInterface $parameters Initialization parameters for the connection. + * @param ProtocolProcessorInterface $protocol Protocol processor. + */ + public function __construct( + ParametersInterface $parameters, + ProtocolProcessorInterface $protocol = null + ) { + $this->parameters = $this->assertParameters($parameters); + $this->protocol = $protocol ?: new TextProtocolProcessor(); + } + + /** + * {@inheritdoc} + */ + public function getProtocol() + { + return $this->protocol; + } + + /** + * {@inheritdoc} + */ + public function writeBuffer($buffer) + { + $this->write($buffer); + } + + /** + * {@inheritdoc} + */ + public function readBuffer($length) + { + if ($length <= 0) { + throw new \InvalidArgumentException('Length parameter must be greater than 0.'); + } + + $value = ''; + $socket = $this->getResource(); + + do { + $chunk = fread($socket, $length); + + if ($chunk === false || $chunk === '') { + $this->onConnectionError('Error while reading bytes from the server.'); + } + + $value .= $chunk; + } while (($length -= strlen($chunk)) > 0); + + return $value; + } + + /** + * {@inheritdoc} + */ + public function readLine() + { + $value = ''; + $socket = $this->getResource(); + + do { + $chunk = fgets($socket); + + if ($chunk === false || $chunk === '') { + $this->onConnectionError('Error while reading line from the server.'); + } + + $value .= $chunk; + } while (substr($value, -2) !== "\r\n"); + + return substr($value, 0, -2); + } + + /** + * {@inheritdoc} + */ + public function writeRequest(CommandInterface $command) + { + $this->protocol->write($this, $command); + } + + /** + * {@inheritdoc} + */ + public function read() + { + return $this->protocol->read($this); + } + + /** + * {@inheritdoc} + */ + public function __sleep() + { + return array_merge(parent::__sleep(), array('protocol')); + } +} diff --git a/vendor/predis/predis/src/Connection/ConnectionException.php b/vendor/predis/predis/src/Connection/ConnectionException.php new file mode 100644 index 000000000..ef2e9d73a --- /dev/null +++ b/vendor/predis/predis/src/Connection/ConnectionException.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +use Predis\CommunicationException; + +/** + * Exception class that identifies connection-related errors. + * + * @author Daniele Alessandri + */ +class ConnectionException extends CommunicationException +{ +} diff --git a/vendor/predis/predis/src/Connection/ConnectionInterface.php b/vendor/predis/predis/src/Connection/ConnectionInterface.php new file mode 100644 index 000000000..11ace1b69 --- /dev/null +++ b/vendor/predis/predis/src/Connection/ConnectionInterface.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +use Predis\Command\CommandInterface; + +/** + * Defines a connection object used to communicate with one or multiple + * Redis servers. + * + * @author Daniele Alessandri + */ +interface ConnectionInterface +{ + /** + * Opens the connection to Redis. + */ + public function connect(); + + /** + * Closes the connection to Redis. + */ + public function disconnect(); + + /** + * Checks if the connection to Redis is considered open. + * + * @return bool + */ + public function isConnected(); + + /** + * Writes the request for the given command over the connection. + * + * @param CommandInterface $command Command instance. + */ + public function writeRequest(CommandInterface $command); + + /** + * Reads the response to the given command from the connection. + * + * @param CommandInterface $command Command instance. + * + * @return mixed + */ + public function readResponse(CommandInterface $command); + + /** + * Writes a request for the given command over the connection and reads back + * the response returned by Redis. + * + * @param CommandInterface $command Command instance. + * + * @return mixed + */ + public function executeCommand(CommandInterface $command); +} diff --git a/vendor/predis/predis/src/Connection/Factory.php b/vendor/predis/predis/src/Connection/Factory.php new file mode 100644 index 000000000..d03e87e79 --- /dev/null +++ b/vendor/predis/predis/src/Connection/Factory.php @@ -0,0 +1,192 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +use Predis\Command\RawCommand; + +/** + * Standard connection factory for creating connections to Redis nodes. + * + * @author Daniele Alessandri + */ +class Factory implements FactoryInterface +{ + private $defaults = array(); + + protected $schemes = array( + 'tcp' => 'Predis\Connection\StreamConnection', + 'unix' => 'Predis\Connection\StreamConnection', + 'tls' => 'Predis\Connection\StreamConnection', + 'redis' => 'Predis\Connection\StreamConnection', + 'rediss' => 'Predis\Connection\StreamConnection', + 'http' => 'Predis\Connection\WebdisConnection', + ); + + /** + * Checks if the provided argument represents a valid connection class + * implementing Predis\Connection\NodeConnectionInterface. Optionally, + * callable objects are used for lazy initialization of connection objects. + * + * @param mixed $initializer FQN of a connection class or a callable for lazy initialization. + * + * @throws \InvalidArgumentException + * + * @return mixed + */ + protected function checkInitializer($initializer) + { + if (is_callable($initializer)) { + return $initializer; + } + + $class = new \ReflectionClass($initializer); + + if (!$class->isSubclassOf('Predis\Connection\NodeConnectionInterface')) { + throw new \InvalidArgumentException( + 'A connection initializer must be a valid connection class or a callable object.' + ); + } + + return $initializer; + } + + /** + * {@inheritdoc} + */ + public function define($scheme, $initializer) + { + $this->schemes[$scheme] = $this->checkInitializer($initializer); + } + + /** + * {@inheritdoc} + */ + public function undefine($scheme) + { + unset($this->schemes[$scheme]); + } + + /** + * {@inheritdoc} + */ + public function create($parameters) + { + if (!$parameters instanceof ParametersInterface) { + $parameters = $this->createParameters($parameters); + } + + $scheme = $parameters->scheme; + + if (!isset($this->schemes[$scheme])) { + throw new \InvalidArgumentException("Unknown connection scheme: '$scheme'."); + } + + $initializer = $this->schemes[$scheme]; + + if (is_callable($initializer)) { + $connection = call_user_func($initializer, $parameters, $this); + } else { + $connection = new $initializer($parameters); + $this->prepareConnection($connection); + } + + if (!$connection instanceof NodeConnectionInterface) { + throw new \UnexpectedValueException( + 'Objects returned by connection initializers must implement '. + "'Predis\Connection\NodeConnectionInterface'." + ); + } + + return $connection; + } + + /** + * {@inheritdoc} + */ + public function aggregate(AggregateConnectionInterface $connection, array $parameters) + { + foreach ($parameters as $node) { + $connection->add($node instanceof NodeConnectionInterface ? $node : $this->create($node)); + } + } + + /** + * Assigns a default set of parameters applied to new connections. + * + * The set of parameters passed to create a new connection have precedence + * over the default values set for the connection factory. + * + * @param array $parameters Set of connection parameters. + */ + public function setDefaultParameters(array $parameters) + { + $this->defaults = $parameters; + } + + /** + * Returns the default set of parameters applied to new connections. + * + * @return array + */ + public function getDefaultParameters() + { + return $this->defaults; + } + + /** + * Creates a connection parameters instance from the supplied argument. + * + * @param mixed $parameters Original connection parameters. + * + * @return ParametersInterface + */ + protected function createParameters($parameters) + { + if (is_string($parameters)) { + $parameters = Parameters::parse($parameters); + } else { + $parameters = $parameters ?: array(); + } + + if ($this->defaults) { + $parameters += $this->defaults; + } + + return new Parameters($parameters); + } + + /** + * Prepares a connection instance after its initialization. + * + * @param NodeConnectionInterface $connection Connection instance. + */ + protected function prepareConnection(NodeConnectionInterface $connection) + { + $parameters = $connection->getParameters(); + + if (isset($parameters->password) && strlen($parameters->password)) { + $cmdAuthArgs = isset($parameters->username) && strlen($parameters->username) + ? array('AUTH', $parameters->username, $parameters->password) + : array('AUTH', $parameters->password); + + $connection->addConnectCommand( + new RawCommand($cmdAuthArgs) + ); + } + + if (isset($parameters->database) && strlen($parameters->database)) { + $connection->addConnectCommand( + new RawCommand(array('SELECT', $parameters->database)) + ); + } + } +} diff --git a/vendor/predis/predis/src/Connection/FactoryInterface.php b/vendor/predis/predis/src/Connection/FactoryInterface.php new file mode 100644 index 000000000..2bae0839e --- /dev/null +++ b/vendor/predis/predis/src/Connection/FactoryInterface.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +/** + * Interface for classes providing a factory of connections to Redis nodes. + * + * @author Daniele Alessandri + */ +interface FactoryInterface +{ + /** + * Defines or overrides the connection class identified by a scheme prefix. + * + * @param string $scheme Target connection scheme. + * @param mixed $initializer Fully-qualified name of a class or a callable for lazy initialization. + */ + public function define($scheme, $initializer); + + /** + * Undefines the connection identified by a scheme prefix. + * + * @param string $scheme Target connection scheme. + */ + public function undefine($scheme); + + /** + * Creates a new connection object. + * + * @param mixed $parameters Initialization parameters for the connection. + * + * @return NodeConnectionInterface + */ + public function create($parameters); + + /** + * Aggregates single connections into an aggregate connection instance. + * + * @param AggregateConnectionInterface $aggregate Aggregate connection instance. + * @param array $parameters List of parameters for each connection. + */ + public function aggregate(AggregateConnectionInterface $aggregate, array $parameters); +} diff --git a/vendor/predis/predis/src/Connection/NodeConnectionInterface.php b/vendor/predis/predis/src/Connection/NodeConnectionInterface.php new file mode 100644 index 000000000..665b862c1 --- /dev/null +++ b/vendor/predis/predis/src/Connection/NodeConnectionInterface.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +use Predis\Command\CommandInterface; + +/** + * Defines a connection used to communicate with a single Redis node. + * + * @author Daniele Alessandri + */ +interface NodeConnectionInterface extends ConnectionInterface +{ + /** + * Returns a string representation of the connection. + * + * @return string + */ + public function __toString(); + + /** + * Returns the underlying resource used to communicate with Redis. + * + * @return mixed + */ + public function getResource(); + + /** + * Returns the parameters used to initialize the connection. + * + * @return ParametersInterface + */ + public function getParameters(); + + /** + * Pushes the given command into a queue of commands executed when + * establishing the actual connection to Redis. + * + * @param CommandInterface $command Instance of a Redis command. + */ + public function addConnectCommand(CommandInterface $command); + + /** + * Reads a response from the server. + * + * @return mixed + */ + public function read(); +} diff --git a/vendor/predis/predis/src/Connection/Parameters.php b/vendor/predis/predis/src/Connection/Parameters.php new file mode 100644 index 000000000..c1c3a73be --- /dev/null +++ b/vendor/predis/predis/src/Connection/Parameters.php @@ -0,0 +1,185 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +/** + * Container for connection parameters used to initialize connections to Redis. + * + * {@inheritdoc} + * + * @author Daniele Alessandri + */ +class Parameters implements ParametersInterface +{ + private $parameters; + + private static $defaults = array( + 'scheme' => 'tcp', + 'host' => '127.0.0.1', + 'port' => 6379, + ); + + /** + * @param array $parameters Named array of connection parameters. + */ + public function __construct(array $parameters = array()) + { + $this->parameters = $this->filter($parameters) + $this->getDefaults(); + } + + /** + * Returns some default parameters with their values. + * + * @return array + */ + protected function getDefaults() + { + return self::$defaults; + } + + /** + * Creates a new instance by supplying the initial parameters either in the + * form of an URI string or a named array. + * + * @param array|string $parameters Set of connection parameters. + * + * @return Parameters + */ + public static function create($parameters) + { + if (is_string($parameters)) { + $parameters = static::parse($parameters); + } + + return new static($parameters ?: array()); + } + + /** + * Parses an URI string returning an array of connection parameters. + * + * When using the "redis" and "rediss" schemes the URI is parsed according + * to the rules defined by the provisional registration documents approved + * by IANA. If the URI has a password in its "user-information" part or a + * database number in the "path" part these values override the values of + * "password" and "database" if they are present in the "query" part. + * + * @link http://www.iana.org/assignments/uri-schemes/prov/redis + * @link http://www.iana.org/assignments/uri-schemes/prov/rediss + * + * @param string $uri URI string. + * + * @throws \InvalidArgumentException + * + * @return array + */ + public static function parse($uri) + { + if (stripos($uri, 'unix://') === 0) { + // parse_url() can parse unix:/path/to/sock so we do not need the + // unix:///path/to/sock hack, we will support it anyway until 2.0. + $uri = str_ireplace('unix://', 'unix:', $uri); + } + + if (!$parsed = parse_url($uri)) { + throw new \InvalidArgumentException("Invalid parameters URI: $uri"); + } + + if ( + isset($parsed['host']) + && false !== strpos($parsed['host'], '[') + && false !== strpos($parsed['host'], ']') + ) { + $parsed['host'] = substr($parsed['host'], 1, -1); + } + + if (isset($parsed['query'])) { + parse_str($parsed['query'], $queryarray); + unset($parsed['query']); + + $parsed = array_merge($parsed, $queryarray); + } + + if (stripos($uri, 'redis') === 0) { + if (isset($parsed['user'])) { + if (strlen($parsed['user'])) { + $parsed['username'] = $parsed['user']; + } + unset($parsed['user']); + } + + if (isset($parsed['pass'])) { + if (strlen($parsed['pass'])) { + $parsed['password'] = $parsed['pass']; + } + unset($parsed['pass']); + } + + if (isset($parsed['path']) && preg_match('/^\/(\d+)(\/.*)?/', $parsed['path'], $path)) { + $parsed['database'] = $path[1]; + + if (isset($path[2])) { + $parsed['path'] = $path[2]; + } else { + unset($parsed['path']); + } + } + } + + return $parsed; + } + + /** + * Validates and converts each value of the connection parameters array. + * + * @param array $parameters Connection parameters. + * + * @return array + */ + protected function filter(array $parameters) + { + return $parameters ?: array(); + } + + /** + * {@inheritdoc} + */ + public function __get($parameter) + { + if (isset($this->parameters[$parameter])) { + return $this->parameters[$parameter]; + } + } + + /** + * {@inheritdoc} + */ + public function __isset($parameter) + { + return isset($this->parameters[$parameter]); + } + + /** + * {@inheritdoc} + */ + public function toArray() + { + return $this->parameters; + } + + /** + * {@inheritdoc} + */ + public function __sleep() + { + return array('parameters'); + } +} diff --git a/vendor/predis/predis/src/Connection/ParametersInterface.php b/vendor/predis/predis/src/Connection/ParametersInterface.php new file mode 100644 index 000000000..fd8a908e6 --- /dev/null +++ b/vendor/predis/predis/src/Connection/ParametersInterface.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +/** + * Interface defining a container for connection parameters. + * + * The actual list of connection parameters depends on the features supported by + * each connection backend class (please refer to their specific documentation), + * but the most common parameters used through the library are: + * + * @property-read string scheme Connection scheme, such as 'tcp' or 'unix'. + * @property-read string host IP address or hostname of Redis. + * @property-read int port TCP port on which Redis is listening to. + * @property-read string path Path of a UNIX domain socket file. + * @property-read string alias Alias for the connection. + * @property-read float timeout Timeout for the connect() operation. + * @property-read float read_write_timeout Timeout for read() and write() operations. + * @property-read bool async_connect Performs the connect() operation asynchronously. + * @property-read bool tcp_nodelay Toggles the Nagle's algorithm for coalescing. + * @property-read bool persistent Leaves the connection open after a GC collection. + * @property-read string password Password to access Redis (see the AUTH command). + * @property-read string database Database index (see the SELECT command). + * + * @author Daniele Alessandri + */ +interface ParametersInterface +{ + /** + * Checks if the specified parameters is set. + * + * @param string $parameter Name of the parameter. + * + * @return bool + */ + public function __isset($parameter); + + /** + * Returns the value of the specified parameter. + * + * @param string $parameter Name of the parameter. + * + * @return mixed|null + */ + public function __get($parameter); + + /** + * Returns an array representation of the connection parameters. + * + * @return array + */ + public function toArray(); +} diff --git a/vendor/predis/predis/src/Connection/PhpiredisSocketConnection.php b/vendor/predis/predis/src/Connection/PhpiredisSocketConnection.php new file mode 100644 index 000000000..edded2d05 --- /dev/null +++ b/vendor/predis/predis/src/Connection/PhpiredisSocketConnection.php @@ -0,0 +1,418 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +use Predis\Command\CommandInterface; +use Predis\NotSupportedException; +use Predis\Response\Error as ErrorResponse; +use Predis\Response\ErrorInterface as ErrorResponseInterface; +use Predis\Response\Status as StatusResponse; + +/** + * This class provides the implementation of a Predis connection that uses the + * PHP socket extension for network communication and wraps the phpiredis C + * extension (PHP bindings for hiredis) to parse the Redis protocol. + * + * This class is intended to provide an optional low-overhead alternative for + * processing responses from Redis compared to the standard pure-PHP classes. + * Differences in speed when dealing with short inline responses are practically + * nonexistent, the actual speed boost is for big multibulk responses when this + * protocol processor can parse and return responses very fast. + * + * For instructions on how to build and install the phpiredis extension, please + * consult the repository of the project. + * + * The connection parameters supported by this class are: + * + * - scheme: it can be either 'redis', 'tcp' or 'unix'. + * - host: hostname or IP address of the server. + * - port: TCP port of the server. + * - path: path of a UNIX domain socket when scheme is 'unix'. + * - timeout: timeout to perform the connection (default is 5 seconds). + * - read_write_timeout: timeout of read / write operations. + * + * @link http://github.com/nrk/phpiredis + * + * @author Daniele Alessandri + */ +class PhpiredisSocketConnection extends AbstractConnection +{ + private $reader; + + /** + * {@inheritdoc} + */ + public function __construct(ParametersInterface $parameters) + { + $this->assertExtensions(); + + parent::__construct($parameters); + + $this->reader = $this->createReader(); + } + + /** + * Disconnects from the server and destroys the underlying resource and the + * protocol reader resource when PHP's garbage collector kicks in. + */ + public function __destruct() + { + phpiredis_reader_destroy($this->reader); + + parent::__destruct(); + } + + /** + * Checks if the socket and phpiredis extensions are loaded in PHP. + */ + protected function assertExtensions() + { + if (!extension_loaded('sockets')) { + throw new NotSupportedException( + 'The "sockets" extension is required by this connection backend.' + ); + } + + if (!extension_loaded('phpiredis')) { + throw new NotSupportedException( + 'The "phpiredis" extension is required by this connection backend.' + ); + } + } + + /** + * {@inheritdoc} + */ + protected function assertParameters(ParametersInterface $parameters) + { + switch ($parameters->scheme) { + case 'tcp': + case 'redis': + case 'unix': + break; + + default: + throw new \InvalidArgumentException("Invalid scheme: '$parameters->scheme'."); + } + + if (isset($parameters->persistent)) { + throw new NotSupportedException( + 'Persistent connections are not supported by this connection backend.' + ); + } + + return $parameters; + } + + /** + * Creates a new instance of the protocol reader resource. + * + * @return resource + */ + private function createReader() + { + $reader = phpiredis_reader_create(); + + phpiredis_reader_set_status_handler($reader, $this->getStatusHandler()); + phpiredis_reader_set_error_handler($reader, $this->getErrorHandler()); + + return $reader; + } + + /** + * Returns the underlying protocol reader resource. + * + * @return resource + */ + protected function getReader() + { + return $this->reader; + } + + /** + * Returns the handler used by the protocol reader for inline responses. + * + * @return \Closure + */ + protected function getStatusHandler() + { + static $statusHandler; + + if (!$statusHandler) { + $statusHandler = function ($payload) { + return StatusResponse::get($payload); + }; + } + + return $statusHandler; + } + + /** + * Returns the handler used by the protocol reader for error responses. + * + * @return \Closure + */ + protected function getErrorHandler() + { + static $errorHandler; + + if (!$errorHandler) { + $errorHandler = function ($errorMessage) { + return new ErrorResponse($errorMessage); + }; + } + + return $errorHandler; + } + + /** + * Helper method used to throw exceptions on socket errors. + */ + private function emitSocketError() + { + $errno = socket_last_error(); + $errstr = socket_strerror($errno); + + $this->disconnect(); + + $this->onConnectionError(trim($errstr), $errno); + } + + /** + * Gets the address of an host from connection parameters. + * + * @param ParametersInterface $parameters Parameters used to initialize the connection. + * + * @return string + */ + protected static function getAddress(ParametersInterface $parameters) + { + if (filter_var($host = $parameters->host, FILTER_VALIDATE_IP)) { + return $host; + } + + if ($host === $address = gethostbyname($host)) { + return false; + } + + return $address; + } + + /** + * {@inheritdoc} + */ + protected function createResource() + { + $parameters = $this->parameters; + + if ($parameters->scheme === 'unix') { + $address = $parameters->path; + $domain = AF_UNIX; + $protocol = 0; + } else { + if (false === $address = self::getAddress($parameters)) { + $this->onConnectionError("Cannot resolve the address of '$parameters->host'."); + } + + $domain = filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? AF_INET6 : AF_INET; + $protocol = SOL_TCP; + } + + $socket = @socket_create($domain, SOCK_STREAM, $protocol); + + if (!is_resource($socket)) { + $this->emitSocketError(); + } + + $this->setSocketOptions($socket, $parameters); + $this->connectWithTimeout($socket, $address, $parameters); + + return $socket; + } + + /** + * Sets options on the socket resource from the connection parameters. + * + * @param resource $socket Socket resource. + * @param ParametersInterface $parameters Parameters used to initialize the connection. + */ + private function setSocketOptions($socket, ParametersInterface $parameters) + { + if ($parameters->scheme !== 'unix') { + if (!socket_set_option($socket, SOL_TCP, TCP_NODELAY, 1)) { + $this->emitSocketError(); + } + + if (!socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1)) { + $this->emitSocketError(); + } + } + + if (isset($parameters->read_write_timeout)) { + $rwtimeout = (float) $parameters->read_write_timeout; + $timeoutSec = floor($rwtimeout); + $timeoutUsec = ($rwtimeout - $timeoutSec) * 1000000; + + $timeout = array( + 'sec' => $timeoutSec, + 'usec' => $timeoutUsec, + ); + + if (!socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, $timeout)) { + $this->emitSocketError(); + } + + if (!socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, $timeout)) { + $this->emitSocketError(); + } + } + } + + /** + * Opens the actual connection to the server with a timeout. + * + * @param resource $socket Socket resource. + * @param string $address IP address (DNS-resolved from hostname) + * @param ParametersInterface $parameters Parameters used to initialize the connection. + * + * @return string + */ + private function connectWithTimeout($socket, $address, ParametersInterface $parameters) + { + socket_set_nonblock($socket); + + if (@socket_connect($socket, $address, (int) $parameters->port) === false) { + $error = socket_last_error(); + + if ($error != SOCKET_EINPROGRESS && $error != SOCKET_EALREADY) { + $this->emitSocketError(); + } + } + + socket_set_block($socket); + + $null = null; + $selectable = array($socket); + + $timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0); + $timeoutSecs = floor($timeout); + $timeoutUSecs = ($timeout - $timeoutSecs) * 1000000; + + $selected = socket_select($selectable, $selectable, $null, $timeoutSecs, $timeoutUSecs); + + if ($selected === 2) { + $this->onConnectionError('Connection refused.', SOCKET_ECONNREFUSED); + } + + if ($selected === 0) { + $this->onConnectionError('Connection timed out.', SOCKET_ETIMEDOUT); + } + + if ($selected === false) { + $this->emitSocketError(); + } + } + + /** + * {@inheritdoc} + */ + public function connect() + { + if (parent::connect() && $this->initCommands) { + foreach ($this->initCommands as $command) { + $response = $this->executeCommand($command); + + if ($response instanceof ErrorResponseInterface) { + $this->onConnectionError("`{$command->getId()}` failed: $response", 0); + } + } + } + } + + /** + * {@inheritdoc} + */ + public function disconnect() + { + if ($this->isConnected()) { + socket_close($this->getResource()); + parent::disconnect(); + } + } + + /** + * {@inheritdoc} + */ + protected function write($buffer) + { + $socket = $this->getResource(); + + while (($length = strlen($buffer)) > 0) { + $written = socket_write($socket, $buffer, $length); + + if ($length === $written) { + return; + } + + if ($written === false) { + $this->onConnectionError('Error while writing bytes to the server.'); + } + + $buffer = substr($buffer, $written); + } + } + + /** + * {@inheritdoc} + */ + public function read() + { + $socket = $this->getResource(); + $reader = $this->reader; + + while (PHPIREDIS_READER_STATE_INCOMPLETE === $state = phpiredis_reader_get_state($reader)) { + if (@socket_recv($socket, $buffer, 4096, 0) === false || $buffer === '' || $buffer === null) { + $this->emitSocketError(); + } + + phpiredis_reader_feed($reader, $buffer); + } + + if ($state === PHPIREDIS_READER_STATE_COMPLETE) { + return phpiredis_reader_get_reply($reader); + } else { + $this->onProtocolError(phpiredis_reader_get_error($reader)); + + return; + } + } + + /** + * {@inheritdoc} + */ + public function writeRequest(CommandInterface $command) + { + $arguments = $command->getArguments(); + array_unshift($arguments, $command->getId()); + + $this->write(phpiredis_format_command($arguments)); + } + + /** + * {@inheritdoc} + */ + public function __wakeup() + { + $this->assertExtensions(); + $this->reader = $this->createReader(); + } +} diff --git a/vendor/predis/predis/src/Connection/PhpiredisStreamConnection.php b/vendor/predis/predis/src/Connection/PhpiredisStreamConnection.php new file mode 100644 index 000000000..c63a5979c --- /dev/null +++ b/vendor/predis/predis/src/Connection/PhpiredisStreamConnection.php @@ -0,0 +1,252 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +use Predis\Command\CommandInterface; +use Predis\NotSupportedException; +use Predis\Response\Error as ErrorResponse; +use Predis\Response\Status as StatusResponse; + +/** + * This class provides the implementation of a Predis connection that uses PHP's + * streams for network communication and wraps the phpiredis C extension (PHP + * bindings for hiredis) to parse and serialize the Redis protocol. + * + * This class is intended to provide an optional low-overhead alternative for + * processing responses from Redis compared to the standard pure-PHP classes. + * Differences in speed when dealing with short inline responses are practically + * nonexistent, the actual speed boost is for big multibulk responses when this + * protocol processor can parse and return responses very fast. + * + * For instructions on how to build and install the phpiredis extension, please + * consult the repository of the project. + * + * The connection parameters supported by this class are: + * + * - scheme: it can be either 'redis', 'tcp' or 'unix'. + * - host: hostname or IP address of the server. + * - port: TCP port of the server. + * - path: path of a UNIX domain socket when scheme is 'unix'. + * - timeout: timeout to perform the connection. + * - read_write_timeout: timeout of read / write operations. + * - async_connect: performs the connection asynchronously. + * - tcp_nodelay: enables or disables Nagle's algorithm for coalescing. + * - persistent: the connection is left intact after a GC collection. + * + * @link https://github.com/nrk/phpiredis + * + * @author Daniele Alessandri + */ +class PhpiredisStreamConnection extends StreamConnection +{ + private $reader; + + /** + * {@inheritdoc} + */ + public function __construct(ParametersInterface $parameters) + { + $this->assertExtensions(); + + parent::__construct($parameters); + + $this->reader = $this->createReader(); + } + + /** + * {@inheritdoc} + */ + public function __destruct() + { + phpiredis_reader_destroy($this->reader); + + parent::__destruct(); + } + + /** + * Checks if the phpiredis extension is loaded in PHP. + */ + private function assertExtensions() + { + if (!extension_loaded('phpiredis')) { + throw new NotSupportedException( + 'The "phpiredis" extension is required by this connection backend.' + ); + } + } + + /** + * {@inheritdoc} + */ + protected function assertParameters(ParametersInterface $parameters) + { + switch ($parameters->scheme) { + case 'tcp': + case 'redis': + case 'unix': + break; + + case 'tls': + case 'rediss': + throw new \InvalidArgumentException('SSL encryption is not supported by this connection backend.'); + + default: + throw new \InvalidArgumentException("Invalid scheme: '$parameters->scheme'."); + } + + return $parameters; + } + + /** + * {@inheritdoc} + */ + protected function createStreamSocket(ParametersInterface $parameters, $address, $flags, $context = null) + { + $socket = null; + $timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0); + + $resource = @stream_socket_client($address, $errno, $errstr, $timeout, $flags); + + if (!$resource) { + $this->onConnectionError(trim($errstr), $errno); + } + + if (isset($parameters->read_write_timeout) && function_exists('socket_import_stream')) { + $rwtimeout = (float) $parameters->read_write_timeout; + $rwtimeout = $rwtimeout > 0 ? $rwtimeout : -1; + + $timeout = array( + 'sec' => $timeoutSeconds = floor($rwtimeout), + 'usec' => ($rwtimeout - $timeoutSeconds) * 1000000, + ); + + $socket = $socket ?: socket_import_stream($resource); + @socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, $timeout); + @socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, $timeout); + } + + if (isset($parameters->tcp_nodelay) && function_exists('socket_import_stream')) { + $socket = $socket ?: socket_import_stream($resource); + socket_set_option($socket, SOL_TCP, TCP_NODELAY, (int) $parameters->tcp_nodelay); + } + + return $resource; + } + + /** + * Creates a new instance of the protocol reader resource. + * + * @return resource + */ + private function createReader() + { + $reader = phpiredis_reader_create(); + + phpiredis_reader_set_status_handler($reader, $this->getStatusHandler()); + phpiredis_reader_set_error_handler($reader, $this->getErrorHandler()); + + return $reader; + } + + /** + * Returns the underlying protocol reader resource. + * + * @return resource + */ + protected function getReader() + { + return $this->reader; + } + + /** + * Returns the handler used by the protocol reader for inline responses. + * + * @return \Closure + */ + protected function getStatusHandler() + { + static $statusHandler; + + if (!$statusHandler) { + $statusHandler = function ($payload) { + return StatusResponse::get($payload); + }; + } + + return $statusHandler; + } + + /** + * Returns the handler used by the protocol reader for error responses. + * + * @return \Closure + */ + protected function getErrorHandler() + { + static $errorHandler; + + if (!$errorHandler) { + $errorHandler = function ($errorMessage) { + return new ErrorResponse($errorMessage); + }; + } + + return $errorHandler; + } + + /** + * {@inheritdoc} + */ + public function read() + { + $socket = $this->getResource(); + $reader = $this->reader; + + while (PHPIREDIS_READER_STATE_INCOMPLETE === $state = phpiredis_reader_get_state($reader)) { + $buffer = stream_socket_recvfrom($socket, 4096); + + if ($buffer === false || $buffer === '') { + $this->onConnectionError('Error while reading bytes from the server.'); + } + + phpiredis_reader_feed($reader, $buffer); + } + + if ($state === PHPIREDIS_READER_STATE_COMPLETE) { + return phpiredis_reader_get_reply($reader); + } else { + $this->onProtocolError(phpiredis_reader_get_error($reader)); + + return; + } + } + + /** + * {@inheritdoc} + */ + public function writeRequest(CommandInterface $command) + { + $arguments = $command->getArguments(); + array_unshift($arguments, $command->getId()); + + $this->write(phpiredis_format_command($arguments)); + } + + /** + * {@inheritdoc} + */ + public function __wakeup() + { + $this->assertExtensions(); + $this->reader = $this->createReader(); + } +} diff --git a/vendor/predis/predis/src/Connection/StreamConnection.php b/vendor/predis/predis/src/Connection/StreamConnection.php new file mode 100644 index 000000000..9c26272d6 --- /dev/null +++ b/vendor/predis/predis/src/Connection/StreamConnection.php @@ -0,0 +1,396 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +use Predis\Command\CommandInterface; +use Predis\Response\Error as ErrorResponse; +use Predis\Response\ErrorInterface as ErrorResponseInterface; +use Predis\Response\Status as StatusResponse; + +/** + * Standard connection to Redis servers implemented on top of PHP's streams. + * The connection parameters supported by this class are:. + * + * - scheme: it can be either 'redis', 'tcp', 'rediss', 'tls' or 'unix'. + * - host: hostname or IP address of the server. + * - port: TCP port of the server. + * - path: path of a UNIX domain socket when scheme is 'unix'. + * - timeout: timeout to perform the connection (default is 5 seconds). + * - read_write_timeout: timeout of read / write operations. + * - async_connect: performs the connection asynchronously. + * - tcp_nodelay: enables or disables Nagle's algorithm for coalescing. + * - persistent: the connection is left intact after a GC collection. + * - ssl: context options array (see http://php.net/manual/en/context.ssl.php) + * + * @author Daniele Alessandri + */ +class StreamConnection extends AbstractConnection +{ + /** + * Disconnects from the server and destroys the underlying resource when the + * garbage collector kicks in only if the connection has not been marked as + * persistent. + */ + public function __destruct() + { + if (isset($this->parameters->persistent) && $this->parameters->persistent) { + return; + } + + $this->disconnect(); + } + + /** + * {@inheritdoc} + */ + protected function assertParameters(ParametersInterface $parameters) + { + switch ($parameters->scheme) { + case 'tcp': + case 'redis': + case 'unix': + break; + + case 'tls': + case 'rediss': + $this->assertSslSupport($parameters); + break; + + default: + throw new \InvalidArgumentException("Invalid scheme: '$parameters->scheme'."); + } + + return $parameters; + } + + /** + * Checks needed conditions for SSL-encrypted connections. + * + * @param ParametersInterface $parameters Initialization parameters for the connection. + * + * @throws \InvalidArgumentException + */ + protected function assertSslSupport(ParametersInterface $parameters) + { + if ( + filter_var($parameters->persistent, FILTER_VALIDATE_BOOLEAN) && + version_compare(PHP_VERSION, '7.0.0beta') < 0 + ) { + throw new \InvalidArgumentException('Persistent SSL connections require PHP >= 7.0.0.'); + } + } + + /** + * {@inheritdoc} + */ + protected function createResource() + { + switch ($this->parameters->scheme) { + case 'tcp': + case 'redis': + return $this->tcpStreamInitializer($this->parameters); + + case 'unix': + return $this->unixStreamInitializer($this->parameters); + + case 'tls': + case 'rediss': + return $this->tlsStreamInitializer($this->parameters); + + default: + throw new \InvalidArgumentException("Invalid scheme: '{$this->parameters->scheme}'."); + } + } + + /** + * Creates a connected stream socket resource. + * + * @param ParametersInterface $parameters Connection parameters. + * @param string $address Address for stream_socket_client(). + * @param int $flags Flags for stream_socket_client(). + * + * @return resource + */ + protected function createStreamSocket(ParametersInterface $parameters, $address, $flags) + { + $timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0); + + if (!$resource = @stream_socket_client($address, $errno, $errstr, $timeout, $flags)) { + $this->onConnectionError(trim($errstr), $errno); + } + + if (isset($parameters->read_write_timeout)) { + $rwtimeout = (float) $parameters->read_write_timeout; + $rwtimeout = $rwtimeout > 0 ? $rwtimeout : -1; + $timeoutSeconds = floor($rwtimeout); + $timeoutUSeconds = ($rwtimeout - $timeoutSeconds) * 1000000; + stream_set_timeout($resource, $timeoutSeconds, $timeoutUSeconds); + } + + if (isset($parameters->tcp_nodelay) && function_exists('socket_import_stream')) { + $socket = socket_import_stream($resource); + socket_set_option($socket, SOL_TCP, TCP_NODELAY, (int) $parameters->tcp_nodelay); + } + + return $resource; + } + + /** + * Initializes a TCP stream resource. + * + * @param ParametersInterface $parameters Initialization parameters for the connection. + * + * @return resource + */ + protected function tcpStreamInitializer(ParametersInterface $parameters) + { + if (!filter_var($parameters->host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + $address = "tcp://$parameters->host:$parameters->port"; + } else { + $address = "tcp://[$parameters->host]:$parameters->port"; + } + + $flags = STREAM_CLIENT_CONNECT; + + if (isset($parameters->async_connect) && $parameters->async_connect) { + $flags |= STREAM_CLIENT_ASYNC_CONNECT; + } + + if (isset($parameters->persistent)) { + if (false !== $persistent = filter_var($parameters->persistent, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) { + $flags |= STREAM_CLIENT_PERSISTENT; + + if ($persistent === null) { + $address = "{$address}/{$parameters->persistent}"; + } + } + } + + $resource = $this->createStreamSocket($parameters, $address, $flags); + + return $resource; + } + + /** + * Initializes a UNIX stream resource. + * + * @param ParametersInterface $parameters Initialization parameters for the connection. + * + * @return resource + */ + protected function unixStreamInitializer(ParametersInterface $parameters) + { + if (!isset($parameters->path)) { + throw new \InvalidArgumentException('Missing UNIX domain socket path.'); + } + + $flags = STREAM_CLIENT_CONNECT; + + if (isset($parameters->persistent)) { + if (false !== $persistent = filter_var($parameters->persistent, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) { + $flags |= STREAM_CLIENT_PERSISTENT; + + if ($persistent === null) { + throw new \InvalidArgumentException( + 'Persistent connection IDs are not supported when using UNIX domain sockets.' + ); + } + } + } + + $resource = $this->createStreamSocket($parameters, "unix://{$parameters->path}", $flags); + + return $resource; + } + + /** + * Initializes a SSL-encrypted TCP stream resource. + * + * @param ParametersInterface $parameters Initialization parameters for the connection. + * + * @return resource + */ + protected function tlsStreamInitializer(ParametersInterface $parameters) + { + $resource = $this->tcpStreamInitializer($parameters); + $metadata = stream_get_meta_data($resource); + + // Detect if crypto mode is already enabled for this stream (PHP >= 7.0.0). + if (isset($metadata['crypto'])) { + return $resource; + } + + if (is_array($parameters->ssl)) { + $options = $parameters->ssl; + } else { + $options = array(); + } + + if (!isset($options['crypto_type'])) { + $options['crypto_type'] = STREAM_CRYPTO_METHOD_TLS_CLIENT; + } + + if (!stream_context_set_option($resource, array('ssl' => $options))) { + $this->onConnectionError('Error while setting SSL context options'); + } + + if (!stream_socket_enable_crypto($resource, true, $options['crypto_type'])) { + $this->onConnectionError('Error while switching to encrypted communication'); + } + + return $resource; + } + + /** + * {@inheritdoc} + */ + public function connect() + { + if (parent::connect() && $this->initCommands) { + foreach ($this->initCommands as $command) { + $response = $this->executeCommand($command); + + if ($response instanceof ErrorResponseInterface) { + $this->onConnectionError("`{$command->getId()}` failed: $response", 0); + } + } + } + } + + /** + * {@inheritdoc} + */ + public function disconnect() + { + if ($this->isConnected()) { + fclose($this->getResource()); + parent::disconnect(); + } + } + + /** + * Performs a write operation over the stream of the buffer containing a + * command serialized with the Redis wire protocol. + * + * @param string $buffer Representation of a command in the Redis wire protocol. + */ + protected function write($buffer) + { + $socket = $this->getResource(); + + while (($length = strlen($buffer)) > 0) { + $written = @fwrite($socket, $buffer); + + if ($length === $written) { + return; + } + + if ($written === false || $written === 0) { + $this->onConnectionError('Error while writing bytes to the server.'); + } + + $buffer = substr($buffer, $written); + } + } + + /** + * {@inheritdoc} + */ + public function read() + { + $socket = $this->getResource(); + $chunk = fgets($socket); + + if ($chunk === false || $chunk === '') { + $this->onConnectionError('Error while reading line from the server.'); + } + + $prefix = $chunk[0]; + $payload = substr($chunk, 1, -2); + + switch ($prefix) { + case '+': + return StatusResponse::get($payload); + + case '$': + $size = (int) $payload; + + if ($size === -1) { + return; + } + + $bulkData = ''; + $bytesLeft = ($size += 2); + + do { + $chunk = fread($socket, min($bytesLeft, 4096)); + + if ($chunk === false || $chunk === '') { + $this->onConnectionError('Error while reading bytes from the server.'); + } + + $bulkData .= $chunk; + $bytesLeft = $size - strlen($bulkData); + } while ($bytesLeft > 0); + + return substr($bulkData, 0, -2); + + case '*': + $count = (int) $payload; + + if ($count === -1) { + return; + } + + $multibulk = array(); + + for ($i = 0; $i < $count; ++$i) { + $multibulk[$i] = $this->read(); + } + + return $multibulk; + + case ':': + $integer = (int) $payload; + return $integer == $payload ? $integer : $payload; + + case '-': + return new ErrorResponse($payload); + + default: + $this->onProtocolError("Unknown response prefix: '$prefix'."); + + return; + } + } + + /** + * {@inheritdoc} + */ + public function writeRequest(CommandInterface $command) + { + $commandID = $command->getId(); + $arguments = $command->getArguments(); + + $cmdlen = strlen($commandID); + $reqlen = count($arguments) + 1; + + $buffer = "*{$reqlen}\r\n\${$cmdlen}\r\n{$commandID}\r\n"; + + foreach ($arguments as $argument) { + $arglen = strlen($argument); + $buffer .= "\${$arglen}\r\n{$argument}\r\n"; + } + + $this->write($buffer); + } +} diff --git a/vendor/predis/predis/src/Connection/WebdisConnection.php b/vendor/predis/predis/src/Connection/WebdisConnection.php new file mode 100644 index 000000000..e8cd721f0 --- /dev/null +++ b/vendor/predis/predis/src/Connection/WebdisConnection.php @@ -0,0 +1,366 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +use Predis\Command\CommandInterface; +use Predis\NotSupportedException; +use Predis\Protocol\ProtocolException; +use Predis\Response\Error as ErrorResponse; +use Predis\Response\Status as StatusResponse; + +/** + * This class implements a Predis connection that actually talks with Webdis + * instead of connecting directly to Redis. It relies on the cURL extension to + * communicate with the web server and the phpiredis extension to parse the + * protocol for responses returned in the http response bodies. + * + * Some features are not yet available or they simply cannot be implemented: + * - Pipelining commands. + * - Publish / Subscribe. + * - MULTI / EXEC transactions (not yet supported by Webdis). + * + * The connection parameters supported by this class are: + * + * - scheme: must be 'http'. + * - host: hostname or IP address of the server. + * - port: TCP port of the server. + * - timeout: timeout to perform the connection (default is 5 seconds). + * - user: username for authentication. + * - pass: password for authentication. + * + * @link http://webd.is + * @link http://github.com/nicolasff/webdis + * @link http://github.com/seppo0010/phpiredis + * + * @author Daniele Alessandri + */ +class WebdisConnection implements NodeConnectionInterface +{ + private $parameters; + private $resource; + private $reader; + + /** + * @param ParametersInterface $parameters Initialization parameters for the connection. + * + * @throws \InvalidArgumentException + */ + public function __construct(ParametersInterface $parameters) + { + $this->assertExtensions(); + + if ($parameters->scheme !== 'http') { + throw new \InvalidArgumentException("Invalid scheme: '{$parameters->scheme}'."); + } + + $this->parameters = $parameters; + + $this->resource = $this->createCurl(); + $this->reader = $this->createReader(); + } + + /** + * Frees the underlying cURL and protocol reader resources when the garbage + * collector kicks in. + */ + public function __destruct() + { + curl_close($this->resource); + phpiredis_reader_destroy($this->reader); + } + + /** + * Helper method used to throw on unsupported methods. + * + * @param string $method Name of the unsupported method. + * + * @throws NotSupportedException + */ + private function throwNotSupportedException($method) + { + $class = __CLASS__; + throw new NotSupportedException("The method $class::$method() is not supported."); + } + + /** + * Checks if the cURL and phpiredis extensions are loaded in PHP. + */ + private function assertExtensions() + { + if (!extension_loaded('curl')) { + throw new NotSupportedException( + 'The "curl" extension is required by this connection backend.' + ); + } + + if (!extension_loaded('phpiredis')) { + throw new NotSupportedException( + 'The "phpiredis" extension is required by this connection backend.' + ); + } + } + + /** + * Initializes cURL. + * + * @return resource + */ + private function createCurl() + { + $parameters = $this->getParameters(); + $timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0) * 1000; + + if (filter_var($host = $parameters->host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + $host = "[$host]"; + } + + $options = array( + CURLOPT_FAILONERROR => true, + CURLOPT_CONNECTTIMEOUT_MS => $timeout, + CURLOPT_URL => "$parameters->scheme://$host:$parameters->port", + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_POST => true, + CURLOPT_WRITEFUNCTION => array($this, 'feedReader'), + ); + + if (isset($parameters->user, $parameters->pass)) { + $options[CURLOPT_USERPWD] = "{$parameters->user}:{$parameters->pass}"; + } + + curl_setopt_array($resource = curl_init(), $options); + + return $resource; + } + + /** + * Initializes the phpiredis protocol reader. + * + * @return resource + */ + private function createReader() + { + $reader = phpiredis_reader_create(); + + phpiredis_reader_set_status_handler($reader, $this->getStatusHandler()); + phpiredis_reader_set_error_handler($reader, $this->getErrorHandler()); + + return $reader; + } + + /** + * Returns the handler used by the protocol reader for inline responses. + * + * @return \Closure + */ + protected function getStatusHandler() + { + static $statusHandler; + + if (!$statusHandler) { + $statusHandler = function ($payload) { + return StatusResponse::get($payload); + }; + } + + return $statusHandler; + } + + /** + * Returns the handler used by the protocol reader for error responses. + * + * @return \Closure + */ + protected function getErrorHandler() + { + static $errorHandler; + + if (!$errorHandler) { + $errorHandler = function ($errorMessage) { + return new ErrorResponse($errorMessage); + }; + } + + return $errorHandler; + } + + /** + * Feeds the phpredis reader resource with the data read from the network. + * + * @param resource $resource Reader resource. + * @param string $buffer Buffer of data read from a connection. + * + * @return int + */ + protected function feedReader($resource, $buffer) + { + phpiredis_reader_feed($this->reader, $buffer); + + return strlen($buffer); + } + + /** + * {@inheritdoc} + */ + public function connect() + { + // NOOP + } + + /** + * {@inheritdoc} + */ + public function disconnect() + { + // NOOP + } + + /** + * {@inheritdoc} + */ + public function isConnected() + { + return true; + } + + /** + * Checks if the specified command is supported by this connection class. + * + * @param CommandInterface $command Command instance. + * + * @throws NotSupportedException + * + * @return string + */ + protected function getCommandId(CommandInterface $command) + { + switch ($commandID = $command->getId()) { + case 'AUTH': + case 'SELECT': + case 'MULTI': + case 'EXEC': + case 'WATCH': + case 'UNWATCH': + case 'DISCARD': + case 'MONITOR': + throw new NotSupportedException("Command '$commandID' is not allowed by Webdis."); + + default: + return $commandID; + } + } + + /** + * {@inheritdoc} + */ + public function writeRequest(CommandInterface $command) + { + $this->throwNotSupportedException(__FUNCTION__); + } + + /** + * {@inheritdoc} + */ + public function readResponse(CommandInterface $command) + { + $this->throwNotSupportedException(__FUNCTION__); + } + + /** + * {@inheritdoc} + */ + public function executeCommand(CommandInterface $command) + { + $resource = $this->resource; + $commandId = $this->getCommandId($command); + + if ($arguments = $command->getArguments()) { + $arguments = implode('/', array_map('urlencode', $arguments)); + $serializedCommand = "$commandId/$arguments.raw"; + } else { + $serializedCommand = "$commandId.raw"; + } + + curl_setopt($resource, CURLOPT_POSTFIELDS, $serializedCommand); + + if (curl_exec($resource) === false) { + $error = curl_error($resource); + $errno = curl_errno($resource); + + throw new ConnectionException($this, trim($error), $errno); + } + + if (phpiredis_reader_get_state($this->reader) !== PHPIREDIS_READER_STATE_COMPLETE) { + throw new ProtocolException($this, phpiredis_reader_get_error($this->reader)); + } + + return phpiredis_reader_get_reply($this->reader); + } + + /** + * {@inheritdoc} + */ + public function getResource() + { + return $this->resource; + } + + /** + * {@inheritdoc} + */ + public function getParameters() + { + return $this->parameters; + } + + /** + * {@inheritdoc} + */ + public function addConnectCommand(CommandInterface $command) + { + $this->throwNotSupportedException(__FUNCTION__); + } + + /** + * {@inheritdoc} + */ + public function read() + { + $this->throwNotSupportedException(__FUNCTION__); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return "{$this->parameters->host}:{$this->parameters->port}"; + } + + /** + * {@inheritdoc} + */ + public function __sleep() + { + return array('parameters'); + } + + /** + * {@inheritdoc} + */ + public function __wakeup() + { + $this->assertExtensions(); + + $this->resource = $this->createCurl(); + $this->reader = $this->createReader(); + } +} diff --git a/vendor/predis/predis/src/Monitor/Consumer.php b/vendor/predis/predis/src/Monitor/Consumer.php new file mode 100644 index 000000000..aaf645be0 --- /dev/null +++ b/vendor/predis/predis/src/Monitor/Consumer.php @@ -0,0 +1,173 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Monitor; + +use Predis\ClientInterface; +use Predis\Connection\AggregateConnectionInterface; +use Predis\NotSupportedException; + +/** + * Redis MONITOR consumer. + * + * @author Daniele Alessandri + */ +class Consumer implements \Iterator +{ + private $client; + private $valid; + private $position; + + /** + * @param ClientInterface $client Client instance used by the consumer. + */ + public function __construct(ClientInterface $client) + { + $this->assertClient($client); + + $this->client = $client; + + $this->start(); + } + + /** + * Automatically stops the consumer when the garbage collector kicks in. + */ + public function __destruct() + { + $this->stop(); + } + + /** + * Checks if the passed client instance satisfies the required conditions + * needed to initialize a monitor consumer. + * + * @param ClientInterface $client Client instance used by the consumer. + * + * @throws NotSupportedException + */ + private function assertClient(ClientInterface $client) + { + if ($client->getConnection() instanceof AggregateConnectionInterface) { + throw new NotSupportedException( + 'Cannot initialize a monitor consumer over aggregate connections.' + ); + } + + if ($client->getProfile()->supportsCommand('MONITOR') === false) { + throw new NotSupportedException("The current profile does not support 'MONITOR'."); + } + } + + /** + * Initializes the consumer and sends the MONITOR command to the server. + */ + protected function start() + { + $this->client->executeCommand( + $this->client->createCommand('MONITOR') + ); + $this->valid = true; + } + + /** + * Stops the consumer. Internally this is done by disconnecting from server + * since there is no way to terminate the stream initialized by MONITOR. + */ + public function stop() + { + $this->client->disconnect(); + $this->valid = false; + } + + /** + * {@inheritdoc} + */ + public function rewind() + { + // NOOP + } + + /** + * Returns the last message payload retrieved from the server. + * + * @return object + */ + public function current() + { + return $this->getValue(); + } + + /** + * {@inheritdoc} + */ + public function key() + { + return $this->position; + } + + /** + * {@inheritdoc} + */ + public function next() + { + ++$this->position; + } + + /** + * Checks if the the consumer is still in a valid state to continue. + * + * @return bool + */ + public function valid() + { + return $this->valid; + } + + /** + * Waits for a new message from the server generated by MONITOR and returns + * it when available. + * + * @return object + */ + private function getValue() + { + $database = 0; + $client = null; + $event = $this->client->getConnection()->read(); + + $callback = function ($matches) use (&$database, &$client) { + if (2 === $count = count($matches)) { + // Redis <= 2.4 + $database = (int) $matches[1]; + } + + if (4 === $count) { + // Redis >= 2.6 + $database = (int) $matches[2]; + $client = $matches[3]; + } + + return ' '; + }; + + $event = preg_replace_callback('/ \(db (\d+)\) | \[(\d+) (.*?)\] /', $callback, $event, 1); + @list($timestamp, $command, $arguments) = explode(' ', $event, 3); + + return (object) array( + 'timestamp' => (float) $timestamp, + 'database' => $database, + 'client' => $client, + 'command' => substr($command, 1, -1), + 'arguments' => $arguments, + ); + } +} diff --git a/vendor/predis/predis/src/NotSupportedException.php b/vendor/predis/predis/src/NotSupportedException.php new file mode 100644 index 000000000..be82aba72 --- /dev/null +++ b/vendor/predis/predis/src/NotSupportedException.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis; + +/** + * Exception class thrown when trying to use features not supported by certain + * classes or abstractions of Predis. + * + * @author Daniele Alessandri + */ +class NotSupportedException extends PredisException +{ +} diff --git a/vendor/predis/predis/src/Pipeline/Atomic.php b/vendor/predis/predis/src/Pipeline/Atomic.php new file mode 100644 index 000000000..1c9c92aa2 --- /dev/null +++ b/vendor/predis/predis/src/Pipeline/Atomic.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Pipeline; + +use Predis\ClientException; +use Predis\ClientInterface; +use Predis\Connection\ConnectionInterface; +use Predis\Connection\NodeConnectionInterface; +use Predis\Response\ErrorInterface as ErrorResponseInterface; +use Predis\Response\ResponseInterface; +use Predis\Response\ServerException; + +/** + * Command pipeline wrapped into a MULTI / EXEC transaction. + * + * @author Daniele Alessandri + */ +class Atomic extends Pipeline +{ + /** + * {@inheritdoc} + */ + public function __construct(ClientInterface $client) + { + if (!$client->getProfile()->supportsCommands(array('multi', 'exec', 'discard'))) { + throw new ClientException( + "The current profile does not support 'MULTI', 'EXEC' and 'DISCARD'." + ); + } + + parent::__construct($client); + } + + /** + * {@inheritdoc} + */ + protected function getConnection() + { + $connection = $this->getClient()->getConnection(); + + if (!$connection instanceof NodeConnectionInterface) { + $class = __CLASS__; + + throw new ClientException("The class '$class' does not support aggregate connections."); + } + + return $connection; + } + + /** + * {@inheritdoc} + */ + protected function executePipeline(ConnectionInterface $connection, \SplQueue $commands) + { + $profile = $this->getClient()->getProfile(); + $connection->executeCommand($profile->createCommand('multi')); + + foreach ($commands as $command) { + $connection->writeRequest($command); + } + + foreach ($commands as $command) { + $response = $connection->readResponse($command); + + if ($response instanceof ErrorResponseInterface) { + $connection->executeCommand($profile->createCommand('discard')); + throw new ServerException($response->getMessage()); + } + } + + $executed = $connection->executeCommand($profile->createCommand('exec')); + + if (!isset($executed)) { + // TODO: should be throwing a more appropriate exception. + throw new ClientException( + 'The underlying transaction has been aborted by the server.' + ); + } + + if (count($executed) !== count($commands)) { + $expected = count($commands); + $received = count($executed); + + throw new ClientException( + "Invalid number of responses [expected $expected, received $received]." + ); + } + + $responses = array(); + $sizeOfPipe = count($commands); + $exceptions = $this->throwServerExceptions(); + + for ($i = 0; $i < $sizeOfPipe; ++$i) { + $command = $commands->dequeue(); + $response = $executed[$i]; + + if (!$response instanceof ResponseInterface) { + $responses[] = $command->parseResponse($response); + } elseif ($response instanceof ErrorResponseInterface && $exceptions) { + $this->exception($connection, $response); + } else { + $responses[] = $response; + } + + unset($executed[$i]); + } + + return $responses; + } +} diff --git a/vendor/predis/predis/src/Pipeline/ConnectionErrorProof.php b/vendor/predis/predis/src/Pipeline/ConnectionErrorProof.php new file mode 100644 index 000000000..d3bc732e4 --- /dev/null +++ b/vendor/predis/predis/src/Pipeline/ConnectionErrorProof.php @@ -0,0 +1,130 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Pipeline; + +use Predis\CommunicationException; +use Predis\Connection\Aggregate\ClusterInterface; +use Predis\Connection\ConnectionInterface; +use Predis\Connection\NodeConnectionInterface; +use Predis\NotSupportedException; + +/** + * Command pipeline that does not throw exceptions on connection errors, but + * returns the exception instances as the rest of the response elements. + * + * @todo Awful naming! + * + * @author Daniele Alessandri + */ +class ConnectionErrorProof extends Pipeline +{ + /** + * {@inheritdoc} + */ + protected function getConnection() + { + return $this->getClient()->getConnection(); + } + + /** + * {@inheritdoc} + */ + protected function executePipeline(ConnectionInterface $connection, \SplQueue $commands) + { + if ($connection instanceof NodeConnectionInterface) { + return $this->executeSingleNode($connection, $commands); + } elseif ($connection instanceof ClusterInterface) { + return $this->executeCluster($connection, $commands); + } else { + $class = get_class($connection); + + throw new NotSupportedException("The connection class '$class' is not supported."); + } + } + + /** + * {@inheritdoc} + */ + protected function executeSingleNode(NodeConnectionInterface $connection, \SplQueue $commands) + { + $responses = array(); + $sizeOfPipe = count($commands); + + foreach ($commands as $command) { + try { + $connection->writeRequest($command); + } catch (CommunicationException $exception) { + return array_fill(0, $sizeOfPipe, $exception); + } + } + + for ($i = 0; $i < $sizeOfPipe; ++$i) { + $command = $commands->dequeue(); + + try { + $responses[$i] = $connection->readResponse($command); + } catch (CommunicationException $exception) { + $add = count($commands) - count($responses); + $responses = array_merge($responses, array_fill(0, $add, $exception)); + + break; + } + } + + return $responses; + } + + /** + * {@inheritdoc} + */ + protected function executeCluster(ClusterInterface $connection, \SplQueue $commands) + { + $responses = array(); + $sizeOfPipe = count($commands); + $exceptions = array(); + + foreach ($commands as $command) { + $cmdConnection = $connection->getConnection($command); + + if (isset($exceptions[spl_object_hash($cmdConnection)])) { + continue; + } + + try { + $cmdConnection->writeRequest($command); + } catch (CommunicationException $exception) { + $exceptions[spl_object_hash($cmdConnection)] = $exception; + } + } + + for ($i = 0; $i < $sizeOfPipe; ++$i) { + $command = $commands->dequeue(); + + $cmdConnection = $connection->getConnection($command); + $connectionHash = spl_object_hash($cmdConnection); + + if (isset($exceptions[$connectionHash])) { + $responses[$i] = $exceptions[$connectionHash]; + continue; + } + + try { + $responses[$i] = $cmdConnection->readResponse($command); + } catch (CommunicationException $exception) { + $responses[$i] = $exception; + $exceptions[$connectionHash] = $exception; + } + } + + return $responses; + } +} diff --git a/vendor/predis/predis/src/Pipeline/FireAndForget.php b/vendor/predis/predis/src/Pipeline/FireAndForget.php new file mode 100644 index 000000000..95a062b64 --- /dev/null +++ b/vendor/predis/predis/src/Pipeline/FireAndForget.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Pipeline; + +use Predis\Connection\ConnectionInterface; + +/** + * Command pipeline that writes commands to the servers but discards responses. + * + * @author Daniele Alessandri + */ +class FireAndForget extends Pipeline +{ + /** + * {@inheritdoc} + */ + protected function executePipeline(ConnectionInterface $connection, \SplQueue $commands) + { + while (!$commands->isEmpty()) { + $connection->writeRequest($commands->dequeue()); + } + + $connection->disconnect(); + + return array(); + } +} diff --git a/vendor/predis/predis/src/Pipeline/Pipeline.php b/vendor/predis/predis/src/Pipeline/Pipeline.php new file mode 100644 index 000000000..cf9c59e4f --- /dev/null +++ b/vendor/predis/predis/src/Pipeline/Pipeline.php @@ -0,0 +1,247 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Pipeline; + +use Predis\ClientContextInterface; +use Predis\ClientException; +use Predis\ClientInterface; +use Predis\Command\CommandInterface; +use Predis\Connection\Aggregate\ReplicationInterface; +use Predis\Connection\ConnectionInterface; +use Predis\Response\ErrorInterface as ErrorResponseInterface; +use Predis\Response\ResponseInterface; +use Predis\Response\ServerException; + +/** + * Implementation of a command pipeline in which write and read operations of + * Redis commands are pipelined to alleviate the effects of network round-trips. + * + * {@inheritdoc} + * + * @author Daniele Alessandri + */ +class Pipeline implements ClientContextInterface +{ + private $client; + private $pipeline; + + private $responses = array(); + private $running = false; + + /** + * @param ClientInterface $client Client instance used by the context. + */ + public function __construct(ClientInterface $client) + { + $this->client = $client; + $this->pipeline = new \SplQueue(); + } + + /** + * Queues a command into the pipeline buffer. + * + * @param string $method Command ID. + * @param array $arguments Arguments for the command. + * + * @return $this + */ + public function __call($method, $arguments) + { + $command = $this->client->createCommand($method, $arguments); + $this->recordCommand($command); + + return $this; + } + + /** + * Queues a command instance into the pipeline buffer. + * + * @param CommandInterface $command Command to be queued in the buffer. + */ + protected function recordCommand(CommandInterface $command) + { + $this->pipeline->enqueue($command); + } + + /** + * Queues a command instance into the pipeline buffer. + * + * @param CommandInterface $command Command instance to be queued in the buffer. + * + * @return $this + */ + public function executeCommand(CommandInterface $command) + { + $this->recordCommand($command); + + return $this; + } + + /** + * Throws an exception on -ERR responses returned by Redis. + * + * @param ConnectionInterface $connection Redis connection that returned the error. + * @param ErrorResponseInterface $response Instance of the error response. + * + * @throws ServerException + */ + protected function exception(ConnectionInterface $connection, ErrorResponseInterface $response) + { + $connection->disconnect(); + $message = $response->getMessage(); + + throw new ServerException($message); + } + + /** + * Returns the underlying connection to be used by the pipeline. + * + * @return ConnectionInterface + */ + protected function getConnection() + { + $connection = $this->getClient()->getConnection(); + + if ($connection instanceof ReplicationInterface) { + $connection->switchTo('master'); + } + + return $connection; + } + + /** + * Implements the logic to flush the queued commands and read the responses + * from the current connection. + * + * @param ConnectionInterface $connection Current connection instance. + * @param \SplQueue $commands Queued commands. + * + * @return array + */ + protected function executePipeline(ConnectionInterface $connection, \SplQueue $commands) + { + foreach ($commands as $command) { + $connection->writeRequest($command); + } + + $responses = array(); + $exceptions = $this->throwServerExceptions(); + + while (!$commands->isEmpty()) { + $command = $commands->dequeue(); + $response = $connection->readResponse($command); + + if (!$response instanceof ResponseInterface) { + $responses[] = $command->parseResponse($response); + } elseif ($response instanceof ErrorResponseInterface && $exceptions) { + $this->exception($connection, $response); + } else { + $responses[] = $response; + } + } + + return $responses; + } + + /** + * Flushes the buffer holding all of the commands queued so far. + * + * @param bool $send Specifies if the commands in the buffer should be sent to Redis. + * + * @return $this + */ + public function flushPipeline($send = true) + { + if ($send && !$this->pipeline->isEmpty()) { + $responses = $this->executePipeline($this->getConnection(), $this->pipeline); + $this->responses = array_merge($this->responses, $responses); + } else { + $this->pipeline = new \SplQueue(); + } + + return $this; + } + + /** + * Marks the running status of the pipeline. + * + * @param bool $bool Sets the running status of the pipeline. + * + * @throws ClientException + */ + private function setRunning($bool) + { + if ($bool && $this->running) { + throw new ClientException('The current pipeline context is already being executed.'); + } + + $this->running = $bool; + } + + /** + * Handles the actual execution of the whole pipeline. + * + * @param mixed $callable Optional callback for execution. + * + * @throws \Exception + * @throws \InvalidArgumentException + * + * @return array + */ + public function execute($callable = null) + { + if ($callable && !is_callable($callable)) { + throw new \InvalidArgumentException('The argument must be a callable object.'); + } + + $exception = null; + $this->setRunning(true); + + try { + if ($callable) { + call_user_func($callable, $this); + } + + $this->flushPipeline(); + } catch (\Exception $exception) { + // NOOP + } + + $this->setRunning(false); + + if ($exception) { + throw $exception; + } + + return $this->responses; + } + + /** + * Returns if the pipeline should throw exceptions on server errors. + * + * @return bool + */ + protected function throwServerExceptions() + { + return (bool) $this->client->getOptions()->exceptions; + } + + /** + * Returns the underlying client instance used by the pipeline object. + * + * @return ClientInterface + */ + public function getClient() + { + return $this->client; + } +} diff --git a/vendor/predis/predis/src/PredisException.php b/vendor/predis/predis/src/PredisException.php new file mode 100644 index 000000000..122bde16d --- /dev/null +++ b/vendor/predis/predis/src/PredisException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis; + +/** + * Base exception class for Predis-related errors. + * + * @author Daniele Alessandri + */ +abstract class PredisException extends \Exception +{ +} diff --git a/vendor/predis/predis/src/Profile/Factory.php b/vendor/predis/predis/src/Profile/Factory.php new file mode 100644 index 000000000..d4907a232 --- /dev/null +++ b/vendor/predis/predis/src/Profile/Factory.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Profile; + +use Predis\ClientException; + +/** + * Factory class for creating profile instances from strings. + * + * @author Daniele Alessandri + */ +final class Factory +{ + private static $profiles = array( + '2.0' => 'Predis\Profile\RedisVersion200', + '2.2' => 'Predis\Profile\RedisVersion220', + '2.4' => 'Predis\Profile\RedisVersion240', + '2.6' => 'Predis\Profile\RedisVersion260', + '2.8' => 'Predis\Profile\RedisVersion280', + '3.0' => 'Predis\Profile\RedisVersion300', + '3.2' => 'Predis\Profile\RedisVersion320', + 'dev' => 'Predis\Profile\RedisUnstable', + 'default' => 'Predis\Profile\RedisVersion320', + ); + + /** + * + */ + private function __construct() + { + // NOOP + } + + /** + * Returns the default server profile. + * + * @return ProfileInterface + */ + public static function getDefault() + { + return self::get('default'); + } + + /** + * Returns the development server profile. + * + * @return ProfileInterface + */ + public static function getDevelopment() + { + return self::get('dev'); + } + + /** + * Registers a new server profile. + * + * @param string $alias Profile version or alias. + * @param string $class FQN of a class implementing Predis\Profile\ProfileInterface. + * + * @throws \InvalidArgumentException + */ + public static function define($alias, $class) + { + $reflection = new \ReflectionClass($class); + + if (!$reflection->isSubclassOf('Predis\Profile\ProfileInterface')) { + throw new \InvalidArgumentException("The class '$class' is not a valid profile class."); + } + + self::$profiles[$alias] = $class; + } + + /** + * Returns the specified server profile. + * + * @param string $version Profile version or alias. + * + * @throws ClientException + * + * @return ProfileInterface + */ + public static function get($version) + { + if (!isset(self::$profiles[$version])) { + throw new ClientException("Unknown server profile: '$version'."); + } + + $profile = self::$profiles[$version]; + + return new $profile(); + } +} diff --git a/vendor/predis/predis/src/Profile/ProfileInterface.php b/vendor/predis/predis/src/Profile/ProfileInterface.php new file mode 100644 index 000000000..abe71aa63 --- /dev/null +++ b/vendor/predis/predis/src/Profile/ProfileInterface.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Profile; + +use Predis\Command\CommandInterface; + +/** + * A profile defines all the features and commands supported by certain versions + * of Redis. Instances of Predis\Client should use a server profile matching the + * version of Redis being used. + * + * @author Daniele Alessandri + */ +interface ProfileInterface +{ + /** + * Returns the profile version corresponding to the Redis version. + * + * @return string + */ + public function getVersion(); + + /** + * Checks if the profile supports the specified command. + * + * @param string $commandID Command ID. + * + * @return bool + */ + public function supportsCommand($commandID); + + /** + * Checks if the profile supports the specified list of commands. + * + * @param array $commandIDs List of command IDs. + * + * @return string + */ + public function supportsCommands(array $commandIDs); + + /** + * Creates a new command instance. + * + * @param string $commandID Command ID. + * @param array $arguments Arguments for the command. + * + * @return CommandInterface + */ + public function createCommand($commandID, array $arguments = array()); +} diff --git a/vendor/predis/predis/src/Profile/RedisProfile.php b/vendor/predis/predis/src/Profile/RedisProfile.php new file mode 100644 index 000000000..3ef316886 --- /dev/null +++ b/vendor/predis/predis/src/Profile/RedisProfile.php @@ -0,0 +1,146 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Profile; + +use Predis\ClientException; +use Predis\Command\Processor\ProcessorInterface; + +/** + * Base class implementing common functionalities for Redis server profiles. + * + * @author Daniele Alessandri + */ +abstract class RedisProfile implements ProfileInterface +{ + private $commands; + private $processor; + + /** + * + */ + public function __construct() + { + $this->commands = $this->getSupportedCommands(); + } + + /** + * Returns a map of all the commands supported by the profile and their + * actual PHP classes. + * + * @return array + */ + abstract protected function getSupportedCommands(); + + /** + * {@inheritdoc} + */ + public function supportsCommand($commandID) + { + return isset($this->commands[strtoupper($commandID)]); + } + + /** + * {@inheritdoc} + */ + public function supportsCommands(array $commandIDs) + { + foreach ($commandIDs as $commandID) { + if (!$this->supportsCommand($commandID)) { + return false; + } + } + + return true; + } + + /** + * Returns the fully-qualified name of a class representing the specified + * command ID registered in the current server profile. + * + * @param string $commandID Command ID. + * + * @return string|null + */ + public function getCommandClass($commandID) + { + if (isset($this->commands[$commandID = strtoupper($commandID)])) { + return $this->commands[$commandID]; + } + } + + /** + * {@inheritdoc} + */ + public function createCommand($commandID, array $arguments = array()) + { + $commandID = strtoupper($commandID); + + if (!isset($this->commands[$commandID])) { + throw new ClientException("Command '$commandID' is not a registered Redis command."); + } + + $commandClass = $this->commands[$commandID]; + $command = new $commandClass(); + $command->setArguments($arguments); + + if (isset($this->processor)) { + $this->processor->process($command); + } + + return $command; + } + + /** + * Defines a new command in the server profile. + * + * @param string $commandID Command ID. + * @param string $class Fully-qualified name of a Predis\Command\CommandInterface. + * + * @throws \InvalidArgumentException + */ + public function defineCommand($commandID, $class) + { + $reflection = new \ReflectionClass($class); + + if (!$reflection->isSubclassOf('Predis\Command\CommandInterface')) { + throw new \InvalidArgumentException("The class '$class' is not a valid command class."); + } + + $this->commands[strtoupper($commandID)] = $class; + } + + /** + * {@inheritdoc} + */ + public function setProcessor(ProcessorInterface $processor = null) + { + $this->processor = $processor; + } + + /** + * {@inheritdoc} + */ + public function getProcessor() + { + return $this->processor; + } + + /** + * Returns the version of server profile as its string representation. + * + * @return string + */ + public function __toString() + { + return $this->getVersion(); + } +} diff --git a/vendor/predis/predis/src/Profile/RedisUnstable.php b/vendor/predis/predis/src/Profile/RedisUnstable.php new file mode 100644 index 000000000..573cc9e6a --- /dev/null +++ b/vendor/predis/predis/src/Profile/RedisUnstable.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Profile; + +/** + * Server profile for the current unstable version of Redis. + * + * @author Daniele Alessandri + */ +class RedisUnstable extends RedisVersion320 +{ + /** + * {@inheritdoc} + */ + public function getVersion() + { + return '3.2'; + } + + /** + * {@inheritdoc} + */ + public function getSupportedCommands() + { + return array_merge(parent::getSupportedCommands(), array( + // EMPTY + )); + } +} diff --git a/vendor/predis/predis/src/Profile/RedisVersion200.php b/vendor/predis/predis/src/Profile/RedisVersion200.php new file mode 100644 index 000000000..234d53c00 --- /dev/null +++ b/vendor/predis/predis/src/Profile/RedisVersion200.php @@ -0,0 +1,173 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Profile; + +/** + * Server profile for Redis 2.0. + * + * @author Daniele Alessandri + */ +class RedisVersion200 extends RedisProfile +{ + /** + * {@inheritdoc} + */ + public function getVersion() + { + return '2.0'; + } + + /** + * {@inheritdoc} + */ + public function getSupportedCommands() + { + return array( + /* ---------------- Redis 1.2 ---------------- */ + + /* commands operating on the key space */ + 'EXISTS' => 'Predis\Command\KeyExists', + 'DEL' => 'Predis\Command\KeyDelete', + 'TYPE' => 'Predis\Command\KeyType', + 'KEYS' => 'Predis\Command\KeyKeys', + 'RANDOMKEY' => 'Predis\Command\KeyRandom', + 'RENAME' => 'Predis\Command\KeyRename', + 'RENAMENX' => 'Predis\Command\KeyRenamePreserve', + 'EXPIRE' => 'Predis\Command\KeyExpire', + 'EXPIREAT' => 'Predis\Command\KeyExpireAt', + 'TTL' => 'Predis\Command\KeyTimeToLive', + 'MOVE' => 'Predis\Command\KeyMove', + 'SORT' => 'Predis\Command\KeySort', + + /* commands operating on string values */ + 'SET' => 'Predis\Command\StringSet', + 'SETNX' => 'Predis\Command\StringSetPreserve', + 'MSET' => 'Predis\Command\StringSetMultiple', + 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve', + 'GET' => 'Predis\Command\StringGet', + 'MGET' => 'Predis\Command\StringGetMultiple', + 'GETSET' => 'Predis\Command\StringGetSet', + 'INCR' => 'Predis\Command\StringIncrement', + 'INCRBY' => 'Predis\Command\StringIncrementBy', + 'DECR' => 'Predis\Command\StringDecrement', + 'DECRBY' => 'Predis\Command\StringDecrementBy', + + /* commands operating on lists */ + 'RPUSH' => 'Predis\Command\ListPushTail', + 'LPUSH' => 'Predis\Command\ListPushHead', + 'LLEN' => 'Predis\Command\ListLength', + 'LRANGE' => 'Predis\Command\ListRange', + 'LTRIM' => 'Predis\Command\ListTrim', + 'LINDEX' => 'Predis\Command\ListIndex', + 'LSET' => 'Predis\Command\ListSet', + 'LREM' => 'Predis\Command\ListRemove', + 'LPOP' => 'Predis\Command\ListPopFirst', + 'RPOP' => 'Predis\Command\ListPopLast', + 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead', + + /* commands operating on sets */ + 'SADD' => 'Predis\Command\SetAdd', + 'SREM' => 'Predis\Command\SetRemove', + 'SPOP' => 'Predis\Command\SetPop', + 'SMOVE' => 'Predis\Command\SetMove', + 'SCARD' => 'Predis\Command\SetCardinality', + 'SISMEMBER' => 'Predis\Command\SetIsMember', + 'SINTER' => 'Predis\Command\SetIntersection', + 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore', + 'SUNION' => 'Predis\Command\SetUnion', + 'SUNIONSTORE' => 'Predis\Command\SetUnionStore', + 'SDIFF' => 'Predis\Command\SetDifference', + 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore', + 'SMEMBERS' => 'Predis\Command\SetMembers', + 'SRANDMEMBER' => 'Predis\Command\SetRandomMember', + + /* commands operating on sorted sets */ + 'ZADD' => 'Predis\Command\ZSetAdd', + 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy', + 'ZREM' => 'Predis\Command\ZSetRemove', + 'ZRANGE' => 'Predis\Command\ZSetRange', + 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange', + 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore', + 'ZCARD' => 'Predis\Command\ZSetCardinality', + 'ZSCORE' => 'Predis\Command\ZSetScore', + 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore', + + /* connection related commands */ + 'PING' => 'Predis\Command\ConnectionPing', + 'AUTH' => 'Predis\Command\ConnectionAuth', + 'SELECT' => 'Predis\Command\ConnectionSelect', + 'ECHO' => 'Predis\Command\ConnectionEcho', + 'QUIT' => 'Predis\Command\ConnectionQuit', + + /* remote server control commands */ + 'INFO' => 'Predis\Command\ServerInfo', + 'SLAVEOF' => 'Predis\Command\ServerSlaveOf', + 'MONITOR' => 'Predis\Command\ServerMonitor', + 'DBSIZE' => 'Predis\Command\ServerDatabaseSize', + 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase', + 'FLUSHALL' => 'Predis\Command\ServerFlushAll', + 'SAVE' => 'Predis\Command\ServerSave', + 'BGSAVE' => 'Predis\Command\ServerBackgroundSave', + 'LASTSAVE' => 'Predis\Command\ServerLastSave', + 'SHUTDOWN' => 'Predis\Command\ServerShutdown', + 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF', + + /* ---------------- Redis 2.0 ---------------- */ + + /* commands operating on string values */ + 'SETEX' => 'Predis\Command\StringSetExpire', + 'APPEND' => 'Predis\Command\StringAppend', + 'SUBSTR' => 'Predis\Command\StringSubstr', + + /* commands operating on lists */ + 'BLPOP' => 'Predis\Command\ListPopFirstBlocking', + 'BRPOP' => 'Predis\Command\ListPopLastBlocking', + + /* commands operating on sorted sets */ + 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore', + 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore', + 'ZCOUNT' => 'Predis\Command\ZSetCount', + 'ZRANK' => 'Predis\Command\ZSetRank', + 'ZREVRANK' => 'Predis\Command\ZSetReverseRank', + 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank', + + /* commands operating on hashes */ + 'HSET' => 'Predis\Command\HashSet', + 'HSETNX' => 'Predis\Command\HashSetPreserve', + 'HMSET' => 'Predis\Command\HashSetMultiple', + 'HINCRBY' => 'Predis\Command\HashIncrementBy', + 'HGET' => 'Predis\Command\HashGet', + 'HMGET' => 'Predis\Command\HashGetMultiple', + 'HDEL' => 'Predis\Command\HashDelete', + 'HEXISTS' => 'Predis\Command\HashExists', + 'HLEN' => 'Predis\Command\HashLength', + 'HKEYS' => 'Predis\Command\HashKeys', + 'HVALS' => 'Predis\Command\HashValues', + 'HGETALL' => 'Predis\Command\HashGetAll', + + /* transactions */ + 'MULTI' => 'Predis\Command\TransactionMulti', + 'EXEC' => 'Predis\Command\TransactionExec', + 'DISCARD' => 'Predis\Command\TransactionDiscard', + + /* publish - subscribe */ + 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe', + 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe', + 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern', + 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern', + 'PUBLISH' => 'Predis\Command\PubSubPublish', + + /* remote server control commands */ + 'CONFIG' => 'Predis\Command\ServerConfig', + ); + } +} diff --git a/vendor/predis/predis/src/Profile/RedisVersion220.php b/vendor/predis/predis/src/Profile/RedisVersion220.php new file mode 100644 index 000000000..899014e28 --- /dev/null +++ b/vendor/predis/predis/src/Profile/RedisVersion220.php @@ -0,0 +1,202 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Profile; + +/** + * Server profile for Redis 2.2. + * + * @author Daniele Alessandri + */ +class RedisVersion220 extends RedisProfile +{ + /** + * {@inheritdoc} + */ + public function getVersion() + { + return '2.2'; + } + + /** + * {@inheritdoc} + */ + public function getSupportedCommands() + { + return array( + /* ---------------- Redis 1.2 ---------------- */ + + /* commands operating on the key space */ + 'EXISTS' => 'Predis\Command\KeyExists', + 'DEL' => 'Predis\Command\KeyDelete', + 'TYPE' => 'Predis\Command\KeyType', + 'KEYS' => 'Predis\Command\KeyKeys', + 'RANDOMKEY' => 'Predis\Command\KeyRandom', + 'RENAME' => 'Predis\Command\KeyRename', + 'RENAMENX' => 'Predis\Command\KeyRenamePreserve', + 'EXPIRE' => 'Predis\Command\KeyExpire', + 'EXPIREAT' => 'Predis\Command\KeyExpireAt', + 'TTL' => 'Predis\Command\KeyTimeToLive', + 'MOVE' => 'Predis\Command\KeyMove', + 'SORT' => 'Predis\Command\KeySort', + + /* commands operating on string values */ + 'SET' => 'Predis\Command\StringSet', + 'SETNX' => 'Predis\Command\StringSetPreserve', + 'MSET' => 'Predis\Command\StringSetMultiple', + 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve', + 'GET' => 'Predis\Command\StringGet', + 'MGET' => 'Predis\Command\StringGetMultiple', + 'GETSET' => 'Predis\Command\StringGetSet', + 'INCR' => 'Predis\Command\StringIncrement', + 'INCRBY' => 'Predis\Command\StringIncrementBy', + 'DECR' => 'Predis\Command\StringDecrement', + 'DECRBY' => 'Predis\Command\StringDecrementBy', + + /* commands operating on lists */ + 'RPUSH' => 'Predis\Command\ListPushTail', + 'LPUSH' => 'Predis\Command\ListPushHead', + 'LLEN' => 'Predis\Command\ListLength', + 'LRANGE' => 'Predis\Command\ListRange', + 'LTRIM' => 'Predis\Command\ListTrim', + 'LINDEX' => 'Predis\Command\ListIndex', + 'LSET' => 'Predis\Command\ListSet', + 'LREM' => 'Predis\Command\ListRemove', + 'LPOP' => 'Predis\Command\ListPopFirst', + 'RPOP' => 'Predis\Command\ListPopLast', + 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead', + + /* commands operating on sets */ + 'SADD' => 'Predis\Command\SetAdd', + 'SREM' => 'Predis\Command\SetRemove', + 'SPOP' => 'Predis\Command\SetPop', + 'SMOVE' => 'Predis\Command\SetMove', + 'SCARD' => 'Predis\Command\SetCardinality', + 'SISMEMBER' => 'Predis\Command\SetIsMember', + 'SINTER' => 'Predis\Command\SetIntersection', + 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore', + 'SUNION' => 'Predis\Command\SetUnion', + 'SUNIONSTORE' => 'Predis\Command\SetUnionStore', + 'SDIFF' => 'Predis\Command\SetDifference', + 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore', + 'SMEMBERS' => 'Predis\Command\SetMembers', + 'SRANDMEMBER' => 'Predis\Command\SetRandomMember', + + /* commands operating on sorted sets */ + 'ZADD' => 'Predis\Command\ZSetAdd', + 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy', + 'ZREM' => 'Predis\Command\ZSetRemove', + 'ZRANGE' => 'Predis\Command\ZSetRange', + 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange', + 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore', + 'ZCARD' => 'Predis\Command\ZSetCardinality', + 'ZSCORE' => 'Predis\Command\ZSetScore', + 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore', + + /* connection related commands */ + 'PING' => 'Predis\Command\ConnectionPing', + 'AUTH' => 'Predis\Command\ConnectionAuth', + 'SELECT' => 'Predis\Command\ConnectionSelect', + 'ECHO' => 'Predis\Command\ConnectionEcho', + 'QUIT' => 'Predis\Command\ConnectionQuit', + + /* remote server control commands */ + 'INFO' => 'Predis\Command\ServerInfo', + 'SLAVEOF' => 'Predis\Command\ServerSlaveOf', + 'MONITOR' => 'Predis\Command\ServerMonitor', + 'DBSIZE' => 'Predis\Command\ServerDatabaseSize', + 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase', + 'FLUSHALL' => 'Predis\Command\ServerFlushAll', + 'SAVE' => 'Predis\Command\ServerSave', + 'BGSAVE' => 'Predis\Command\ServerBackgroundSave', + 'LASTSAVE' => 'Predis\Command\ServerLastSave', + 'SHUTDOWN' => 'Predis\Command\ServerShutdown', + 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF', + + /* ---------------- Redis 2.0 ---------------- */ + + /* commands operating on string values */ + 'SETEX' => 'Predis\Command\StringSetExpire', + 'APPEND' => 'Predis\Command\StringAppend', + 'SUBSTR' => 'Predis\Command\StringSubstr', + + /* commands operating on lists */ + 'BLPOP' => 'Predis\Command\ListPopFirstBlocking', + 'BRPOP' => 'Predis\Command\ListPopLastBlocking', + + /* commands operating on sorted sets */ + 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore', + 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore', + 'ZCOUNT' => 'Predis\Command\ZSetCount', + 'ZRANK' => 'Predis\Command\ZSetRank', + 'ZREVRANK' => 'Predis\Command\ZSetReverseRank', + 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank', + + /* commands operating on hashes */ + 'HSET' => 'Predis\Command\HashSet', + 'HSETNX' => 'Predis\Command\HashSetPreserve', + 'HMSET' => 'Predis\Command\HashSetMultiple', + 'HINCRBY' => 'Predis\Command\HashIncrementBy', + 'HGET' => 'Predis\Command\HashGet', + 'HMGET' => 'Predis\Command\HashGetMultiple', + 'HDEL' => 'Predis\Command\HashDelete', + 'HEXISTS' => 'Predis\Command\HashExists', + 'HLEN' => 'Predis\Command\HashLength', + 'HKEYS' => 'Predis\Command\HashKeys', + 'HVALS' => 'Predis\Command\HashValues', + 'HGETALL' => 'Predis\Command\HashGetAll', + + /* transactions */ + 'MULTI' => 'Predis\Command\TransactionMulti', + 'EXEC' => 'Predis\Command\TransactionExec', + 'DISCARD' => 'Predis\Command\TransactionDiscard', + + /* publish - subscribe */ + 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe', + 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe', + 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern', + 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern', + 'PUBLISH' => 'Predis\Command\PubSubPublish', + + /* remote server control commands */ + 'CONFIG' => 'Predis\Command\ServerConfig', + + /* ---------------- Redis 2.2 ---------------- */ + + /* commands operating on the key space */ + 'PERSIST' => 'Predis\Command\KeyPersist', + + /* commands operating on string values */ + 'STRLEN' => 'Predis\Command\StringStrlen', + 'SETRANGE' => 'Predis\Command\StringSetRange', + 'GETRANGE' => 'Predis\Command\StringGetRange', + 'SETBIT' => 'Predis\Command\StringSetBit', + 'GETBIT' => 'Predis\Command\StringGetBit', + + /* commands operating on lists */ + 'RPUSHX' => 'Predis\Command\ListPushTailX', + 'LPUSHX' => 'Predis\Command\ListPushHeadX', + 'LINSERT' => 'Predis\Command\ListInsert', + 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking', + + /* commands operating on sorted sets */ + 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore', + + /* transactions */ + 'WATCH' => 'Predis\Command\TransactionWatch', + 'UNWATCH' => 'Predis\Command\TransactionUnwatch', + + /* remote server control commands */ + 'OBJECT' => 'Predis\Command\ServerObject', + 'SLOWLOG' => 'Predis\Command\ServerSlowlog', + ); + } +} diff --git a/vendor/predis/predis/src/Profile/RedisVersion240.php b/vendor/predis/predis/src/Profile/RedisVersion240.php new file mode 100644 index 000000000..0856c37c4 --- /dev/null +++ b/vendor/predis/predis/src/Profile/RedisVersion240.php @@ -0,0 +1,207 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Profile; + +/** + * Server profile for Redis 2.4. + * + * @author Daniele Alessandri + */ +class RedisVersion240 extends RedisProfile +{ + /** + * {@inheritdoc} + */ + public function getVersion() + { + return '2.4'; + } + + /** + * {@inheritdoc} + */ + public function getSupportedCommands() + { + return array( + /* ---------------- Redis 1.2 ---------------- */ + + /* commands operating on the key space */ + 'EXISTS' => 'Predis\Command\KeyExists', + 'DEL' => 'Predis\Command\KeyDelete', + 'TYPE' => 'Predis\Command\KeyType', + 'KEYS' => 'Predis\Command\KeyKeys', + 'RANDOMKEY' => 'Predis\Command\KeyRandom', + 'RENAME' => 'Predis\Command\KeyRename', + 'RENAMENX' => 'Predis\Command\KeyRenamePreserve', + 'EXPIRE' => 'Predis\Command\KeyExpire', + 'EXPIREAT' => 'Predis\Command\KeyExpireAt', + 'TTL' => 'Predis\Command\KeyTimeToLive', + 'MOVE' => 'Predis\Command\KeyMove', + 'SORT' => 'Predis\Command\KeySort', + + /* commands operating on string values */ + 'SET' => 'Predis\Command\StringSet', + 'SETNX' => 'Predis\Command\StringSetPreserve', + 'MSET' => 'Predis\Command\StringSetMultiple', + 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve', + 'GET' => 'Predis\Command\StringGet', + 'MGET' => 'Predis\Command\StringGetMultiple', + 'GETSET' => 'Predis\Command\StringGetSet', + 'INCR' => 'Predis\Command\StringIncrement', + 'INCRBY' => 'Predis\Command\StringIncrementBy', + 'DECR' => 'Predis\Command\StringDecrement', + 'DECRBY' => 'Predis\Command\StringDecrementBy', + + /* commands operating on lists */ + 'RPUSH' => 'Predis\Command\ListPushTail', + 'LPUSH' => 'Predis\Command\ListPushHead', + 'LLEN' => 'Predis\Command\ListLength', + 'LRANGE' => 'Predis\Command\ListRange', + 'LTRIM' => 'Predis\Command\ListTrim', + 'LINDEX' => 'Predis\Command\ListIndex', + 'LSET' => 'Predis\Command\ListSet', + 'LREM' => 'Predis\Command\ListRemove', + 'LPOP' => 'Predis\Command\ListPopFirst', + 'RPOP' => 'Predis\Command\ListPopLast', + 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead', + + /* commands operating on sets */ + 'SADD' => 'Predis\Command\SetAdd', + 'SREM' => 'Predis\Command\SetRemove', + 'SPOP' => 'Predis\Command\SetPop', + 'SMOVE' => 'Predis\Command\SetMove', + 'SCARD' => 'Predis\Command\SetCardinality', + 'SISMEMBER' => 'Predis\Command\SetIsMember', + 'SINTER' => 'Predis\Command\SetIntersection', + 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore', + 'SUNION' => 'Predis\Command\SetUnion', + 'SUNIONSTORE' => 'Predis\Command\SetUnionStore', + 'SDIFF' => 'Predis\Command\SetDifference', + 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore', + 'SMEMBERS' => 'Predis\Command\SetMembers', + 'SRANDMEMBER' => 'Predis\Command\SetRandomMember', + + /* commands operating on sorted sets */ + 'ZADD' => 'Predis\Command\ZSetAdd', + 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy', + 'ZREM' => 'Predis\Command\ZSetRemove', + 'ZRANGE' => 'Predis\Command\ZSetRange', + 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange', + 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore', + 'ZCARD' => 'Predis\Command\ZSetCardinality', + 'ZSCORE' => 'Predis\Command\ZSetScore', + 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore', + + /* connection related commands */ + 'PING' => 'Predis\Command\ConnectionPing', + 'AUTH' => 'Predis\Command\ConnectionAuth', + 'SELECT' => 'Predis\Command\ConnectionSelect', + 'ECHO' => 'Predis\Command\ConnectionEcho', + 'QUIT' => 'Predis\Command\ConnectionQuit', + + /* remote server control commands */ + 'INFO' => 'Predis\Command\ServerInfo', + 'SLAVEOF' => 'Predis\Command\ServerSlaveOf', + 'MONITOR' => 'Predis\Command\ServerMonitor', + 'DBSIZE' => 'Predis\Command\ServerDatabaseSize', + 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase', + 'FLUSHALL' => 'Predis\Command\ServerFlushAll', + 'SAVE' => 'Predis\Command\ServerSave', + 'BGSAVE' => 'Predis\Command\ServerBackgroundSave', + 'LASTSAVE' => 'Predis\Command\ServerLastSave', + 'SHUTDOWN' => 'Predis\Command\ServerShutdown', + 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF', + + /* ---------------- Redis 2.0 ---------------- */ + + /* commands operating on string values */ + 'SETEX' => 'Predis\Command\StringSetExpire', + 'APPEND' => 'Predis\Command\StringAppend', + 'SUBSTR' => 'Predis\Command\StringSubstr', + + /* commands operating on lists */ + 'BLPOP' => 'Predis\Command\ListPopFirstBlocking', + 'BRPOP' => 'Predis\Command\ListPopLastBlocking', + + /* commands operating on sorted sets */ + 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore', + 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore', + 'ZCOUNT' => 'Predis\Command\ZSetCount', + 'ZRANK' => 'Predis\Command\ZSetRank', + 'ZREVRANK' => 'Predis\Command\ZSetReverseRank', + 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank', + + /* commands operating on hashes */ + 'HSET' => 'Predis\Command\HashSet', + 'HSETNX' => 'Predis\Command\HashSetPreserve', + 'HMSET' => 'Predis\Command\HashSetMultiple', + 'HINCRBY' => 'Predis\Command\HashIncrementBy', + 'HGET' => 'Predis\Command\HashGet', + 'HMGET' => 'Predis\Command\HashGetMultiple', + 'HDEL' => 'Predis\Command\HashDelete', + 'HEXISTS' => 'Predis\Command\HashExists', + 'HLEN' => 'Predis\Command\HashLength', + 'HKEYS' => 'Predis\Command\HashKeys', + 'HVALS' => 'Predis\Command\HashValues', + 'HGETALL' => 'Predis\Command\HashGetAll', + + /* transactions */ + 'MULTI' => 'Predis\Command\TransactionMulti', + 'EXEC' => 'Predis\Command\TransactionExec', + 'DISCARD' => 'Predis\Command\TransactionDiscard', + + /* publish - subscribe */ + 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe', + 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe', + 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern', + 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern', + 'PUBLISH' => 'Predis\Command\PubSubPublish', + + /* remote server control commands */ + 'CONFIG' => 'Predis\Command\ServerConfig', + + /* ---------------- Redis 2.2 ---------------- */ + + /* commands operating on the key space */ + 'PERSIST' => 'Predis\Command\KeyPersist', + + /* commands operating on string values */ + 'STRLEN' => 'Predis\Command\StringStrlen', + 'SETRANGE' => 'Predis\Command\StringSetRange', + 'GETRANGE' => 'Predis\Command\StringGetRange', + 'SETBIT' => 'Predis\Command\StringSetBit', + 'GETBIT' => 'Predis\Command\StringGetBit', + + /* commands operating on lists */ + 'RPUSHX' => 'Predis\Command\ListPushTailX', + 'LPUSHX' => 'Predis\Command\ListPushHeadX', + 'LINSERT' => 'Predis\Command\ListInsert', + 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking', + + /* commands operating on sorted sets */ + 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore', + + /* transactions */ + 'WATCH' => 'Predis\Command\TransactionWatch', + 'UNWATCH' => 'Predis\Command\TransactionUnwatch', + + /* remote server control commands */ + 'OBJECT' => 'Predis\Command\ServerObject', + 'SLOWLOG' => 'Predis\Command\ServerSlowlog', + + /* ---------------- Redis 2.4 ---------------- */ + + /* remote server control commands */ + 'CLIENT' => 'Predis\Command\ServerClient', + ); + } +} diff --git a/vendor/predis/predis/src/Profile/RedisVersion260.php b/vendor/predis/predis/src/Profile/RedisVersion260.php new file mode 100644 index 000000000..ba5084aa0 --- /dev/null +++ b/vendor/predis/predis/src/Profile/RedisVersion260.php @@ -0,0 +1,235 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Profile; + +/** + * Server profile for Redis 2.6. + * + * @author Daniele Alessandri + */ +class RedisVersion260 extends RedisProfile +{ + /** + * {@inheritdoc} + */ + public function getVersion() + { + return '2.6'; + } + + /** + * {@inheritdoc} + */ + public function getSupportedCommands() + { + return array( + /* ---------------- Redis 1.2 ---------------- */ + + /* commands operating on the key space */ + 'EXISTS' => 'Predis\Command\KeyExists', + 'DEL' => 'Predis\Command\KeyDelete', + 'TYPE' => 'Predis\Command\KeyType', + 'KEYS' => 'Predis\Command\KeyKeys', + 'RANDOMKEY' => 'Predis\Command\KeyRandom', + 'RENAME' => 'Predis\Command\KeyRename', + 'RENAMENX' => 'Predis\Command\KeyRenamePreserve', + 'EXPIRE' => 'Predis\Command\KeyExpire', + 'EXPIREAT' => 'Predis\Command\KeyExpireAt', + 'TTL' => 'Predis\Command\KeyTimeToLive', + 'MOVE' => 'Predis\Command\KeyMove', + 'SORT' => 'Predis\Command\KeySort', + 'DUMP' => 'Predis\Command\KeyDump', + 'RESTORE' => 'Predis\Command\KeyRestore', + + /* commands operating on string values */ + 'SET' => 'Predis\Command\StringSet', + 'SETNX' => 'Predis\Command\StringSetPreserve', + 'MSET' => 'Predis\Command\StringSetMultiple', + 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve', + 'GET' => 'Predis\Command\StringGet', + 'MGET' => 'Predis\Command\StringGetMultiple', + 'GETSET' => 'Predis\Command\StringGetSet', + 'INCR' => 'Predis\Command\StringIncrement', + 'INCRBY' => 'Predis\Command\StringIncrementBy', + 'DECR' => 'Predis\Command\StringDecrement', + 'DECRBY' => 'Predis\Command\StringDecrementBy', + + /* commands operating on lists */ + 'RPUSH' => 'Predis\Command\ListPushTail', + 'LPUSH' => 'Predis\Command\ListPushHead', + 'LLEN' => 'Predis\Command\ListLength', + 'LRANGE' => 'Predis\Command\ListRange', + 'LTRIM' => 'Predis\Command\ListTrim', + 'LINDEX' => 'Predis\Command\ListIndex', + 'LSET' => 'Predis\Command\ListSet', + 'LREM' => 'Predis\Command\ListRemove', + 'LPOP' => 'Predis\Command\ListPopFirst', + 'RPOP' => 'Predis\Command\ListPopLast', + 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead', + + /* commands operating on sets */ + 'SADD' => 'Predis\Command\SetAdd', + 'SREM' => 'Predis\Command\SetRemove', + 'SPOP' => 'Predis\Command\SetPop', + 'SMOVE' => 'Predis\Command\SetMove', + 'SCARD' => 'Predis\Command\SetCardinality', + 'SISMEMBER' => 'Predis\Command\SetIsMember', + 'SINTER' => 'Predis\Command\SetIntersection', + 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore', + 'SUNION' => 'Predis\Command\SetUnion', + 'SUNIONSTORE' => 'Predis\Command\SetUnionStore', + 'SDIFF' => 'Predis\Command\SetDifference', + 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore', + 'SMEMBERS' => 'Predis\Command\SetMembers', + 'SRANDMEMBER' => 'Predis\Command\SetRandomMember', + + /* commands operating on sorted sets */ + 'ZADD' => 'Predis\Command\ZSetAdd', + 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy', + 'ZREM' => 'Predis\Command\ZSetRemove', + 'ZRANGE' => 'Predis\Command\ZSetRange', + 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange', + 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore', + 'ZCARD' => 'Predis\Command\ZSetCardinality', + 'ZSCORE' => 'Predis\Command\ZSetScore', + 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore', + + /* connection related commands */ + 'PING' => 'Predis\Command\ConnectionPing', + 'AUTH' => 'Predis\Command\ConnectionAuth', + 'SELECT' => 'Predis\Command\ConnectionSelect', + 'ECHO' => 'Predis\Command\ConnectionEcho', + 'QUIT' => 'Predis\Command\ConnectionQuit', + + /* remote server control commands */ + 'INFO' => 'Predis\Command\ServerInfoV26x', + 'SLAVEOF' => 'Predis\Command\ServerSlaveOf', + 'MONITOR' => 'Predis\Command\ServerMonitor', + 'DBSIZE' => 'Predis\Command\ServerDatabaseSize', + 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase', + 'FLUSHALL' => 'Predis\Command\ServerFlushAll', + 'SAVE' => 'Predis\Command\ServerSave', + 'BGSAVE' => 'Predis\Command\ServerBackgroundSave', + 'LASTSAVE' => 'Predis\Command\ServerLastSave', + 'SHUTDOWN' => 'Predis\Command\ServerShutdown', + 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF', + + /* ---------------- Redis 2.0 ---------------- */ + + /* commands operating on string values */ + 'SETEX' => 'Predis\Command\StringSetExpire', + 'APPEND' => 'Predis\Command\StringAppend', + 'SUBSTR' => 'Predis\Command\StringSubstr', + + /* commands operating on lists */ + 'BLPOP' => 'Predis\Command\ListPopFirstBlocking', + 'BRPOP' => 'Predis\Command\ListPopLastBlocking', + + /* commands operating on sorted sets */ + 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore', + 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore', + 'ZCOUNT' => 'Predis\Command\ZSetCount', + 'ZRANK' => 'Predis\Command\ZSetRank', + 'ZREVRANK' => 'Predis\Command\ZSetReverseRank', + 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank', + + /* commands operating on hashes */ + 'HSET' => 'Predis\Command\HashSet', + 'HSETNX' => 'Predis\Command\HashSetPreserve', + 'HMSET' => 'Predis\Command\HashSetMultiple', + 'HINCRBY' => 'Predis\Command\HashIncrementBy', + 'HGET' => 'Predis\Command\HashGet', + 'HMGET' => 'Predis\Command\HashGetMultiple', + 'HDEL' => 'Predis\Command\HashDelete', + 'HEXISTS' => 'Predis\Command\HashExists', + 'HLEN' => 'Predis\Command\HashLength', + 'HKEYS' => 'Predis\Command\HashKeys', + 'HVALS' => 'Predis\Command\HashValues', + 'HGETALL' => 'Predis\Command\HashGetAll', + + /* transactions */ + 'MULTI' => 'Predis\Command\TransactionMulti', + 'EXEC' => 'Predis\Command\TransactionExec', + 'DISCARD' => 'Predis\Command\TransactionDiscard', + + /* publish - subscribe */ + 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe', + 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe', + 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern', + 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern', + 'PUBLISH' => 'Predis\Command\PubSubPublish', + + /* remote server control commands */ + 'CONFIG' => 'Predis\Command\ServerConfig', + + /* ---------------- Redis 2.2 ---------------- */ + + /* commands operating on the key space */ + 'PERSIST' => 'Predis\Command\KeyPersist', + + /* commands operating on string values */ + 'STRLEN' => 'Predis\Command\StringStrlen', + 'SETRANGE' => 'Predis\Command\StringSetRange', + 'GETRANGE' => 'Predis\Command\StringGetRange', + 'SETBIT' => 'Predis\Command\StringSetBit', + 'GETBIT' => 'Predis\Command\StringGetBit', + + /* commands operating on lists */ + 'RPUSHX' => 'Predis\Command\ListPushTailX', + 'LPUSHX' => 'Predis\Command\ListPushHeadX', + 'LINSERT' => 'Predis\Command\ListInsert', + 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking', + + /* commands operating on sorted sets */ + 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore', + + /* transactions */ + 'WATCH' => 'Predis\Command\TransactionWatch', + 'UNWATCH' => 'Predis\Command\TransactionUnwatch', + + /* remote server control commands */ + 'OBJECT' => 'Predis\Command\ServerObject', + 'SLOWLOG' => 'Predis\Command\ServerSlowlog', + + /* ---------------- Redis 2.4 ---------------- */ + + /* remote server control commands */ + 'CLIENT' => 'Predis\Command\ServerClient', + + /* ---------------- Redis 2.6 ---------------- */ + + /* commands operating on the key space */ + 'PTTL' => 'Predis\Command\KeyPreciseTimeToLive', + 'PEXPIRE' => 'Predis\Command\KeyPreciseExpire', + 'PEXPIREAT' => 'Predis\Command\KeyPreciseExpireAt', + 'MIGRATE' => 'Predis\Command\KeyMigrate', + + /* commands operating on string values */ + 'PSETEX' => 'Predis\Command\StringPreciseSetExpire', + 'INCRBYFLOAT' => 'Predis\Command\StringIncrementByFloat', + 'BITOP' => 'Predis\Command\StringBitOp', + 'BITCOUNT' => 'Predis\Command\StringBitCount', + + /* commands operating on hashes */ + 'HINCRBYFLOAT' => 'Predis\Command\HashIncrementByFloat', + + /* scripting */ + 'EVAL' => 'Predis\Command\ServerEval', + 'EVALSHA' => 'Predis\Command\ServerEvalSHA', + 'SCRIPT' => 'Predis\Command\ServerScript', + + /* remote server control commands */ + 'TIME' => 'Predis\Command\ServerTime', + 'SENTINEL' => 'Predis\Command\ServerSentinel', + ); + } +} diff --git a/vendor/predis/predis/src/Profile/RedisVersion280.php b/vendor/predis/predis/src/Profile/RedisVersion280.php new file mode 100644 index 000000000..ea17e6829 --- /dev/null +++ b/vendor/predis/predis/src/Profile/RedisVersion280.php @@ -0,0 +1,267 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Profile; + +/** + * Server profile for Redis 2.8. + * + * @author Daniele Alessandri + */ +class RedisVersion280 extends RedisProfile +{ + /** + * {@inheritdoc} + */ + public function getVersion() + { + return '2.8'; + } + + /** + * {@inheritdoc} + */ + public function getSupportedCommands() + { + return array( + /* ---------------- Redis 1.2 ---------------- */ + + /* commands operating on the key space */ + 'EXISTS' => 'Predis\Command\KeyExists', + 'DEL' => 'Predis\Command\KeyDelete', + 'TYPE' => 'Predis\Command\KeyType', + 'KEYS' => 'Predis\Command\KeyKeys', + 'RANDOMKEY' => 'Predis\Command\KeyRandom', + 'RENAME' => 'Predis\Command\KeyRename', + 'RENAMENX' => 'Predis\Command\KeyRenamePreserve', + 'EXPIRE' => 'Predis\Command\KeyExpire', + 'EXPIREAT' => 'Predis\Command\KeyExpireAt', + 'TTL' => 'Predis\Command\KeyTimeToLive', + 'MOVE' => 'Predis\Command\KeyMove', + 'SORT' => 'Predis\Command\KeySort', + 'DUMP' => 'Predis\Command\KeyDump', + 'RESTORE' => 'Predis\Command\KeyRestore', + + /* commands operating on string values */ + 'SET' => 'Predis\Command\StringSet', + 'SETNX' => 'Predis\Command\StringSetPreserve', + 'MSET' => 'Predis\Command\StringSetMultiple', + 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve', + 'GET' => 'Predis\Command\StringGet', + 'MGET' => 'Predis\Command\StringGetMultiple', + 'GETSET' => 'Predis\Command\StringGetSet', + 'INCR' => 'Predis\Command\StringIncrement', + 'INCRBY' => 'Predis\Command\StringIncrementBy', + 'DECR' => 'Predis\Command\StringDecrement', + 'DECRBY' => 'Predis\Command\StringDecrementBy', + + /* commands operating on lists */ + 'RPUSH' => 'Predis\Command\ListPushTail', + 'LPUSH' => 'Predis\Command\ListPushHead', + 'LLEN' => 'Predis\Command\ListLength', + 'LRANGE' => 'Predis\Command\ListRange', + 'LTRIM' => 'Predis\Command\ListTrim', + 'LINDEX' => 'Predis\Command\ListIndex', + 'LSET' => 'Predis\Command\ListSet', + 'LREM' => 'Predis\Command\ListRemove', + 'LPOP' => 'Predis\Command\ListPopFirst', + 'RPOP' => 'Predis\Command\ListPopLast', + 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead', + + /* commands operating on sets */ + 'SADD' => 'Predis\Command\SetAdd', + 'SREM' => 'Predis\Command\SetRemove', + 'SPOP' => 'Predis\Command\SetPop', + 'SMOVE' => 'Predis\Command\SetMove', + 'SCARD' => 'Predis\Command\SetCardinality', + 'SISMEMBER' => 'Predis\Command\SetIsMember', + 'SINTER' => 'Predis\Command\SetIntersection', + 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore', + 'SUNION' => 'Predis\Command\SetUnion', + 'SUNIONSTORE' => 'Predis\Command\SetUnionStore', + 'SDIFF' => 'Predis\Command\SetDifference', + 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore', + 'SMEMBERS' => 'Predis\Command\SetMembers', + 'SRANDMEMBER' => 'Predis\Command\SetRandomMember', + + /* commands operating on sorted sets */ + 'ZADD' => 'Predis\Command\ZSetAdd', + 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy', + 'ZREM' => 'Predis\Command\ZSetRemove', + 'ZRANGE' => 'Predis\Command\ZSetRange', + 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange', + 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore', + 'ZCARD' => 'Predis\Command\ZSetCardinality', + 'ZSCORE' => 'Predis\Command\ZSetScore', + 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore', + + /* connection related commands */ + 'PING' => 'Predis\Command\ConnectionPing', + 'AUTH' => 'Predis\Command\ConnectionAuth', + 'SELECT' => 'Predis\Command\ConnectionSelect', + 'ECHO' => 'Predis\Command\ConnectionEcho', + 'QUIT' => 'Predis\Command\ConnectionQuit', + + /* remote server control commands */ + 'INFO' => 'Predis\Command\ServerInfoV26x', + 'SLAVEOF' => 'Predis\Command\ServerSlaveOf', + 'MONITOR' => 'Predis\Command\ServerMonitor', + 'DBSIZE' => 'Predis\Command\ServerDatabaseSize', + 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase', + 'FLUSHALL' => 'Predis\Command\ServerFlushAll', + 'SAVE' => 'Predis\Command\ServerSave', + 'BGSAVE' => 'Predis\Command\ServerBackgroundSave', + 'LASTSAVE' => 'Predis\Command\ServerLastSave', + 'SHUTDOWN' => 'Predis\Command\ServerShutdown', + 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF', + + /* ---------------- Redis 2.0 ---------------- */ + + /* commands operating on string values */ + 'SETEX' => 'Predis\Command\StringSetExpire', + 'APPEND' => 'Predis\Command\StringAppend', + 'SUBSTR' => 'Predis\Command\StringSubstr', + + /* commands operating on lists */ + 'BLPOP' => 'Predis\Command\ListPopFirstBlocking', + 'BRPOP' => 'Predis\Command\ListPopLastBlocking', + + /* commands operating on sorted sets */ + 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore', + 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore', + 'ZCOUNT' => 'Predis\Command\ZSetCount', + 'ZRANK' => 'Predis\Command\ZSetRank', + 'ZREVRANK' => 'Predis\Command\ZSetReverseRank', + 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank', + + /* commands operating on hashes */ + 'HSET' => 'Predis\Command\HashSet', + 'HSETNX' => 'Predis\Command\HashSetPreserve', + 'HMSET' => 'Predis\Command\HashSetMultiple', + 'HINCRBY' => 'Predis\Command\HashIncrementBy', + 'HGET' => 'Predis\Command\HashGet', + 'HMGET' => 'Predis\Command\HashGetMultiple', + 'HDEL' => 'Predis\Command\HashDelete', + 'HEXISTS' => 'Predis\Command\HashExists', + 'HLEN' => 'Predis\Command\HashLength', + 'HKEYS' => 'Predis\Command\HashKeys', + 'HVALS' => 'Predis\Command\HashValues', + 'HGETALL' => 'Predis\Command\HashGetAll', + + /* transactions */ + 'MULTI' => 'Predis\Command\TransactionMulti', + 'EXEC' => 'Predis\Command\TransactionExec', + 'DISCARD' => 'Predis\Command\TransactionDiscard', + + /* publish - subscribe */ + 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe', + 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe', + 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern', + 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern', + 'PUBLISH' => 'Predis\Command\PubSubPublish', + + /* remote server control commands */ + 'CONFIG' => 'Predis\Command\ServerConfig', + + /* ---------------- Redis 2.2 ---------------- */ + + /* commands operating on the key space */ + 'PERSIST' => 'Predis\Command\KeyPersist', + + /* commands operating on string values */ + 'STRLEN' => 'Predis\Command\StringStrlen', + 'SETRANGE' => 'Predis\Command\StringSetRange', + 'GETRANGE' => 'Predis\Command\StringGetRange', + 'SETBIT' => 'Predis\Command\StringSetBit', + 'GETBIT' => 'Predis\Command\StringGetBit', + + /* commands operating on lists */ + 'RPUSHX' => 'Predis\Command\ListPushTailX', + 'LPUSHX' => 'Predis\Command\ListPushHeadX', + 'LINSERT' => 'Predis\Command\ListInsert', + 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking', + + /* commands operating on sorted sets */ + 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore', + + /* transactions */ + 'WATCH' => 'Predis\Command\TransactionWatch', + 'UNWATCH' => 'Predis\Command\TransactionUnwatch', + + /* remote server control commands */ + 'OBJECT' => 'Predis\Command\ServerObject', + 'SLOWLOG' => 'Predis\Command\ServerSlowlog', + + /* ---------------- Redis 2.4 ---------------- */ + + /* remote server control commands */ + 'CLIENT' => 'Predis\Command\ServerClient', + + /* ---------------- Redis 2.6 ---------------- */ + + /* commands operating on the key space */ + 'PTTL' => 'Predis\Command\KeyPreciseTimeToLive', + 'PEXPIRE' => 'Predis\Command\KeyPreciseExpire', + 'PEXPIREAT' => 'Predis\Command\KeyPreciseExpireAt', + 'MIGRATE' => 'Predis\Command\KeyMigrate', + + /* commands operating on string values */ + 'PSETEX' => 'Predis\Command\StringPreciseSetExpire', + 'INCRBYFLOAT' => 'Predis\Command\StringIncrementByFloat', + 'BITOP' => 'Predis\Command\StringBitOp', + 'BITCOUNT' => 'Predis\Command\StringBitCount', + + /* commands operating on hashes */ + 'HINCRBYFLOAT' => 'Predis\Command\HashIncrementByFloat', + + /* scripting */ + 'EVAL' => 'Predis\Command\ServerEval', + 'EVALSHA' => 'Predis\Command\ServerEvalSHA', + 'SCRIPT' => 'Predis\Command\ServerScript', + + /* remote server control commands */ + 'TIME' => 'Predis\Command\ServerTime', + 'SENTINEL' => 'Predis\Command\ServerSentinel', + + /* ---------------- Redis 2.8 ---------------- */ + + /* commands operating on the key space */ + 'SCAN' => 'Predis\Command\KeyScan', + + /* commands operating on string values */ + 'BITPOS' => 'Predis\Command\StringBitPos', + + /* commands operating on sets */ + 'SSCAN' => 'Predis\Command\SetScan', + + /* commands operating on sorted sets */ + 'ZSCAN' => 'Predis\Command\ZSetScan', + 'ZLEXCOUNT' => 'Predis\Command\ZSetLexCount', + 'ZRANGEBYLEX' => 'Predis\Command\ZSetRangeByLex', + 'ZREMRANGEBYLEX' => 'Predis\Command\ZSetRemoveRangeByLex', + 'ZREVRANGEBYLEX' => 'Predis\Command\ZSetReverseRangeByLex', + + /* commands operating on hashes */ + 'HSCAN' => 'Predis\Command\HashScan', + + /* publish - subscribe */ + 'PUBSUB' => 'Predis\Command\PubSubPubsub', + + /* commands operating on HyperLogLog */ + 'PFADD' => 'Predis\Command\HyperLogLogAdd', + 'PFCOUNT' => 'Predis\Command\HyperLogLogCount', + 'PFMERGE' => 'Predis\Command\HyperLogLogMerge', + + /* remote server control commands */ + 'COMMAND' => 'Predis\Command\ServerCommand', + ); + } +} diff --git a/vendor/predis/predis/src/Profile/RedisVersion300.php b/vendor/predis/predis/src/Profile/RedisVersion300.php new file mode 100644 index 000000000..8a2fac8b7 --- /dev/null +++ b/vendor/predis/predis/src/Profile/RedisVersion300.php @@ -0,0 +1,270 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Profile; + +/** + * Server profile for Redis 3.0. + * + * @author Daniele Alessandri + */ +class RedisVersion300 extends RedisProfile +{ + /** + * {@inheritdoc} + */ + public function getVersion() + { + return '3.0'; + } + + /** + * {@inheritdoc} + */ + public function getSupportedCommands() + { + return array( + /* ---------------- Redis 1.2 ---------------- */ + + /* commands operating on the key space */ + 'EXISTS' => 'Predis\Command\KeyExists', + 'DEL' => 'Predis\Command\KeyDelete', + 'TYPE' => 'Predis\Command\KeyType', + 'KEYS' => 'Predis\Command\KeyKeys', + 'RANDOMKEY' => 'Predis\Command\KeyRandom', + 'RENAME' => 'Predis\Command\KeyRename', + 'RENAMENX' => 'Predis\Command\KeyRenamePreserve', + 'EXPIRE' => 'Predis\Command\KeyExpire', + 'EXPIREAT' => 'Predis\Command\KeyExpireAt', + 'TTL' => 'Predis\Command\KeyTimeToLive', + 'MOVE' => 'Predis\Command\KeyMove', + 'SORT' => 'Predis\Command\KeySort', + 'DUMP' => 'Predis\Command\KeyDump', + 'RESTORE' => 'Predis\Command\KeyRestore', + + /* commands operating on string values */ + 'SET' => 'Predis\Command\StringSet', + 'SETNX' => 'Predis\Command\StringSetPreserve', + 'MSET' => 'Predis\Command\StringSetMultiple', + 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve', + 'GET' => 'Predis\Command\StringGet', + 'MGET' => 'Predis\Command\StringGetMultiple', + 'GETSET' => 'Predis\Command\StringGetSet', + 'INCR' => 'Predis\Command\StringIncrement', + 'INCRBY' => 'Predis\Command\StringIncrementBy', + 'DECR' => 'Predis\Command\StringDecrement', + 'DECRBY' => 'Predis\Command\StringDecrementBy', + + /* commands operating on lists */ + 'RPUSH' => 'Predis\Command\ListPushTail', + 'LPUSH' => 'Predis\Command\ListPushHead', + 'LLEN' => 'Predis\Command\ListLength', + 'LRANGE' => 'Predis\Command\ListRange', + 'LTRIM' => 'Predis\Command\ListTrim', + 'LINDEX' => 'Predis\Command\ListIndex', + 'LSET' => 'Predis\Command\ListSet', + 'LREM' => 'Predis\Command\ListRemove', + 'LPOP' => 'Predis\Command\ListPopFirst', + 'RPOP' => 'Predis\Command\ListPopLast', + 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead', + + /* commands operating on sets */ + 'SADD' => 'Predis\Command\SetAdd', + 'SREM' => 'Predis\Command\SetRemove', + 'SPOP' => 'Predis\Command\SetPop', + 'SMOVE' => 'Predis\Command\SetMove', + 'SCARD' => 'Predis\Command\SetCardinality', + 'SISMEMBER' => 'Predis\Command\SetIsMember', + 'SINTER' => 'Predis\Command\SetIntersection', + 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore', + 'SUNION' => 'Predis\Command\SetUnion', + 'SUNIONSTORE' => 'Predis\Command\SetUnionStore', + 'SDIFF' => 'Predis\Command\SetDifference', + 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore', + 'SMEMBERS' => 'Predis\Command\SetMembers', + 'SRANDMEMBER' => 'Predis\Command\SetRandomMember', + + /* commands operating on sorted sets */ + 'ZADD' => 'Predis\Command\ZSetAdd', + 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy', + 'ZREM' => 'Predis\Command\ZSetRemove', + 'ZRANGE' => 'Predis\Command\ZSetRange', + 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange', + 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore', + 'ZCARD' => 'Predis\Command\ZSetCardinality', + 'ZSCORE' => 'Predis\Command\ZSetScore', + 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore', + + /* connection related commands */ + 'PING' => 'Predis\Command\ConnectionPing', + 'AUTH' => 'Predis\Command\ConnectionAuth', + 'SELECT' => 'Predis\Command\ConnectionSelect', + 'ECHO' => 'Predis\Command\ConnectionEcho', + 'QUIT' => 'Predis\Command\ConnectionQuit', + + /* remote server control commands */ + 'INFO' => 'Predis\Command\ServerInfoV26x', + 'SLAVEOF' => 'Predis\Command\ServerSlaveOf', + 'MONITOR' => 'Predis\Command\ServerMonitor', + 'DBSIZE' => 'Predis\Command\ServerDatabaseSize', + 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase', + 'FLUSHALL' => 'Predis\Command\ServerFlushAll', + 'SAVE' => 'Predis\Command\ServerSave', + 'BGSAVE' => 'Predis\Command\ServerBackgroundSave', + 'LASTSAVE' => 'Predis\Command\ServerLastSave', + 'SHUTDOWN' => 'Predis\Command\ServerShutdown', + 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF', + + /* ---------------- Redis 2.0 ---------------- */ + + /* commands operating on string values */ + 'SETEX' => 'Predis\Command\StringSetExpire', + 'APPEND' => 'Predis\Command\StringAppend', + 'SUBSTR' => 'Predis\Command\StringSubstr', + + /* commands operating on lists */ + 'BLPOP' => 'Predis\Command\ListPopFirstBlocking', + 'BRPOP' => 'Predis\Command\ListPopLastBlocking', + + /* commands operating on sorted sets */ + 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore', + 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore', + 'ZCOUNT' => 'Predis\Command\ZSetCount', + 'ZRANK' => 'Predis\Command\ZSetRank', + 'ZREVRANK' => 'Predis\Command\ZSetReverseRank', + 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank', + + /* commands operating on hashes */ + 'HSET' => 'Predis\Command\HashSet', + 'HSETNX' => 'Predis\Command\HashSetPreserve', + 'HMSET' => 'Predis\Command\HashSetMultiple', + 'HINCRBY' => 'Predis\Command\HashIncrementBy', + 'HGET' => 'Predis\Command\HashGet', + 'HMGET' => 'Predis\Command\HashGetMultiple', + 'HDEL' => 'Predis\Command\HashDelete', + 'HEXISTS' => 'Predis\Command\HashExists', + 'HLEN' => 'Predis\Command\HashLength', + 'HKEYS' => 'Predis\Command\HashKeys', + 'HVALS' => 'Predis\Command\HashValues', + 'HGETALL' => 'Predis\Command\HashGetAll', + + /* transactions */ + 'MULTI' => 'Predis\Command\TransactionMulti', + 'EXEC' => 'Predis\Command\TransactionExec', + 'DISCARD' => 'Predis\Command\TransactionDiscard', + + /* publish - subscribe */ + 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe', + 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe', + 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern', + 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern', + 'PUBLISH' => 'Predis\Command\PubSubPublish', + + /* remote server control commands */ + 'CONFIG' => 'Predis\Command\ServerConfig', + + /* ---------------- Redis 2.2 ---------------- */ + + /* commands operating on the key space */ + 'PERSIST' => 'Predis\Command\KeyPersist', + + /* commands operating on string values */ + 'STRLEN' => 'Predis\Command\StringStrlen', + 'SETRANGE' => 'Predis\Command\StringSetRange', + 'GETRANGE' => 'Predis\Command\StringGetRange', + 'SETBIT' => 'Predis\Command\StringSetBit', + 'GETBIT' => 'Predis\Command\StringGetBit', + + /* commands operating on lists */ + 'RPUSHX' => 'Predis\Command\ListPushTailX', + 'LPUSHX' => 'Predis\Command\ListPushHeadX', + 'LINSERT' => 'Predis\Command\ListInsert', + 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking', + + /* commands operating on sorted sets */ + 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore', + + /* transactions */ + 'WATCH' => 'Predis\Command\TransactionWatch', + 'UNWATCH' => 'Predis\Command\TransactionUnwatch', + + /* remote server control commands */ + 'OBJECT' => 'Predis\Command\ServerObject', + 'SLOWLOG' => 'Predis\Command\ServerSlowlog', + + /* ---------------- Redis 2.4 ---------------- */ + + /* remote server control commands */ + 'CLIENT' => 'Predis\Command\ServerClient', + + /* ---------------- Redis 2.6 ---------------- */ + + /* commands operating on the key space */ + 'PTTL' => 'Predis\Command\KeyPreciseTimeToLive', + 'PEXPIRE' => 'Predis\Command\KeyPreciseExpire', + 'PEXPIREAT' => 'Predis\Command\KeyPreciseExpireAt', + 'MIGRATE' => 'Predis\Command\KeyMigrate', + + /* commands operating on string values */ + 'PSETEX' => 'Predis\Command\StringPreciseSetExpire', + 'INCRBYFLOAT' => 'Predis\Command\StringIncrementByFloat', + 'BITOP' => 'Predis\Command\StringBitOp', + 'BITCOUNT' => 'Predis\Command\StringBitCount', + + /* commands operating on hashes */ + 'HINCRBYFLOAT' => 'Predis\Command\HashIncrementByFloat', + + /* scripting */ + 'EVAL' => 'Predis\Command\ServerEval', + 'EVALSHA' => 'Predis\Command\ServerEvalSHA', + 'SCRIPT' => 'Predis\Command\ServerScript', + + /* remote server control commands */ + 'TIME' => 'Predis\Command\ServerTime', + 'SENTINEL' => 'Predis\Command\ServerSentinel', + + /* ---------------- Redis 2.8 ---------------- */ + + /* commands operating on the key space */ + 'SCAN' => 'Predis\Command\KeyScan', + + /* commands operating on string values */ + 'BITPOS' => 'Predis\Command\StringBitPos', + + /* commands operating on sets */ + 'SSCAN' => 'Predis\Command\SetScan', + + /* commands operating on sorted sets */ + 'ZSCAN' => 'Predis\Command\ZSetScan', + 'ZLEXCOUNT' => 'Predis\Command\ZSetLexCount', + 'ZRANGEBYLEX' => 'Predis\Command\ZSetRangeByLex', + 'ZREMRANGEBYLEX' => 'Predis\Command\ZSetRemoveRangeByLex', + 'ZREVRANGEBYLEX' => 'Predis\Command\ZSetReverseRangeByLex', + + /* commands operating on hashes */ + 'HSCAN' => 'Predis\Command\HashScan', + + /* publish - subscribe */ + 'PUBSUB' => 'Predis\Command\PubSubPubsub', + + /* commands operating on HyperLogLog */ + 'PFADD' => 'Predis\Command\HyperLogLogAdd', + 'PFCOUNT' => 'Predis\Command\HyperLogLogCount', + 'PFMERGE' => 'Predis\Command\HyperLogLogMerge', + + /* remote server control commands */ + 'COMMAND' => 'Predis\Command\ServerCommand', + + /* ---------------- Redis 3.0 ---------------- */ + + ); + } +} diff --git a/vendor/predis/predis/src/Profile/RedisVersion320.php b/vendor/predis/predis/src/Profile/RedisVersion320.php new file mode 100644 index 000000000..7de79573d --- /dev/null +++ b/vendor/predis/predis/src/Profile/RedisVersion320.php @@ -0,0 +1,281 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Profile; + +/** + * Server profile for Redis 3.0. + * + * @author Daniele Alessandri + */ +class RedisVersion320 extends RedisProfile +{ + /** + * {@inheritdoc} + */ + public function getVersion() + { + return '3.2'; + } + + /** + * {@inheritdoc} + */ + public function getSupportedCommands() + { + return array( + /* ---------------- Redis 1.2 ---------------- */ + + /* commands operating on the key space */ + 'EXISTS' => 'Predis\Command\KeyExists', + 'DEL' => 'Predis\Command\KeyDelete', + 'TYPE' => 'Predis\Command\KeyType', + 'KEYS' => 'Predis\Command\KeyKeys', + 'RANDOMKEY' => 'Predis\Command\KeyRandom', + 'RENAME' => 'Predis\Command\KeyRename', + 'RENAMENX' => 'Predis\Command\KeyRenamePreserve', + 'EXPIRE' => 'Predis\Command\KeyExpire', + 'EXPIREAT' => 'Predis\Command\KeyExpireAt', + 'TTL' => 'Predis\Command\KeyTimeToLive', + 'MOVE' => 'Predis\Command\KeyMove', + 'SORT' => 'Predis\Command\KeySort', + 'DUMP' => 'Predis\Command\KeyDump', + 'RESTORE' => 'Predis\Command\KeyRestore', + + /* commands operating on string values */ + 'SET' => 'Predis\Command\StringSet', + 'SETNX' => 'Predis\Command\StringSetPreserve', + 'MSET' => 'Predis\Command\StringSetMultiple', + 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve', + 'GET' => 'Predis\Command\StringGet', + 'MGET' => 'Predis\Command\StringGetMultiple', + 'GETSET' => 'Predis\Command\StringGetSet', + 'INCR' => 'Predis\Command\StringIncrement', + 'INCRBY' => 'Predis\Command\StringIncrementBy', + 'DECR' => 'Predis\Command\StringDecrement', + 'DECRBY' => 'Predis\Command\StringDecrementBy', + + /* commands operating on lists */ + 'RPUSH' => 'Predis\Command\ListPushTail', + 'LPUSH' => 'Predis\Command\ListPushHead', + 'LLEN' => 'Predis\Command\ListLength', + 'LRANGE' => 'Predis\Command\ListRange', + 'LTRIM' => 'Predis\Command\ListTrim', + 'LINDEX' => 'Predis\Command\ListIndex', + 'LSET' => 'Predis\Command\ListSet', + 'LREM' => 'Predis\Command\ListRemove', + 'LPOP' => 'Predis\Command\ListPopFirst', + 'RPOP' => 'Predis\Command\ListPopLast', + 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead', + + /* commands operating on sets */ + 'SADD' => 'Predis\Command\SetAdd', + 'SREM' => 'Predis\Command\SetRemove', + 'SPOP' => 'Predis\Command\SetPop', + 'SMOVE' => 'Predis\Command\SetMove', + 'SCARD' => 'Predis\Command\SetCardinality', + 'SISMEMBER' => 'Predis\Command\SetIsMember', + 'SINTER' => 'Predis\Command\SetIntersection', + 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore', + 'SUNION' => 'Predis\Command\SetUnion', + 'SUNIONSTORE' => 'Predis\Command\SetUnionStore', + 'SDIFF' => 'Predis\Command\SetDifference', + 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore', + 'SMEMBERS' => 'Predis\Command\SetMembers', + 'SRANDMEMBER' => 'Predis\Command\SetRandomMember', + + /* commands operating on sorted sets */ + 'ZADD' => 'Predis\Command\ZSetAdd', + 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy', + 'ZREM' => 'Predis\Command\ZSetRemove', + 'ZRANGE' => 'Predis\Command\ZSetRange', + 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange', + 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore', + 'ZCARD' => 'Predis\Command\ZSetCardinality', + 'ZSCORE' => 'Predis\Command\ZSetScore', + 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore', + + /* connection related commands */ + 'PING' => 'Predis\Command\ConnectionPing', + 'AUTH' => 'Predis\Command\ConnectionAuth', + 'SELECT' => 'Predis\Command\ConnectionSelect', + 'ECHO' => 'Predis\Command\ConnectionEcho', + 'QUIT' => 'Predis\Command\ConnectionQuit', + + /* remote server control commands */ + 'INFO' => 'Predis\Command\ServerInfoV26x', + 'SLAVEOF' => 'Predis\Command\ServerSlaveOf', + 'MONITOR' => 'Predis\Command\ServerMonitor', + 'DBSIZE' => 'Predis\Command\ServerDatabaseSize', + 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase', + 'FLUSHALL' => 'Predis\Command\ServerFlushAll', + 'SAVE' => 'Predis\Command\ServerSave', + 'BGSAVE' => 'Predis\Command\ServerBackgroundSave', + 'LASTSAVE' => 'Predis\Command\ServerLastSave', + 'SHUTDOWN' => 'Predis\Command\ServerShutdown', + 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF', + + /* ---------------- Redis 2.0 ---------------- */ + + /* commands operating on string values */ + 'SETEX' => 'Predis\Command\StringSetExpire', + 'APPEND' => 'Predis\Command\StringAppend', + 'SUBSTR' => 'Predis\Command\StringSubstr', + + /* commands operating on lists */ + 'BLPOP' => 'Predis\Command\ListPopFirstBlocking', + 'BRPOP' => 'Predis\Command\ListPopLastBlocking', + + /* commands operating on sorted sets */ + 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore', + 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore', + 'ZCOUNT' => 'Predis\Command\ZSetCount', + 'ZRANK' => 'Predis\Command\ZSetRank', + 'ZREVRANK' => 'Predis\Command\ZSetReverseRank', + 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank', + + /* commands operating on hashes */ + 'HSET' => 'Predis\Command\HashSet', + 'HSETNX' => 'Predis\Command\HashSetPreserve', + 'HMSET' => 'Predis\Command\HashSetMultiple', + 'HINCRBY' => 'Predis\Command\HashIncrementBy', + 'HGET' => 'Predis\Command\HashGet', + 'HMGET' => 'Predis\Command\HashGetMultiple', + 'HDEL' => 'Predis\Command\HashDelete', + 'HEXISTS' => 'Predis\Command\HashExists', + 'HLEN' => 'Predis\Command\HashLength', + 'HKEYS' => 'Predis\Command\HashKeys', + 'HVALS' => 'Predis\Command\HashValues', + 'HGETALL' => 'Predis\Command\HashGetAll', + + /* transactions */ + 'MULTI' => 'Predis\Command\TransactionMulti', + 'EXEC' => 'Predis\Command\TransactionExec', + 'DISCARD' => 'Predis\Command\TransactionDiscard', + + /* publish - subscribe */ + 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe', + 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe', + 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern', + 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern', + 'PUBLISH' => 'Predis\Command\PubSubPublish', + + /* remote server control commands */ + 'CONFIG' => 'Predis\Command\ServerConfig', + + /* ---------------- Redis 2.2 ---------------- */ + + /* commands operating on the key space */ + 'PERSIST' => 'Predis\Command\KeyPersist', + + /* commands operating on string values */ + 'STRLEN' => 'Predis\Command\StringStrlen', + 'SETRANGE' => 'Predis\Command\StringSetRange', + 'GETRANGE' => 'Predis\Command\StringGetRange', + 'SETBIT' => 'Predis\Command\StringSetBit', + 'GETBIT' => 'Predis\Command\StringGetBit', + + /* commands operating on lists */ + 'RPUSHX' => 'Predis\Command\ListPushTailX', + 'LPUSHX' => 'Predis\Command\ListPushHeadX', + 'LINSERT' => 'Predis\Command\ListInsert', + 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking', + + /* commands operating on sorted sets */ + 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore', + + /* transactions */ + 'WATCH' => 'Predis\Command\TransactionWatch', + 'UNWATCH' => 'Predis\Command\TransactionUnwatch', + + /* remote server control commands */ + 'OBJECT' => 'Predis\Command\ServerObject', + 'SLOWLOG' => 'Predis\Command\ServerSlowlog', + + /* ---------------- Redis 2.4 ---------------- */ + + /* remote server control commands */ + 'CLIENT' => 'Predis\Command\ServerClient', + + /* ---------------- Redis 2.6 ---------------- */ + + /* commands operating on the key space */ + 'PTTL' => 'Predis\Command\KeyPreciseTimeToLive', + 'PEXPIRE' => 'Predis\Command\KeyPreciseExpire', + 'PEXPIREAT' => 'Predis\Command\KeyPreciseExpireAt', + 'MIGRATE' => 'Predis\Command\KeyMigrate', + + /* commands operating on string values */ + 'PSETEX' => 'Predis\Command\StringPreciseSetExpire', + 'INCRBYFLOAT' => 'Predis\Command\StringIncrementByFloat', + 'BITOP' => 'Predis\Command\StringBitOp', + 'BITCOUNT' => 'Predis\Command\StringBitCount', + + /* commands operating on hashes */ + 'HINCRBYFLOAT' => 'Predis\Command\HashIncrementByFloat', + + /* scripting */ + 'EVAL' => 'Predis\Command\ServerEval', + 'EVALSHA' => 'Predis\Command\ServerEvalSHA', + 'SCRIPT' => 'Predis\Command\ServerScript', + + /* remote server control commands */ + 'TIME' => 'Predis\Command\ServerTime', + 'SENTINEL' => 'Predis\Command\ServerSentinel', + + /* ---------------- Redis 2.8 ---------------- */ + + /* commands operating on the key space */ + 'SCAN' => 'Predis\Command\KeyScan', + + /* commands operating on string values */ + 'BITPOS' => 'Predis\Command\StringBitPos', + + /* commands operating on sets */ + 'SSCAN' => 'Predis\Command\SetScan', + + /* commands operating on sorted sets */ + 'ZSCAN' => 'Predis\Command\ZSetScan', + 'ZLEXCOUNT' => 'Predis\Command\ZSetLexCount', + 'ZRANGEBYLEX' => 'Predis\Command\ZSetRangeByLex', + 'ZREMRANGEBYLEX' => 'Predis\Command\ZSetRemoveRangeByLex', + 'ZREVRANGEBYLEX' => 'Predis\Command\ZSetReverseRangeByLex', + + /* commands operating on hashes */ + 'HSCAN' => 'Predis\Command\HashScan', + + /* publish - subscribe */ + 'PUBSUB' => 'Predis\Command\PubSubPubsub', + + /* commands operating on HyperLogLog */ + 'PFADD' => 'Predis\Command\HyperLogLogAdd', + 'PFCOUNT' => 'Predis\Command\HyperLogLogCount', + 'PFMERGE' => 'Predis\Command\HyperLogLogMerge', + + /* remote server control commands */ + 'COMMAND' => 'Predis\Command\ServerCommand', + + /* ---------------- Redis 3.2 ---------------- */ + + /* commands operating on hashes */ + 'HSTRLEN' => 'Predis\Command\HashStringLength', + 'BITFIELD' => 'Predis\Command\StringBitField', + + /* commands performing geospatial operations */ + 'GEOADD' => 'Predis\Command\GeospatialGeoAdd', + 'GEOHASH' => 'Predis\Command\GeospatialGeoHash', + 'GEOPOS' => 'Predis\Command\GeospatialGeoPos', + 'GEODIST' => 'Predis\Command\GeospatialGeoDist', + 'GEORADIUS' => 'Predis\Command\GeospatialGeoRadius', + 'GEORADIUSBYMEMBER' => 'Predis\Command\GeospatialGeoRadiusByMember', + ); + } +} diff --git a/vendor/predis/predis/src/Protocol/ProtocolException.php b/vendor/predis/predis/src/Protocol/ProtocolException.php new file mode 100644 index 000000000..6fe5d6d3a --- /dev/null +++ b/vendor/predis/predis/src/Protocol/ProtocolException.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol; + +use Predis\CommunicationException; + +/** + * Exception used to indentify errors encountered while parsing the Redis wire + * protocol. + * + * @author Daniele Alessandri + */ +class ProtocolException extends CommunicationException +{ +} diff --git a/vendor/predis/predis/src/Protocol/ProtocolProcessorInterface.php b/vendor/predis/predis/src/Protocol/ProtocolProcessorInterface.php new file mode 100644 index 000000000..b34ea1814 --- /dev/null +++ b/vendor/predis/predis/src/Protocol/ProtocolProcessorInterface.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol; + +use Predis\Command\CommandInterface; +use Predis\Connection\CompositeConnectionInterface; + +/** + * Defines a pluggable protocol processor capable of serializing commands and + * deserializing responses into PHP objects directly from a connection. + * + * @author Daniele Alessandri + */ +interface ProtocolProcessorInterface +{ + /** + * Writes a request over a connection to Redis. + * + * @param CompositeConnectionInterface $connection Redis connection. + * @param CommandInterface $command Command instance. + */ + public function write(CompositeConnectionInterface $connection, CommandInterface $command); + + /** + * Reads a response from a connection to Redis. + * + * @param CompositeConnectionInterface $connection Redis connection. + * + * @return mixed + */ + public function read(CompositeConnectionInterface $connection); +} diff --git a/vendor/predis/predis/src/Protocol/RequestSerializerInterface.php b/vendor/predis/predis/src/Protocol/RequestSerializerInterface.php new file mode 100644 index 000000000..eef72a640 --- /dev/null +++ b/vendor/predis/predis/src/Protocol/RequestSerializerInterface.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol; + +use Predis\Command\CommandInterface; + +/** + * Defines a pluggable serializer for Redis commands. + * + * @author Daniele Alessandri + */ +interface RequestSerializerInterface +{ + /** + * Serializes a Redis command. + * + * @param CommandInterface $command Redis command. + * + * @return string + */ + public function serialize(CommandInterface $command); +} diff --git a/vendor/predis/predis/src/Protocol/ResponseReaderInterface.php b/vendor/predis/predis/src/Protocol/ResponseReaderInterface.php new file mode 100644 index 000000000..86a7bdcce --- /dev/null +++ b/vendor/predis/predis/src/Protocol/ResponseReaderInterface.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol; + +use Predis\Connection\CompositeConnectionInterface; + +/** + * Defines a pluggable reader capable of parsing responses returned by Redis and + * deserializing them to PHP objects. + * + * @author Daniele Alessandri + */ +interface ResponseReaderInterface +{ + /** + * Reads a response from a connection to Redis. + * + * @param CompositeConnectionInterface $connection Redis connection. + * + * @return mixed + */ + public function read(CompositeConnectionInterface $connection); +} diff --git a/vendor/predis/predis/src/Protocol/Text/CompositeProtocolProcessor.php b/vendor/predis/predis/src/Protocol/Text/CompositeProtocolProcessor.php new file mode 100644 index 000000000..ea85ed303 --- /dev/null +++ b/vendor/predis/predis/src/Protocol/Text/CompositeProtocolProcessor.php @@ -0,0 +1,107 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol\Text; + +use Predis\Command\CommandInterface; +use Predis\Connection\CompositeConnectionInterface; +use Predis\Protocol\ProtocolProcessorInterface; +use Predis\Protocol\RequestSerializerInterface; +use Predis\Protocol\ResponseReaderInterface; + +/** + * Composite protocol processor for the standard Redis wire protocol using + * pluggable handlers to serialize requests and deserialize responses. + * + * @link http://redis.io/topics/protocol + * + * @author Daniele Alessandri + */ +class CompositeProtocolProcessor implements ProtocolProcessorInterface +{ + /* + * @var RequestSerializerInterface + */ + protected $serializer; + + /* + * @var ResponseReaderInterface + */ + protected $reader; + + /** + * @param RequestSerializerInterface $serializer Request serializer. + * @param ResponseReaderInterface $reader Response reader. + */ + public function __construct( + RequestSerializerInterface $serializer = null, + ResponseReaderInterface $reader = null + ) { + $this->setRequestSerializer($serializer ?: new RequestSerializer()); + $this->setResponseReader($reader ?: new ResponseReader()); + } + + /** + * {@inheritdoc} + */ + public function write(CompositeConnectionInterface $connection, CommandInterface $command) + { + $connection->writeBuffer($this->serializer->serialize($command)); + } + + /** + * {@inheritdoc} + */ + public function read(CompositeConnectionInterface $connection) + { + return $this->reader->read($connection); + } + + /** + * Sets the request serializer used by the protocol processor. + * + * @param RequestSerializerInterface $serializer Request serializer. + */ + public function setRequestSerializer(RequestSerializerInterface $serializer) + { + $this->serializer = $serializer; + } + + /** + * Returns the request serializer used by the protocol processor. + * + * @return RequestSerializerInterface + */ + public function getRequestSerializer() + { + return $this->serializer; + } + + /** + * Sets the response reader used by the protocol processor. + * + * @param ResponseReaderInterface $reader Response reader. + */ + public function setResponseReader(ResponseReaderInterface $reader) + { + $this->reader = $reader; + } + + /** + * Returns the Response reader used by the protocol processor. + * + * @return ResponseReaderInterface + */ + public function getResponseReader() + { + return $this->reader; + } +} diff --git a/vendor/predis/predis/src/Protocol/Text/Handler/BulkResponse.php b/vendor/predis/predis/src/Protocol/Text/Handler/BulkResponse.php new file mode 100644 index 000000000..5b0bf3c2d --- /dev/null +++ b/vendor/predis/predis/src/Protocol/Text/Handler/BulkResponse.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol\Text\Handler; + +use Predis\CommunicationException; +use Predis\Connection\CompositeConnectionInterface; +use Predis\Protocol\ProtocolException; + +/** + * Handler for the bulk response type in the standard Redis wire protocol. + * It translates the payload to a string or a NULL. + * + * @link http://redis.io/topics/protocol + * + * @author Daniele Alessandri + */ +class BulkResponse implements ResponseHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handle(CompositeConnectionInterface $connection, $payload) + { + $length = (int) $payload; + + if ("$length" !== $payload) { + CommunicationException::handle(new ProtocolException( + $connection, "Cannot parse '$payload' as a valid length for a bulk response." + )); + } + + if ($length >= 0) { + return substr($connection->readBuffer($length + 2), 0, -2); + } + + if ($length == -1) { + return; + } + + CommunicationException::handle(new ProtocolException( + $connection, "Value '$payload' is not a valid length for a bulk response." + )); + + return; + } +} diff --git a/vendor/predis/predis/src/Protocol/Text/Handler/ErrorResponse.php b/vendor/predis/predis/src/Protocol/Text/Handler/ErrorResponse.php new file mode 100644 index 000000000..3e18b7b9e --- /dev/null +++ b/vendor/predis/predis/src/Protocol/Text/Handler/ErrorResponse.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol\Text\Handler; + +use Predis\Connection\CompositeConnectionInterface; +use Predis\Response\Error; + +/** + * Handler for the error response type in the standard Redis wire protocol. + * It translates the payload to a complex response object for Predis. + * + * @link http://redis.io/topics/protocol + * + * @author Daniele Alessandri + */ +class ErrorResponse implements ResponseHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handle(CompositeConnectionInterface $connection, $payload) + { + return new Error($payload); + } +} diff --git a/vendor/predis/predis/src/Protocol/Text/Handler/IntegerResponse.php b/vendor/predis/predis/src/Protocol/Text/Handler/IntegerResponse.php new file mode 100644 index 000000000..f96560198 --- /dev/null +++ b/vendor/predis/predis/src/Protocol/Text/Handler/IntegerResponse.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol\Text\Handler; + +use Predis\CommunicationException; +use Predis\Connection\CompositeConnectionInterface; +use Predis\Protocol\ProtocolException; + +/** + * Handler for the integer response type in the standard Redis wire protocol. + * It translates the payload an integer or NULL. + * + * @link http://redis.io/topics/protocol + * + * @author Daniele Alessandri + */ +class IntegerResponse implements ResponseHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handle(CompositeConnectionInterface $connection, $payload) + { + if (is_numeric($payload)) { + $integer = (int) $payload; + return $integer == $payload ? $integer : $payload; + } + + if ($payload !== 'nil') { + CommunicationException::handle(new ProtocolException( + $connection, "Cannot parse '$payload' as a valid numeric response." + )); + } + + return; + } +} diff --git a/vendor/predis/predis/src/Protocol/Text/Handler/MultiBulkResponse.php b/vendor/predis/predis/src/Protocol/Text/Handler/MultiBulkResponse.php new file mode 100644 index 000000000..820b9b4a6 --- /dev/null +++ b/vendor/predis/predis/src/Protocol/Text/Handler/MultiBulkResponse.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol\Text\Handler; + +use Predis\CommunicationException; +use Predis\Connection\CompositeConnectionInterface; +use Predis\Protocol\ProtocolException; + +/** + * Handler for the multibulk response type in the standard Redis wire protocol. + * It returns multibulk responses as PHP arrays. + * + * @link http://redis.io/topics/protocol + * + * @author Daniele Alessandri + */ +class MultiBulkResponse implements ResponseHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handle(CompositeConnectionInterface $connection, $payload) + { + $length = (int) $payload; + + if ("$length" !== $payload) { + CommunicationException::handle(new ProtocolException( + $connection, "Cannot parse '$payload' as a valid length of a multi-bulk response." + )); + } + + if ($length === -1) { + return; + } + + $list = array(); + + if ($length > 0) { + $handlersCache = array(); + $reader = $connection->getProtocol()->getResponseReader(); + + for ($i = 0; $i < $length; ++$i) { + $header = $connection->readLine(); + $prefix = $header[0]; + + if (isset($handlersCache[$prefix])) { + $handler = $handlersCache[$prefix]; + } else { + $handler = $reader->getHandler($prefix); + $handlersCache[$prefix] = $handler; + } + + $list[$i] = $handler->handle($connection, substr($header, 1)); + } + } + + return $list; + } +} diff --git a/vendor/predis/predis/src/Protocol/Text/Handler/ResponseHandlerInterface.php b/vendor/predis/predis/src/Protocol/Text/Handler/ResponseHandlerInterface.php new file mode 100644 index 000000000..ca08a9c53 --- /dev/null +++ b/vendor/predis/predis/src/Protocol/Text/Handler/ResponseHandlerInterface.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol\Text\Handler; + +use Predis\Connection\CompositeConnectionInterface; + +/** + * Defines a pluggable handler used to parse a particular type of response. + * + * @author Daniele Alessandri + */ +interface ResponseHandlerInterface +{ + /** + * Deserializes a response returned by Redis and reads more data from the + * connection if needed. + * + * @param CompositeConnectionInterface $connection Redis connection. + * @param string $payload String payload. + * + * @return mixed + */ + public function handle(CompositeConnectionInterface $connection, $payload); +} diff --git a/vendor/predis/predis/src/Protocol/Text/Handler/StatusResponse.php b/vendor/predis/predis/src/Protocol/Text/Handler/StatusResponse.php new file mode 100644 index 000000000..7bde5558f --- /dev/null +++ b/vendor/predis/predis/src/Protocol/Text/Handler/StatusResponse.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol\Text\Handler; + +use Predis\Connection\CompositeConnectionInterface; +use Predis\Response\Status; + +/** + * Handler for the status response type in the standard Redis wire protocol. It + * translates certain classes of status response to PHP objects or just returns + * the payload as a string. + * + * @link http://redis.io/topics/protocol + * + * @author Daniele Alessandri + */ +class StatusResponse implements ResponseHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handle(CompositeConnectionInterface $connection, $payload) + { + return Status::get($payload); + } +} diff --git a/vendor/predis/predis/src/Protocol/Text/Handler/StreamableMultiBulkResponse.php b/vendor/predis/predis/src/Protocol/Text/Handler/StreamableMultiBulkResponse.php new file mode 100644 index 000000000..7cdb736af --- /dev/null +++ b/vendor/predis/predis/src/Protocol/Text/Handler/StreamableMultiBulkResponse.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol\Text\Handler; + +use Predis\CommunicationException; +use Predis\Connection\CompositeConnectionInterface; +use Predis\Protocol\ProtocolException; +use Predis\Response\Iterator\MultiBulk as MultiBulkIterator; + +/** + * Handler for the multibulk response type in the standard Redis wire protocol. + * It returns multibulk responses as iterators that can stream bulk elements. + * + * Streamable multibulk responses are not globally supported by the abstractions + * built-in into Predis, such as transactions or pipelines. Use them with care! + * + * @link http://redis.io/topics/protocol + * + * @author Daniele Alessandri + */ +class StreamableMultiBulkResponse implements ResponseHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handle(CompositeConnectionInterface $connection, $payload) + { + $length = (int) $payload; + + if ("$length" != $payload) { + CommunicationException::handle(new ProtocolException( + $connection, "Cannot parse '$payload' as a valid length for a multi-bulk response." + )); + } + + return new MultiBulkIterator($connection, $length); + } +} diff --git a/vendor/predis/predis/src/Protocol/Text/ProtocolProcessor.php b/vendor/predis/predis/src/Protocol/Text/ProtocolProcessor.php new file mode 100644 index 000000000..99acdf864 --- /dev/null +++ b/vendor/predis/predis/src/Protocol/Text/ProtocolProcessor.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol\Text; + +use Predis\Command\CommandInterface; +use Predis\CommunicationException; +use Predis\Connection\CompositeConnectionInterface; +use Predis\Protocol\ProtocolException; +use Predis\Protocol\ProtocolProcessorInterface; +use Predis\Response\Error as ErrorResponse; +use Predis\Response\Iterator\MultiBulk as MultiBulkIterator; +use Predis\Response\Status as StatusResponse; + +/** + * Protocol processor for the standard Redis wire protocol. + * + * @link http://redis.io/topics/protocol + * + * @author Daniele Alessandri + */ +class ProtocolProcessor implements ProtocolProcessorInterface +{ + protected $mbiterable; + protected $serializer; + + /** + * + */ + public function __construct() + { + $this->mbiterable = false; + $this->serializer = new RequestSerializer(); + } + + /** + * {@inheritdoc} + */ + public function write(CompositeConnectionInterface $connection, CommandInterface $command) + { + $request = $this->serializer->serialize($command); + $connection->writeBuffer($request); + } + + /** + * {@inheritdoc} + */ + public function read(CompositeConnectionInterface $connection) + { + $chunk = $connection->readLine(); + $prefix = $chunk[0]; + $payload = substr($chunk, 1); + + switch ($prefix) { + case '+': + return new StatusResponse($payload); + + case '$': + $size = (int) $payload; + if ($size === -1) { + return; + } + + return substr($connection->readBuffer($size + 2), 0, -2); + + case '*': + $count = (int) $payload; + + if ($count === -1) { + return; + } + if ($this->mbiterable) { + return new MultiBulkIterator($connection, $count); + } + + $multibulk = array(); + + for ($i = 0; $i < $count; ++$i) { + $multibulk[$i] = $this->read($connection); + } + + return $multibulk; + + case ':': + $integer = (int) $payload; + return $integer == $payload ? $integer : $payload; + + case '-': + return new ErrorResponse($payload); + + default: + CommunicationException::handle(new ProtocolException( + $connection, "Unknown response prefix: '$prefix'." + )); + + return; + } + } + + /** + * Enables or disables returning multibulk responses as specialized PHP + * iterators used to stream bulk elements of a multibulk response instead + * returning a plain array. + * + * Streamable multibulk responses are not globally supported by the + * abstractions built-in into Predis, such as transactions or pipelines. + * Use them with care! + * + * @param bool $value Enable or disable streamable multibulk responses. + */ + public function useIterableMultibulk($value) + { + $this->mbiterable = (bool) $value; + } +} diff --git a/vendor/predis/predis/src/Protocol/Text/RequestSerializer.php b/vendor/predis/predis/src/Protocol/Text/RequestSerializer.php new file mode 100644 index 000000000..859595b14 --- /dev/null +++ b/vendor/predis/predis/src/Protocol/Text/RequestSerializer.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol\Text; + +use Predis\Command\CommandInterface; +use Predis\Protocol\RequestSerializerInterface; + +/** + * Request serializer for the standard Redis wire protocol. + * + * @link http://redis.io/topics/protocol + * + * @author Daniele Alessandri + */ +class RequestSerializer implements RequestSerializerInterface +{ + /** + * {@inheritdoc} + */ + public function serialize(CommandInterface $command) + { + $commandID = $command->getId(); + $arguments = $command->getArguments(); + + $cmdlen = strlen($commandID); + $reqlen = count($arguments) + 1; + + $buffer = "*{$reqlen}\r\n\${$cmdlen}\r\n{$commandID}\r\n"; + + foreach ($arguments as $argument) { + $arglen = strlen($argument); + $buffer .= "\${$arglen}\r\n{$argument}\r\n"; + } + + return $buffer; + } +} diff --git a/vendor/predis/predis/src/Protocol/Text/ResponseReader.php b/vendor/predis/predis/src/Protocol/Text/ResponseReader.php new file mode 100644 index 000000000..d96218dfa --- /dev/null +++ b/vendor/predis/predis/src/Protocol/Text/ResponseReader.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol\Text; + +use Predis\CommunicationException; +use Predis\Connection\CompositeConnectionInterface; +use Predis\Protocol\ProtocolException; +use Predis\Protocol\ResponseReaderInterface; + +/** + * Response reader for the standard Redis wire protocol. + * + * @link http://redis.io/topics/protocol + * + * @author Daniele Alessandri + */ +class ResponseReader implements ResponseReaderInterface +{ + protected $handlers; + + /** + * + */ + public function __construct() + { + $this->handlers = $this->getDefaultHandlers(); + } + + /** + * Returns the default handlers for the supported type of responses. + * + * @return array + */ + protected function getDefaultHandlers() + { + return array( + '+' => new Handler\StatusResponse(), + '-' => new Handler\ErrorResponse(), + ':' => new Handler\IntegerResponse(), + '$' => new Handler\BulkResponse(), + '*' => new Handler\MultiBulkResponse(), + ); + } + + /** + * Sets the handler for the specified prefix identifying the response type. + * + * @param string $prefix Identifier of the type of response. + * @param Handler\ResponseHandlerInterface $handler Response handler. + */ + public function setHandler($prefix, Handler\ResponseHandlerInterface $handler) + { + $this->handlers[$prefix] = $handler; + } + + /** + * Returns the response handler associated to a certain type of response. + * + * @param string $prefix Identifier of the type of response. + * + * @return Handler\ResponseHandlerInterface + */ + public function getHandler($prefix) + { + if (isset($this->handlers[$prefix])) { + return $this->handlers[$prefix]; + } + + return; + } + + /** + * {@inheritdoc} + */ + public function read(CompositeConnectionInterface $connection) + { + $header = $connection->readLine(); + + if ($header === '') { + $this->onProtocolError($connection, 'Unexpected empty reponse header.'); + } + + $prefix = $header[0]; + + if (!isset($this->handlers[$prefix])) { + $this->onProtocolError($connection, "Unknown response prefix: '$prefix'."); + } + + $payload = $this->handlers[$prefix]->handle($connection, substr($header, 1)); + + return $payload; + } + + /** + * Handles protocol errors generated while reading responses from a + * connection. + * + * @param CompositeConnectionInterface $connection Redis connection that generated the error. + * @param string $message Error message. + */ + protected function onProtocolError(CompositeConnectionInterface $connection, $message) + { + CommunicationException::handle( + new ProtocolException($connection, $message) + ); + } +} diff --git a/vendor/predis/predis/src/PubSub/AbstractConsumer.php b/vendor/predis/predis/src/PubSub/AbstractConsumer.php new file mode 100644 index 000000000..8c6a71dd3 --- /dev/null +++ b/vendor/predis/predis/src/PubSub/AbstractConsumer.php @@ -0,0 +1,219 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\PubSub; + +/** + * Base implementation of a PUB/SUB consumer abstraction based on PHP iterators. + * + * @author Daniele Alessandri + */ +abstract class AbstractConsumer implements \Iterator +{ + const SUBSCRIBE = 'subscribe'; + const UNSUBSCRIBE = 'unsubscribe'; + const PSUBSCRIBE = 'psubscribe'; + const PUNSUBSCRIBE = 'punsubscribe'; + const MESSAGE = 'message'; + const PMESSAGE = 'pmessage'; + const PONG = 'pong'; + + const STATUS_VALID = 1; // 0b0001 + const STATUS_SUBSCRIBED = 2; // 0b0010 + const STATUS_PSUBSCRIBED = 4; // 0b0100 + + private $position = null; + private $statusFlags = self::STATUS_VALID; + + /** + * Automatically stops the consumer when the garbage collector kicks in. + */ + public function __destruct() + { + $this->stop(true); + } + + /** + * Checks if the specified flag is valid based on the state of the consumer. + * + * @param int $value Flag. + * + * @return bool + */ + protected function isFlagSet($value) + { + return ($this->statusFlags & $value) === $value; + } + + /** + * Subscribes to the specified channels. + * + * @param mixed $channel,... One or more channel names. + */ + public function subscribe($channel /*, ... */) + { + $this->writeRequest(self::SUBSCRIBE, func_get_args()); + $this->statusFlags |= self::STATUS_SUBSCRIBED; + } + + /** + * Unsubscribes from the specified channels. + * + * @param string ... One or more channel names. + */ + public function unsubscribe(/* ... */) + { + $this->writeRequest(self::UNSUBSCRIBE, func_get_args()); + } + + /** + * Subscribes to the specified channels using a pattern. + * + * @param mixed $pattern,... One or more channel name patterns. + */ + public function psubscribe($pattern /* ... */) + { + $this->writeRequest(self::PSUBSCRIBE, func_get_args()); + $this->statusFlags |= self::STATUS_PSUBSCRIBED; + } + + /** + * Unsubscribes from the specified channels using a pattern. + * + * @param string ... One or more channel name patterns. + */ + public function punsubscribe(/* ... */) + { + $this->writeRequest(self::PUNSUBSCRIBE, func_get_args()); + } + + /** + * PING the server with an optional payload that will be echoed as a + * PONG message in the pub/sub loop. + * + * @param string $payload Optional PING payload. + */ + public function ping($payload = null) + { + $this->writeRequest('PING', array($payload)); + } + + /** + * Closes the context by unsubscribing from all the subscribed channels. The + * context can be forcefully closed by dropping the underlying connection. + * + * @param bool $drop Indicates if the context should be closed by dropping the connection. + * + * @return bool Returns false when there are no pending messages. + */ + public function stop($drop = false) + { + if (!$this->valid()) { + return false; + } + + if ($drop) { + $this->invalidate(); + $this->disconnect(); + } else { + if ($this->isFlagSet(self::STATUS_SUBSCRIBED)) { + $this->unsubscribe(); + } + if ($this->isFlagSet(self::STATUS_PSUBSCRIBED)) { + $this->punsubscribe(); + } + } + + return !$drop; + } + + /** + * Closes the underlying connection when forcing a disconnection. + */ + abstract protected function disconnect(); + + /** + * Writes a Redis command on the underlying connection. + * + * @param string $method Command ID. + * @param array $arguments Arguments for the command. + */ + abstract protected function writeRequest($method, $arguments); + + /** + * {@inheritdoc} + */ + public function rewind() + { + // NOOP + } + + /** + * Returns the last message payload retrieved from the server and generated + * by one of the active subscriptions. + * + * @return array + */ + public function current() + { + return $this->getValue(); + } + + /** + * {@inheritdoc} + */ + public function key() + { + return $this->position; + } + + /** + * {@inheritdoc} + */ + public function next() + { + if ($this->valid()) { + ++$this->position; + } + + return $this->position; + } + + /** + * Checks if the the consumer is still in a valid state to continue. + * + * @return bool + */ + public function valid() + { + $isValid = $this->isFlagSet(self::STATUS_VALID); + $subscriptionFlags = self::STATUS_SUBSCRIBED | self::STATUS_PSUBSCRIBED; + $hasSubscriptions = ($this->statusFlags & $subscriptionFlags) > 0; + + return $isValid && $hasSubscriptions; + } + + /** + * Resets the state of the consumer. + */ + protected function invalidate() + { + $this->statusFlags = 0; // 0b0000; + } + + /** + * Waits for a new message from the server generated by one of the active + * subscriptions and returns it when available. + * + * @return array + */ + abstract protected function getValue(); +} diff --git a/vendor/predis/predis/src/PubSub/Consumer.php b/vendor/predis/predis/src/PubSub/Consumer.php new file mode 100644 index 000000000..5f2d8a8bc --- /dev/null +++ b/vendor/predis/predis/src/PubSub/Consumer.php @@ -0,0 +1,158 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\PubSub; + +use Predis\ClientException; +use Predis\ClientInterface; +use Predis\Command\Command; +use Predis\Connection\AggregateConnectionInterface; +use Predis\NotSupportedException; + +/** + * PUB/SUB consumer abstraction. + * + * @author Daniele Alessandri + */ +class Consumer extends AbstractConsumer +{ + private $client; + private $options; + + /** + * @param ClientInterface $client Client instance used by the consumer. + * @param array $options Options for the consumer initialization. + */ + public function __construct(ClientInterface $client, array $options = null) + { + $this->checkCapabilities($client); + + $this->options = $options ?: array(); + $this->client = $client; + + $this->genericSubscribeInit('subscribe'); + $this->genericSubscribeInit('psubscribe'); + } + + /** + * Returns the underlying client instance used by the pub/sub iterator. + * + * @return ClientInterface + */ + public function getClient() + { + return $this->client; + } + + /** + * Checks if the client instance satisfies the required conditions needed to + * initialize a PUB/SUB consumer. + * + * @param ClientInterface $client Client instance used by the consumer. + * + * @throws NotSupportedException + */ + private function checkCapabilities(ClientInterface $client) + { + if ($client->getConnection() instanceof AggregateConnectionInterface) { + throw new NotSupportedException( + 'Cannot initialize a PUB/SUB consumer over aggregate connections.' + ); + } + + $commands = array('publish', 'subscribe', 'unsubscribe', 'psubscribe', 'punsubscribe'); + + if ($client->getProfile()->supportsCommands($commands) === false) { + throw new NotSupportedException( + 'The current profile does not support PUB/SUB related commands.' + ); + } + } + + /** + * This method shares the logic to handle both SUBSCRIBE and PSUBSCRIBE. + * + * @param string $subscribeAction Type of subscription. + */ + private function genericSubscribeInit($subscribeAction) + { + if (isset($this->options[$subscribeAction])) { + $this->$subscribeAction($this->options[$subscribeAction]); + } + } + + /** + * {@inheritdoc} + */ + protected function writeRequest($method, $arguments) + { + $this->client->getConnection()->writeRequest( + $this->client->createCommand($method, + Command::normalizeArguments($arguments) + ) + ); + } + + /** + * {@inheritdoc} + */ + protected function disconnect() + { + $this->client->disconnect(); + } + + /** + * {@inheritdoc} + */ + protected function getValue() + { + $response = $this->client->getConnection()->read(); + + switch ($response[0]) { + case self::SUBSCRIBE: + case self::UNSUBSCRIBE: + case self::PSUBSCRIBE: + case self::PUNSUBSCRIBE: + if ($response[2] === 0) { + $this->invalidate(); + } + // The missing break here is intentional as we must process + // subscriptions and unsubscriptions as standard messages. + // no break + + case self::MESSAGE: + return (object) array( + 'kind' => $response[0], + 'channel' => $response[1], + 'payload' => $response[2], + ); + + case self::PMESSAGE: + return (object) array( + 'kind' => $response[0], + 'pattern' => $response[1], + 'channel' => $response[2], + 'payload' => $response[3], + ); + + case self::PONG: + return (object) array( + 'kind' => $response[0], + 'payload' => $response[1], + ); + + default: + throw new ClientException( + "Unknown message type '{$response[0]}' received in the PUB/SUB context." + ); + } + } +} diff --git a/vendor/predis/predis/src/PubSub/DispatcherLoop.php b/vendor/predis/predis/src/PubSub/DispatcherLoop.php new file mode 100644 index 000000000..d0369e73d --- /dev/null +++ b/vendor/predis/predis/src/PubSub/DispatcherLoop.php @@ -0,0 +1,170 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\PubSub; + +/** + * Method-dispatcher loop built around the client-side abstraction of a Redis + * PUB / SUB context. + * + * @author Daniele Alessandri + */ +class DispatcherLoop +{ + private $pubsub; + + protected $callbacks; + protected $defaultCallback; + protected $subscriptionCallback; + + /** + * @param Consumer $pubsub PubSub consumer instance used by the loop. + */ + public function __construct(Consumer $pubsub) + { + $this->callbacks = array(); + $this->pubsub = $pubsub; + } + + /** + * Checks if the passed argument is a valid callback. + * + * @param mixed $callable A callback. + * + * @throws \InvalidArgumentException + */ + protected function assertCallback($callable) + { + if (!is_callable($callable)) { + throw new \InvalidArgumentException('The given argument must be a callable object.'); + } + } + + /** + * Returns the underlying PUB / SUB context. + * + * @return Consumer + */ + public function getPubSubConsumer() + { + return $this->pubsub; + } + + /** + * Sets a callback that gets invoked upon new subscriptions. + * + * @param mixed $callable A callback. + */ + public function subscriptionCallback($callable = null) + { + if (isset($callable)) { + $this->assertCallback($callable); + } + + $this->subscriptionCallback = $callable; + } + + /** + * Sets a callback that gets invoked when a message is received on a + * channel that does not have an associated callback. + * + * @param mixed $callable A callback. + */ + public function defaultCallback($callable = null) + { + if (isset($callable)) { + $this->assertCallback($callable); + } + + $this->subscriptionCallback = $callable; + } + + /** + * Binds a callback to a channel. + * + * @param string $channel Channel name. + * @param callable $callback A callback. + */ + public function attachCallback($channel, $callback) + { + $callbackName = $this->getPrefixKeys().$channel; + + $this->assertCallback($callback); + $this->callbacks[$callbackName] = $callback; + $this->pubsub->subscribe($channel); + } + + /** + * Stops listening to a channel and removes the associated callback. + * + * @param string $channel Redis channel. + */ + public function detachCallback($channel) + { + $callbackName = $this->getPrefixKeys().$channel; + + if (isset($this->callbacks[$callbackName])) { + unset($this->callbacks[$callbackName]); + $this->pubsub->unsubscribe($channel); + } + } + + /** + * Starts the dispatcher loop. + */ + public function run() + { + foreach ($this->pubsub as $message) { + $kind = $message->kind; + + if ($kind !== Consumer::MESSAGE && $kind !== Consumer::PMESSAGE) { + if (isset($this->subscriptionCallback)) { + $callback = $this->subscriptionCallback; + call_user_func($callback, $message); + } + + continue; + } + + if (isset($this->callbacks[$message->channel])) { + $callback = $this->callbacks[$message->channel]; + call_user_func($callback, $message->payload); + } elseif (isset($this->defaultCallback)) { + $callback = $this->defaultCallback; + call_user_func($callback, $message); + } + } + } + + /** + * Terminates the dispatcher loop. + */ + public function stop() + { + $this->pubsub->stop(); + } + + /** + * Return the prefix used for keys. + * + * @return string + */ + protected function getPrefixKeys() + { + $options = $this->pubsub->getClient()->getOptions(); + + if (isset($options->prefix)) { + return $options->prefix->getPrefix(); + } + + return ''; + } +} diff --git a/vendor/predis/predis/src/Replication/MissingMasterException.php b/vendor/predis/predis/src/Replication/MissingMasterException.php new file mode 100644 index 000000000..223bd2d42 --- /dev/null +++ b/vendor/predis/predis/src/Replication/MissingMasterException.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Replication; + +use Predis\ClientException; + +/** + * Exception class that identifies when master is missing in a replication setup. + * + * @author Daniele Alessandri + */ +class MissingMasterException extends ClientException +{ +} diff --git a/vendor/predis/predis/src/Replication/ReplicationStrategy.php b/vendor/predis/predis/src/Replication/ReplicationStrategy.php new file mode 100644 index 000000000..9a6015d56 --- /dev/null +++ b/vendor/predis/predis/src/Replication/ReplicationStrategy.php @@ -0,0 +1,278 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Replication; + +use Predis\Command\CommandInterface; +use Predis\NotSupportedException; + +/** + * Defines a strategy for master/slave replication. + * + * @author Daniele Alessandri + */ +class ReplicationStrategy +{ + protected $disallowed; + protected $readonly; + protected $readonlySHA1; + + /** + * + */ + public function __construct() + { + $this->disallowed = $this->getDisallowedOperations(); + $this->readonly = $this->getReadOnlyOperations(); + $this->readonlySHA1 = array(); + } + + /** + * Returns if the specified command will perform a read-only operation + * on Redis or not. + * + * @param CommandInterface $command Command instance. + * + * @throws NotSupportedException + * + * @return bool + */ + public function isReadOperation(CommandInterface $command) + { + if (isset($this->disallowed[$id = $command->getId()])) { + throw new NotSupportedException( + "The command '$id' is not allowed in replication mode." + ); + } + + if (isset($this->readonly[$id])) { + if (true === $readonly = $this->readonly[$id]) { + return true; + } + + return call_user_func($readonly, $command); + } + + if (($eval = $id === 'EVAL') || $id === 'EVALSHA') { + $sha1 = $eval ? sha1($command->getArgument(0)) : $command->getArgument(0); + + if (isset($this->readonlySHA1[$sha1])) { + if (true === $readonly = $this->readonlySHA1[$sha1]) { + return true; + } + + return call_user_func($readonly, $command); + } + } + + return false; + } + + /** + * Returns if the specified command is not allowed for execution in a master + * / slave replication context. + * + * @param CommandInterface $command Command instance. + * + * @return bool + */ + public function isDisallowedOperation(CommandInterface $command) + { + return isset($this->disallowed[$command->getId()]); + } + + /** + * Checks if BITFIELD performs a read-only operation by looking for certain + * SET and INCRYBY modifiers in the arguments array of the command. + * + * @param CommandInterface $command Command instance. + * + * @return bool + */ + protected function isBitfieldReadOnly(CommandInterface $command) + { + $arguments = $command->getArguments(); + $argc = count($arguments); + + if ($argc >= 2) { + for ($i = 1; $i < $argc; ++$i) { + $argument = strtoupper($arguments[$i]); + if ($argument === 'SET' || $argument === 'INCRBY') { + return false; + } + } + } + + return true; + } + + /** + * Checks if a GEORADIUS command is a readable operation by parsing the + * arguments array of the specified commad instance. + * + * @param CommandInterface $command Command instance. + * + * @return bool + */ + protected function isGeoradiusReadOnly(CommandInterface $command) + { + $arguments = $command->getArguments(); + $argc = count($arguments); + $startIndex = $command->getId() === 'GEORADIUS' ? 5 : 4; + + if ($argc > $startIndex) { + for ($i = $startIndex; $i < $argc; ++$i) { + $argument = strtoupper($arguments[$i]); + if ($argument === 'STORE' || $argument === 'STOREDIST') { + return false; + } + } + } + + return true; + } + + /** + * Marks a command as a read-only operation. + * + * When the behavior of a command can be decided only at runtime depending + * on its arguments, a callable object can be provided to dynamically check + * if the specified command performs a read or a write operation. + * + * @param string $commandID Command ID. + * @param mixed $readonly A boolean value or a callable object. + */ + public function setCommandReadOnly($commandID, $readonly = true) + { + $commandID = strtoupper($commandID); + + if ($readonly) { + $this->readonly[$commandID] = $readonly; + } else { + unset($this->readonly[$commandID]); + } + } + + /** + * Marks a Lua script for EVAL and EVALSHA as a read-only operation. When + * the behaviour of a script can be decided only at runtime depending on + * its arguments, a callable object can be provided to dynamically check + * if the passed instance of EVAL or EVALSHA performs write operations or + * not. + * + * @param string $script Body of the Lua script. + * @param mixed $readonly A boolean value or a callable object. + */ + public function setScriptReadOnly($script, $readonly = true) + { + $sha1 = sha1($script); + + if ($readonly) { + $this->readonlySHA1[$sha1] = $readonly; + } else { + unset($this->readonlySHA1[$sha1]); + } + } + + /** + * Returns the default list of disallowed commands. + * + * @return array + */ + protected function getDisallowedOperations() + { + return array( + 'SHUTDOWN' => true, + 'INFO' => true, + 'DBSIZE' => true, + 'LASTSAVE' => true, + 'CONFIG' => true, + 'MONITOR' => true, + 'SLAVEOF' => true, + 'SAVE' => true, + 'BGSAVE' => true, + 'BGREWRITEAOF' => true, + 'SLOWLOG' => true, + ); + } + + /** + * Returns the default list of commands performing read-only operations. + * + * @return array + */ + protected function getReadOnlyOperations() + { + return array( + 'EXISTS' => true, + 'TYPE' => true, + 'KEYS' => true, + 'SCAN' => true, + 'RANDOMKEY' => true, + 'TTL' => true, + 'GET' => true, + 'MGET' => true, + 'SUBSTR' => true, + 'STRLEN' => true, + 'GETRANGE' => true, + 'GETBIT' => true, + 'LLEN' => true, + 'LRANGE' => true, + 'LINDEX' => true, + 'SCARD' => true, + 'SISMEMBER' => true, + 'SINTER' => true, + 'SUNION' => true, + 'SDIFF' => true, + 'SMEMBERS' => true, + 'SSCAN' => true, + 'SRANDMEMBER' => true, + 'ZRANGE' => true, + 'ZREVRANGE' => true, + 'ZRANGEBYSCORE' => true, + 'ZREVRANGEBYSCORE' => true, + 'ZCARD' => true, + 'ZSCORE' => true, + 'ZCOUNT' => true, + 'ZRANK' => true, + 'ZREVRANK' => true, + 'ZSCAN' => true, + 'ZLEXCOUNT' => true, + 'ZRANGEBYLEX' => true, + 'ZREVRANGEBYLEX' => true, + 'HGET' => true, + 'HMGET' => true, + 'HEXISTS' => true, + 'HLEN' => true, + 'HKEYS' => true, + 'HVALS' => true, + 'HGETALL' => true, + 'HSCAN' => true, + 'HSTRLEN' => true, + 'PING' => true, + 'AUTH' => true, + 'SELECT' => true, + 'ECHO' => true, + 'QUIT' => true, + 'OBJECT' => true, + 'BITCOUNT' => true, + 'BITPOS' => true, + 'TIME' => true, + 'PFCOUNT' => true, + 'BITFIELD' => array($this, 'isBitfieldReadOnly'), + 'GEOHASH' => true, + 'GEOPOS' => true, + 'GEODIST' => true, + 'GEORADIUS' => array($this, 'isGeoradiusReadOnly'), + 'GEORADIUSBYMEMBER' => array($this, 'isGeoradiusReadOnly'), + ); + } +} diff --git a/vendor/predis/predis/src/Replication/RoleException.php b/vendor/predis/predis/src/Replication/RoleException.php new file mode 100644 index 000000000..0d9954bf5 --- /dev/null +++ b/vendor/predis/predis/src/Replication/RoleException.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Replication; + +use Predis\CommunicationException; + +/** + * Exception class that identifies a role mismatch when connecting to node + * managed by redis-sentinel. + * + * @author Daniele Alessandri + */ +class RoleException extends CommunicationException +{ +} diff --git a/vendor/predis/predis/src/Response/Error.php b/vendor/predis/predis/src/Response/Error.php new file mode 100644 index 000000000..3933857e2 --- /dev/null +++ b/vendor/predis/predis/src/Response/Error.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Response; + +/** + * Represents an error returned by Redis (-ERR responses) during the execution + * of a command on the server. + * + * @author Daniele Alessandri + */ +class Error implements ErrorInterface +{ + private $message; + + /** + * @param string $message Error message returned by Redis + */ + public function __construct($message) + { + $this->message = $message; + } + + /** + * {@inheritdoc} + */ + public function getMessage() + { + return $this->message; + } + + /** + * {@inheritdoc} + */ + public function getErrorType() + { + list($errorType) = explode(' ', $this->getMessage(), 2); + + return $errorType; + } + + /** + * Converts the object to its string representation. + * + * @return string + */ + public function __toString() + { + return $this->getMessage(); + } +} diff --git a/vendor/predis/predis/src/Response/ErrorInterface.php b/vendor/predis/predis/src/Response/ErrorInterface.php new file mode 100644 index 000000000..a4a4a02f7 --- /dev/null +++ b/vendor/predis/predis/src/Response/ErrorInterface.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Response; + +/** + * Represents an error returned by Redis (responses identified by "-" in the + * Redis protocol) during the execution of an operation on the server. + * + * @author Daniele Alessandri + */ +interface ErrorInterface extends ResponseInterface +{ + /** + * Returns the error message. + * + * @return string + */ + public function getMessage(); + + /** + * Returns the error type (e.g. ERR, ASK, MOVED). + * + * @return string + */ + public function getErrorType(); +} diff --git a/vendor/predis/predis/src/Response/Iterator/MultiBulk.php b/vendor/predis/predis/src/Response/Iterator/MultiBulk.php new file mode 100644 index 000000000..b1d29241c --- /dev/null +++ b/vendor/predis/predis/src/Response/Iterator/MultiBulk.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Response\Iterator; + +use Predis\Connection\NodeConnectionInterface; + +/** + * Streamable multibulk response. + * + * @author Daniele Alessandri + */ +class MultiBulk extends MultiBulkIterator +{ + private $connection; + + /** + * @param NodeConnectionInterface $connection Connection to Redis. + * @param int $size Number of elements of the multibulk response. + */ + public function __construct(NodeConnectionInterface $connection, $size) + { + $this->connection = $connection; + $this->size = $size; + $this->position = 0; + $this->current = $size > 0 ? $this->getValue() : null; + } + + /** + * Handles the synchronization of the client with the Redis protocol when + * the garbage collector kicks in (e.g. when the iterator goes out of the + * scope of a foreach or it is unset). + */ + public function __destruct() + { + $this->drop(true); + } + + /** + * Drop queued elements that have not been read from the connection either + * by consuming the rest of the multibulk response or quickly by closing the + * underlying connection. + * + * @param bool $disconnect Consume the iterator or drop the connection. + */ + public function drop($disconnect = false) + { + if ($disconnect) { + if ($this->valid()) { + $this->position = $this->size; + $this->connection->disconnect(); + } + } else { + while ($this->valid()) { + $this->next(); + } + } + } + + /** + * Reads the next item of the multibulk response from the connection. + * + * @return mixed + */ + protected function getValue() + { + return $this->connection->read(); + } +} diff --git a/vendor/predis/predis/src/Response/Iterator/MultiBulkIterator.php b/vendor/predis/predis/src/Response/Iterator/MultiBulkIterator.php new file mode 100644 index 000000000..5d328869b --- /dev/null +++ b/vendor/predis/predis/src/Response/Iterator/MultiBulkIterator.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Response\Iterator; + +use Predis\Response\ResponseInterface; + +/** + * Iterator that abstracts the access to multibulk responses allowing them to be + * consumed in a streamable fashion without keeping the whole payload in memory. + * + * This iterator does not support rewinding which means that the iteration, once + * consumed, cannot be restarted. + * + * Always make sure that the whole iteration is consumed (or dropped) to prevent + * protocol desynchronization issues. + * + * @author Daniele Alessandri + */ +abstract class MultiBulkIterator implements \Iterator, \Countable, ResponseInterface +{ + protected $current; + protected $position; + protected $size; + + /** + * {@inheritdoc} + */ + public function rewind() + { + // NOOP + } + + /** + * {@inheritdoc} + */ + public function current() + { + return $this->current; + } + + /** + * {@inheritdoc} + */ + public function key() + { + return $this->position; + } + + /** + * {@inheritdoc} + */ + public function next() + { + if (++$this->position < $this->size) { + $this->current = $this->getValue(); + } + } + + /** + * {@inheritdoc} + */ + public function valid() + { + return $this->position < $this->size; + } + + /** + * Returns the number of items comprising the whole multibulk response. + * + * This method should be used instead of iterator_count() to get the size of + * the current multibulk response since the former consumes the iteration to + * count the number of elements, but our iterators do not support rewinding. + * + * @return int + */ + public function count() + { + return $this->size; + } + + /** + * Returns the current position of the iterator. + * + * @return int + */ + public function getPosition() + { + return $this->position; + } + + /** + * {@inheritdoc} + */ + abstract protected function getValue(); +} diff --git a/vendor/predis/predis/src/Response/Iterator/MultiBulkTuple.php b/vendor/predis/predis/src/Response/Iterator/MultiBulkTuple.php new file mode 100644 index 000000000..2b6f593c4 --- /dev/null +++ b/vendor/predis/predis/src/Response/Iterator/MultiBulkTuple.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Response\Iterator; + +/** + * Outer iterator consuming streamable multibulk responses by yielding tuples of + * keys and values. + * + * This wrapper is useful for responses to commands such as `HGETALL` that can + * be iterater as $key => $value pairs. + * + * @author Daniele Alessandri + */ +class MultiBulkTuple extends MultiBulk implements \OuterIterator +{ + private $iterator; + + /** + * @param MultiBulk $iterator Inner multibulk response iterator. + */ + public function __construct(MultiBulk $iterator) + { + $this->checkPreconditions($iterator); + + $this->size = count($iterator) / 2; + $this->iterator = $iterator; + $this->position = $iterator->getPosition(); + $this->current = $this->size > 0 ? $this->getValue() : null; + } + + /** + * Checks for valid preconditions. + * + * @param MultiBulk $iterator Inner multibulk response iterator. + * + * @throws \InvalidArgumentException + * @throws \UnexpectedValueException + */ + protected function checkPreconditions(MultiBulk $iterator) + { + if ($iterator->getPosition() !== 0) { + throw new \InvalidArgumentException( + 'Cannot initialize a tuple iterator using an already initiated iterator.' + ); + } + + if (($size = count($iterator)) % 2 !== 0) { + throw new \UnexpectedValueException('Invalid response size for a tuple iterator.'); + } + } + + /** + * {@inheritdoc} + */ + public function getInnerIterator() + { + return $this->iterator; + } + + /** + * {@inheritdoc} + */ + public function __destruct() + { + $this->iterator->drop(true); + } + + /** + * {@inheritdoc} + */ + protected function getValue() + { + $k = $this->iterator->current(); + $this->iterator->next(); + + $v = $this->iterator->current(); + $this->iterator->next(); + + return array($k, $v); + } +} diff --git a/vendor/predis/predis/src/Response/ResponseInterface.php b/vendor/predis/predis/src/Response/ResponseInterface.php new file mode 100644 index 000000000..0af135745 --- /dev/null +++ b/vendor/predis/predis/src/Response/ResponseInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Response; + +/** + * Represents a complex response object from Redis. + * + * @author Daniele Alessandri + */ +interface ResponseInterface +{ +} diff --git a/vendor/predis/predis/src/Response/ServerException.php b/vendor/predis/predis/src/Response/ServerException.php new file mode 100644 index 000000000..407dc5b76 --- /dev/null +++ b/vendor/predis/predis/src/Response/ServerException.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Response; + +use Predis\PredisException; + +/** + * Exception class that identifies server-side Redis errors. + * + * @author Daniele Alessandri + */ +class ServerException extends PredisException implements ErrorInterface +{ + /** + * Gets the type of the error returned by Redis. + * + * @return string + */ + public function getErrorType() + { + list($errorType) = explode(' ', $this->getMessage(), 2); + + return $errorType; + } + + /** + * Converts the exception to an instance of Predis\Response\Error. + * + * @return Error + */ + public function toErrorResponse() + { + return new Error($this->getMessage()); + } +} diff --git a/vendor/predis/predis/src/Response/Status.php b/vendor/predis/predis/src/Response/Status.php new file mode 100644 index 000000000..729bb6635 --- /dev/null +++ b/vendor/predis/predis/src/Response/Status.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Response; + +/** + * Represents a status response returned by Redis. + * + * @author Daniele Alessandri + */ +class Status implements ResponseInterface +{ + private static $OK; + private static $QUEUED; + + private $payload; + + /** + * @param string $payload Payload of the status response as returned by Redis. + */ + public function __construct($payload) + { + $this->payload = $payload; + } + + /** + * Converts the response object to its string representation. + * + * @return string + */ + public function __toString() + { + return $this->payload; + } + + /** + * Returns the payload of status response. + * + * @return string + */ + public function getPayload() + { + return $this->payload; + } + + /** + * Returns an instance of a status response object. + * + * Common status responses such as OK or QUEUED are cached in order to lower + * the global memory usage especially when using pipelines. + * + * @param string $payload Status response payload. + * + * @return string + */ + public static function get($payload) + { + switch ($payload) { + case 'OK': + case 'QUEUED': + if (isset(self::$$payload)) { + return self::$$payload; + } + + return self::$$payload = new self($payload); + + default: + return new self($payload); + } + } +} diff --git a/vendor/predis/predis/src/Session/Handler.php b/vendor/predis/predis/src/Session/Handler.php new file mode 100644 index 000000000..cecb9d539 --- /dev/null +++ b/vendor/predis/predis/src/Session/Handler.php @@ -0,0 +1,142 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Session; + +use Predis\ClientInterface; + +/** + * Session handler class that relies on Predis\Client to store PHP's sessions + * data into one or multiple Redis servers. + * + * This class is mostly intended for PHP 5.4 but it can be used under PHP 5.3 + * provided that a polyfill for `SessionHandlerInterface` is defined by either + * you or an external package such as `symfony/http-foundation`. + * + * @author Daniele Alessandri + */ +class Handler implements \SessionHandlerInterface +{ + protected $client; + protected $ttl; + + /** + * @param ClientInterface $client Fully initialized client instance. + * @param array $options Session handler options. + */ + public function __construct(ClientInterface $client, array $options = array()) + { + $this->client = $client; + + if (isset($options['gc_maxlifetime'])) { + $this->ttl = (int) $options['gc_maxlifetime']; + } else { + $this->ttl = ini_get('session.gc_maxlifetime'); + } + } + + /** + * Registers this instance as the current session handler. + */ + public function register() + { + if (PHP_VERSION_ID >= 50400) { + session_set_save_handler($this, true); + } else { + session_set_save_handler( + array($this, 'open'), + array($this, 'close'), + array($this, 'read'), + array($this, 'write'), + array($this, 'destroy'), + array($this, 'gc') + ); + } + } + + /** + * {@inheritdoc} + */ + public function open($save_path, $session_id) + { + // NOOP + return true; + } + + /** + * {@inheritdoc} + */ + public function close() + { + // NOOP + return true; + } + + /** + * {@inheritdoc} + */ + public function gc($maxlifetime) + { + // NOOP + return true; + } + + /** + * {@inheritdoc} + */ + public function read($session_id) + { + if ($data = $this->client->get($session_id)) { + return $data; + } + + return ''; + } + /** + * {@inheritdoc} + */ + public function write($session_id, $session_data) + { + $this->client->setex($session_id, $this->ttl, $session_data); + + return true; + } + + /** + * {@inheritdoc} + */ + public function destroy($session_id) + { + $this->client->del($session_id); + + return true; + } + + /** + * Returns the underlying client instance. + * + * @return ClientInterface + */ + public function getClient() + { + return $this->client; + } + + /** + * Returns the session max lifetime value. + * + * @return int + */ + public function getMaxLifeTime() + { + return $this->ttl; + } +} diff --git a/vendor/predis/predis/src/Transaction/AbortedMultiExecException.php b/vendor/predis/predis/src/Transaction/AbortedMultiExecException.php new file mode 100644 index 000000000..b36f38aac --- /dev/null +++ b/vendor/predis/predis/src/Transaction/AbortedMultiExecException.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Transaction; + +use Predis\PredisException; + +/** + * Exception class that identifies a MULTI / EXEC transaction aborted by Redis. + * + * @author Daniele Alessandri + */ +class AbortedMultiExecException extends PredisException +{ + private $transaction; + + /** + * @param MultiExec $transaction Transaction that generated the exception. + * @param string $message Error message. + * @param int $code Error code. + */ + public function __construct(MultiExec $transaction, $message, $code = null) + { + parent::__construct($message, $code); + $this->transaction = $transaction; + } + + /** + * Returns the transaction that generated the exception. + * + * @return MultiExec + */ + public function getTransaction() + { + return $this->transaction; + } +} diff --git a/vendor/predis/predis/src/Transaction/MultiExec.php b/vendor/predis/predis/src/Transaction/MultiExec.php new file mode 100644 index 000000000..0cf1962da --- /dev/null +++ b/vendor/predis/predis/src/Transaction/MultiExec.php @@ -0,0 +1,461 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Transaction; + +use Predis\ClientContextInterface; +use Predis\ClientException; +use Predis\ClientInterface; +use Predis\Command\CommandInterface; +use Predis\CommunicationException; +use Predis\Connection\AggregateConnectionInterface; +use Predis\NotSupportedException; +use Predis\Protocol\ProtocolException; +use Predis\Response\ErrorInterface as ErrorResponseInterface; +use Predis\Response\ServerException; +use Predis\Response\Status as StatusResponse; + +/** + * Client-side abstraction of a Redis transaction based on MULTI / EXEC. + * + * {@inheritdoc} + * + * @author Daniele Alessandri + */ +class MultiExec implements ClientContextInterface +{ + private $state; + + protected $client; + protected $commands; + protected $exceptions = true; + protected $attempts = 0; + protected $watchKeys = array(); + protected $modeCAS = false; + + /** + * @param ClientInterface $client Client instance used by the transaction. + * @param array $options Initialization options. + */ + public function __construct(ClientInterface $client, array $options = null) + { + $this->assertClient($client); + + $this->client = $client; + $this->state = new MultiExecState(); + + $this->configure($client, $options ?: array()); + $this->reset(); + } + + /** + * Checks if the passed client instance satisfies the required conditions + * needed to initialize the transaction object. + * + * @param ClientInterface $client Client instance used by the transaction object. + * + * @throws NotSupportedException + */ + private function assertClient(ClientInterface $client) + { + if ($client->getConnection() instanceof AggregateConnectionInterface) { + throw new NotSupportedException( + 'Cannot initialize a MULTI/EXEC transaction over aggregate connections.' + ); + } + + if (!$client->getProfile()->supportsCommands(array('MULTI', 'EXEC', 'DISCARD'))) { + throw new NotSupportedException( + 'The current profile does not support MULTI, EXEC and DISCARD.' + ); + } + } + + /** + * Configures the transaction using the provided options. + * + * @param ClientInterface $client Underlying client instance. + * @param array $options Array of options for the transaction. + **/ + protected function configure(ClientInterface $client, array $options) + { + if (isset($options['exceptions'])) { + $this->exceptions = (bool) $options['exceptions']; + } else { + $this->exceptions = $client->getOptions()->exceptions; + } + + if (isset($options['cas'])) { + $this->modeCAS = (bool) $options['cas']; + } + + if (isset($options['watch']) && $keys = $options['watch']) { + $this->watchKeys = $keys; + } + + if (isset($options['retry'])) { + $this->attempts = (int) $options['retry']; + } + } + + /** + * Resets the state of the transaction. + */ + protected function reset() + { + $this->state->reset(); + $this->commands = new \SplQueue(); + } + + /** + * Initializes the transaction context. + */ + protected function initialize() + { + if ($this->state->isInitialized()) { + return; + } + + if ($this->modeCAS) { + $this->state->flag(MultiExecState::CAS); + } + + if ($this->watchKeys) { + $this->watch($this->watchKeys); + } + + $cas = $this->state->isCAS(); + $discarded = $this->state->isDiscarded(); + + if (!$cas || ($cas && $discarded)) { + $this->call('MULTI'); + + if ($discarded) { + $this->state->unflag(MultiExecState::CAS); + } + } + + $this->state->unflag(MultiExecState::DISCARDED); + $this->state->flag(MultiExecState::INITIALIZED); + } + + /** + * Dynamically invokes a Redis command with the specified arguments. + * + * @param string $method Command ID. + * @param array $arguments Arguments for the command. + * + * @return mixed + */ + public function __call($method, $arguments) + { + return $this->executeCommand( + $this->client->createCommand($method, $arguments) + ); + } + + /** + * Executes a Redis command bypassing the transaction logic. + * + * @param string $commandID Command ID. + * @param array $arguments Arguments for the command. + * + * @throws ServerException + * + * @return mixed + */ + protected function call($commandID, array $arguments = array()) + { + $response = $this->client->executeCommand( + $this->client->createCommand($commandID, $arguments) + ); + + if ($response instanceof ErrorResponseInterface) { + throw new ServerException($response->getMessage()); + } + + return $response; + } + + /** + * Executes the specified Redis command. + * + * @param CommandInterface $command Command instance. + * + * @throws AbortedMultiExecException + * @throws CommunicationException + * + * @return $this|mixed + */ + public function executeCommand(CommandInterface $command) + { + $this->initialize(); + + if ($this->state->isCAS()) { + return $this->client->executeCommand($command); + } + + $response = $this->client->getConnection()->executeCommand($command); + + if ($response instanceof StatusResponse && $response == 'QUEUED') { + $this->commands->enqueue($command); + } elseif ($response instanceof ErrorResponseInterface) { + throw new AbortedMultiExecException($this, $response->getMessage()); + } else { + $this->onProtocolError('The server did not return a +QUEUED status response.'); + } + + return $this; + } + + /** + * Executes WATCH against one or more keys. + * + * @param string|array $keys One or more keys. + * + * @throws NotSupportedException + * @throws ClientException + * + * @return mixed + */ + public function watch($keys) + { + if (!$this->client->getProfile()->supportsCommand('WATCH')) { + throw new NotSupportedException('WATCH is not supported by the current profile.'); + } + + if ($this->state->isWatchAllowed()) { + throw new ClientException('Sending WATCH after MULTI is not allowed.'); + } + + $response = $this->call('WATCH', is_array($keys) ? $keys : array($keys)); + $this->state->flag(MultiExecState::WATCH); + + return $response; + } + + /** + * Finalizes the transaction by executing MULTI on the server. + * + * @return MultiExec + */ + public function multi() + { + if ($this->state->check(MultiExecState::INITIALIZED | MultiExecState::CAS)) { + $this->state->unflag(MultiExecState::CAS); + $this->call('MULTI'); + } else { + $this->initialize(); + } + + return $this; + } + + /** + * Executes UNWATCH. + * + * @throws NotSupportedException + * + * @return MultiExec + */ + public function unwatch() + { + if (!$this->client->getProfile()->supportsCommand('UNWATCH')) { + throw new NotSupportedException( + 'UNWATCH is not supported by the current profile.' + ); + } + + $this->state->unflag(MultiExecState::WATCH); + $this->__call('UNWATCH', array()); + + return $this; + } + + /** + * Resets the transaction by UNWATCH-ing the keys that are being WATCHed and + * DISCARD-ing pending commands that have been already sent to the server. + * + * @return MultiExec + */ + public function discard() + { + if ($this->state->isInitialized()) { + $this->call($this->state->isCAS() ? 'UNWATCH' : 'DISCARD'); + + $this->reset(); + $this->state->flag(MultiExecState::DISCARDED); + } + + return $this; + } + + /** + * Executes the whole transaction. + * + * @return mixed + */ + public function exec() + { + return $this->execute(); + } + + /** + * Checks the state of the transaction before execution. + * + * @param mixed $callable Callback for execution. + * + * @throws \InvalidArgumentException + * @throws ClientException + */ + private function checkBeforeExecution($callable) + { + if ($this->state->isExecuting()) { + throw new ClientException( + 'Cannot invoke "execute" or "exec" inside an active transaction context.' + ); + } + + if ($callable) { + if (!is_callable($callable)) { + throw new \InvalidArgumentException('The argument must be a callable object.'); + } + + if (!$this->commands->isEmpty()) { + $this->discard(); + + throw new ClientException( + 'Cannot execute a transaction block after using fluent interface.' + ); + } + } elseif ($this->attempts) { + $this->discard(); + + throw new ClientException( + 'Automatic retries are supported only when a callable block is provided.' + ); + } + } + + /** + * Handles the actual execution of the whole transaction. + * + * @param mixed $callable Optional callback for execution. + * + * @throws CommunicationException + * @throws AbortedMultiExecException + * @throws ServerException + * + * @return array + */ + public function execute($callable = null) + { + $this->checkBeforeExecution($callable); + + $execResponse = null; + $attempts = $this->attempts; + + do { + if ($callable) { + $this->executeTransactionBlock($callable); + } + + if ($this->commands->isEmpty()) { + if ($this->state->isWatching()) { + $this->discard(); + } + + return; + } + + $execResponse = $this->call('EXEC'); + + if ($execResponse === null) { + if ($attempts === 0) { + throw new AbortedMultiExecException( + $this, 'The current transaction has been aborted by the server.' + ); + } + + $this->reset(); + + continue; + } + + break; + } while ($attempts-- > 0); + + $response = array(); + $commands = $this->commands; + $size = count($execResponse); + + if ($size !== count($commands)) { + $this->onProtocolError('EXEC returned an unexpected number of response items.'); + } + + for ($i = 0; $i < $size; ++$i) { + $cmdResponse = $execResponse[$i]; + + if ($cmdResponse instanceof ErrorResponseInterface && $this->exceptions) { + throw new ServerException($cmdResponse->getMessage()); + } + + $response[$i] = $commands->dequeue()->parseResponse($cmdResponse); + } + + return $response; + } + + /** + * Passes the current transaction object to a callable block for execution. + * + * @param mixed $callable Callback. + * + * @throws CommunicationException + * @throws ServerException + */ + protected function executeTransactionBlock($callable) + { + $exception = null; + $this->state->flag(MultiExecState::INSIDEBLOCK); + + try { + call_user_func($callable, $this); + } catch (CommunicationException $exception) { + // NOOP + } catch (ServerException $exception) { + // NOOP + } catch (\Exception $exception) { + $this->discard(); + } + + $this->state->unflag(MultiExecState::INSIDEBLOCK); + + if ($exception) { + throw $exception; + } + } + + /** + * Helper method for protocol errors encountered inside the transaction. + * + * @param string $message Error message. + */ + private function onProtocolError($message) + { + // Since a MULTI/EXEC block cannot be initialized when using aggregate + // connections we can safely assume that Predis\Client::getConnection() + // will return a Predis\Connection\NodeConnectionInterface instance. + CommunicationException::handle(new ProtocolException( + $this->client->getConnection(), $message + )); + } +} diff --git a/vendor/predis/predis/src/Transaction/MultiExecState.php b/vendor/predis/predis/src/Transaction/MultiExecState.php new file mode 100644 index 000000000..4bed42af6 --- /dev/null +++ b/vendor/predis/predis/src/Transaction/MultiExecState.php @@ -0,0 +1,166 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Transaction; + +/** + * Utility class used to track the state of a MULTI / EXEC transaction. + * + * @author Daniele Alessandri + */ +class MultiExecState +{ + const INITIALIZED = 1; // 0b00001 + const INSIDEBLOCK = 2; // 0b00010 + const DISCARDED = 4; // 0b00100 + const CAS = 8; // 0b01000 + const WATCH = 16; // 0b10000 + + private $flags; + + /** + * + */ + public function __construct() + { + $this->flags = 0; + } + + /** + * Sets the internal state flags. + * + * @param int $flags Set of flags + */ + public function set($flags) + { + $this->flags = $flags; + } + + /** + * Gets the internal state flags. + * + * @return int + */ + public function get() + { + return $this->flags; + } + + /** + * Sets one or more flags. + * + * @param int $flags Set of flags + */ + public function flag($flags) + { + $this->flags |= $flags; + } + + /** + * Resets one or more flags. + * + * @param int $flags Set of flags + */ + public function unflag($flags) + { + $this->flags &= ~$flags; + } + + /** + * Returns if the specified flag or set of flags is set. + * + * @param int $flags Flag + * + * @return bool + */ + public function check($flags) + { + return ($this->flags & $flags) === $flags; + } + + /** + * Resets the state of a transaction. + */ + public function reset() + { + $this->flags = 0; + } + + /** + * Returns the state of the RESET flag. + * + * @return bool + */ + public function isReset() + { + return $this->flags === 0; + } + + /** + * Returns the state of the INITIALIZED flag. + * + * @return bool + */ + public function isInitialized() + { + return $this->check(self::INITIALIZED); + } + + /** + * Returns the state of the INSIDEBLOCK flag. + * + * @return bool + */ + public function isExecuting() + { + return $this->check(self::INSIDEBLOCK); + } + + /** + * Returns the state of the CAS flag. + * + * @return bool + */ + public function isCAS() + { + return $this->check(self::CAS); + } + + /** + * Returns if WATCH is allowed in the current state. + * + * @return bool + */ + public function isWatchAllowed() + { + return $this->check(self::INITIALIZED) && !$this->check(self::CAS); + } + + /** + * Returns the state of the WATCH flag. + * + * @return bool + */ + public function isWatching() + { + return $this->check(self::WATCH); + } + + /** + * Returns the state of the DISCARDED flag. + * + * @return bool + */ + public function isDiscarded() + { + return $this->check(self::DISCARDED); + } +} diff --git a/vendor/predis/predis/tests/README.md b/vendor/predis/predis/tests/README.md new file mode 100644 index 000000000..b14c3b5a9 --- /dev/null +++ b/vendor/predis/predis/tests/README.md @@ -0,0 +1,82 @@ +# About testing Predis # + +__ATTENTION__: Do not ever ever run this test suite against instances of Redis running in production +environments or containing data you are interested in! If you still want to test this library on a +production server without hitting the database, please read ahead about how to disable integration +tests. + +Predis ships a comprehensive test suite that uses __PHPUnit__ to cover every aspect of the library. +The suite is organized into several unit groups with the PHPUnit `@group` annotation which makes it +possible to run only selected groups of tests. The main groups are: + + - __disconnected__: generic tests verifying the correct behaviour of the library without requiring + an active connection to Redis. + - __connected__: integration tests that require an active connection to Redis + - __commands__: tests for the implementation of Redis commands. + - __slow__: tests that might slow down the execution of the test suite (either __connected__ or + __disconnected__). + +A list of all the available groups in the suite can be obtained by running: + +```bash +$ phpunit --list-groups +``` + +Groups of tests can be disabled or enabled via the XML configuration file or the standard command +line test runner. Please note that due to a bug in PHPUnit, older versions ignore the `--group` +option when the group is excluded in the XML configuration file. More details about this issue are +available on [PHPUnit's bug tracker](http://github.com/sebastianbergmann/phpunit/issues/320). + +Certain groups of tests requiring native extensions, such as `ext-curl` or `ext-phpiredis`, are +disabled by default in the configuration file. To enable these groups of tests you should remove +them from the exclusion list in `phpunit.xml`. + +### Combining groups for inclusion or exclusion with the command-line runner ### + +```bash +$ phpunit --group disconnected --exclude-group commands,slow +``` + +### Integration tests ### + +The suite performs integration tests against a running instance of Redis (>= 2.4.0) to verify the +correct behavior of the implementation of each command and certain abstractions implemented in the +library that depend on them. These tests are identified by the __connected__ group. + +Integration tests for commands that are not defined in the specified server profile (see the value +of the `REDIS_SERVER_VERSION` constant in `phpunit.xml`) are marked as __skipped__ automatically. + +By default, the test suite is configured to execute integration tests using the server profile for +Redis 3.2. You can run the suite against a Redis instance built from the `unstable` branch with the +development profile by changing the `REDIS_SERVER_VERSION` to `dev` in the `phpunit.xml` file. + +If you do not have a Redis instance up and running or available for testing, you can completely +disable integration tests by excluding the __connected__ group: + +```bash +$ phpunit --exclude-group connected +``` + +### Slow tests ### + +Certain tests can slow down the execution of the suite. These tests can be disabled by excluding the +__slow__ group: + +```bash +$ phpunit --exclude-group slow +``` + +### Testing Redis commands ### + +We also provide an helper script in the `bin` directory that can be used to automatically generate a +file with the skeleton of a test case to test a Redis command by specifying the name of the class +in the `Predis\Command` namespace (only classes in this namespace are considered valid). For example + to generate a test case for `SET` (represented by the `Predis\Command\StringSet` class): + +```bash +$ ./bin/create-command-test --class=StringSet +``` + +Each command has its own realm (e.g. commands operating on strings, lists, sets and such) which is +automatically inferred from the name of the specified class. The realm can be also provided manually +leveraging the `--realm` option. diff --git a/vendor/predis/predis/tests/phpunit_mock_objects.patch b/vendor/predis/predis/tests/phpunit_mock_objects.patch new file mode 100644 index 000000000..b3dd0f29c --- /dev/null +++ b/vendor/predis/predis/tests/phpunit_mock_objects.patch @@ -0,0 +1,38 @@ +diff --git a/src/Framework/MockObject/Generator.php b/src/Framework/MockObject/Generator.php +index 3df3abb..fc76d5d 100644 +--- a/src/Framework/MockObject/Generator.php ++++ b/src/Framework/MockObject/Generator.php +@@ -1031,16 +1031,29 @@ protected function getMethodParameters(ReflectionMethod $method, $forCall = fals + $typeDeclaration = ''; + + if (!$forCall) { ++ if (PHP_VERSION_ID >= 80000) { ++ $isArray = $parameter->getType() && $parameter->getType()->getName() === 'array'; ++ $isCallable = $parameter->getType() && $parameter->getType()->getName() === 'callable'; ++ } else { ++ $isArray = $parameter->isArray(); ++ $isCallable = version_compare(PHP_VERSION, '5.4.0', '>=') ? $parameter->isCallable() : false; ++ } ++ + if ($this->hasType($parameter)) { +- $typeDeclaration = (string) $parameter->getType() . ' '; +- } elseif ($parameter->isArray()) { ++ $type = $parameter->getType(); ++ $typeDeclaration = (PHP_VERSION_ID >= 70100 ? $type->getName() : (string) $type) . ' '; ++ } elseif ($isArray) { + $typeDeclaration = 'array '; + } elseif ((defined('HHVM_VERSION') || version_compare(PHP_VERSION, '5.4.0', '>=')) +- && $parameter->isCallable()) { ++ && $isCallable) { + $typeDeclaration = 'callable '; + } else { + try { +- $class = $parameter->getClass(); ++ if (PHP_VERSION_ID >= 80000) { ++ $class = $parameter->getType(); ++ } else { ++ $class = $parameter->getClass(); ++ } + } catch (ReflectionException $e) { + throw new PHPUnit_Framework_MockObject_RuntimeException( + sprintf( diff --git a/vendor/predis/predis/tests/phpunit_php7.patch b/vendor/predis/predis/tests/phpunit_php7.patch new file mode 100644 index 000000000..e3f13ffb5 --- /dev/null +++ b/vendor/predis/predis/tests/phpunit_php7.patch @@ -0,0 +1,60 @@ +diff --git a/src/Util/Getopt.php b/src/Util/Getopt.php +index ba21be3..96931a3 100644 +--- a/src/Util/Getopt.php ++++ b/src/Util/Getopt.php +@@ -35,7 +35,15 @@ class PHPUnit_Util_Getopt + reset($args); + array_map('trim', $args); + +- while (list($i, $arg) = each($args)) { ++ while (true) { ++ $arg = current($args); ++ $i = key($args); ++ next($args); ++ ++ if ($arg === false) { ++ break; ++ } ++ + if ($arg == '') { + continue; + } +@@ -94,11 +102,14 @@ class PHPUnit_Util_Getopt + if ($i + 1 < $argLen) { + $opts[] = array($opt, substr($arg, $i + 1)); + break; +- } elseif (list(, $opt_arg) = each($args)) { + } else { +- throw new PHPUnit_Framework_Exception( +- "option requires an argument -- $opt" +- ); ++ $opt_arg = current($args); ++ next($args); ++ if ($opt_arg === false) { ++ throw new PHPUnit_Framework_Exception( ++ "option requires an argument -- $opt" ++ ); ++ } + } + } + } +@@ -139,11 +150,14 @@ class PHPUnit_Util_Getopt + + if (substr($long_opt, -1) == '=') { + if (substr($long_opt, -2) != '==') { +- if (!strlen($opt_arg) && +- !(list(, $opt_arg) = each($args))) { +- throw new PHPUnit_Framework_Exception( +- "option --$opt requires an argument" +- ); ++ if (!strlen($opt_arg)) { ++ $opt_arg = current($args); ++ next($args); ++ if ($opt_arg === false) { ++ throw new PHPUnit_Framework_Exception( ++ "option --$opt requires an argument" ++ ); ++ } + } + } + } elseif ($opt_arg) { diff --git a/vendor/predis/predis/tests/phpunit_php8.patch b/vendor/predis/predis/tests/phpunit_php8.patch new file mode 100644 index 000000000..ded4d3fc3 --- /dev/null +++ b/vendor/predis/predis/tests/phpunit_php8.patch @@ -0,0 +1,39 @@ +diff --git a/src/Framework/TestCase.php b/src/Framework/TestCase.php +index 638f42513..b4c7d3a5e 100644 +--- a/src/Framework/TestCase.php ++++ b/src/Framework/TestCase.php +@@ -905,7 +905,7 @@ protected function runTest() + try { + $testResult = $method->invokeArgs( + $this, +- array_merge($this->data, $this->dependencyInput) ++ array_values(array_merge($this->data, $this->dependencyInput)) + ); + } catch (Throwable $_e) { + $e = $_e; +diff --git a/src/Util/Configuration.php b/src/Util/Configuration.php +index 5c1041608..b2f7a7bd0 100644 +--- a/src/Util/Configuration.php ++++ b/src/Util/Configuration.php +@@ -162,7 +162,7 @@ protected function __construct($filename) + /** + * @since Method available since Release 3.4.0 + */ +- final private function __clone() ++ private function __clone() + { + } + +diff --git a/src/Util/PHP/Template/TestCaseMethod.tpl.dist b/src/Util/PHP/Template/TestCaseMethod.tpl.dist +index b48f354cd..d59cdeea7 100644 +--- a/src/Util/PHP/Template/TestCaseMethod.tpl.dist ++++ b/src/Util/PHP/Template/TestCaseMethod.tpl.dist +@@ -78,7 +78,7 @@ if ('' !== $configurationFilePath) { + unset($configuration); + } + +-function __phpunit_error_handler($errno, $errstr, $errfile, $errline, $errcontext) ++function __phpunit_error_handler($errno, $errstr, $errfile, $errline, $errcontext = null) + { + return true; + } diff --git a/vendor/spomky-labs/base64url/.github/FUNDING.yml b/vendor/spomky-labs/base64url/.github/FUNDING.yml new file mode 100644 index 000000000..726574c1f --- /dev/null +++ b/vendor/spomky-labs/base64url/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: Spomky +patreon: FlorentMorselli diff --git a/vendor/spomky-labs/base64url/LICENSE b/vendor/spomky-labs/base64url/LICENSE new file mode 100644 index 000000000..506ff4857 --- /dev/null +++ b/vendor/spomky-labs/base64url/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2014-2018 Spomky-Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/vendor/spomky-labs/base64url/composer.json b/vendor/spomky-labs/base64url/composer.json new file mode 100644 index 000000000..1f3941b9e --- /dev/null +++ b/vendor/spomky-labs/base64url/composer.json @@ -0,0 +1,35 @@ +{ + "name": "spomky-labs/base64url", + "description": "Base 64 URL Safe Encoding/Decoding PHP Library", + "type": "library", + "license": "MIT", + "keywords": ["Base64", "URL", "Safe", "RFC4648"], + "homepage": "https://github.com/Spomky-Labs/base64url", + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky-Labs/base64url/contributors" + } + ], + "autoload": { + "psr-4": { + "Base64Url\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Base64Url\\Test\\": "tests/" + } + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.11|^0.12", + "phpstan/phpstan-beberlei-assert": "^0.11|^0.12", + "phpstan/phpstan-deprecation-rules": "^0.11|^0.12", + "phpstan/phpstan-phpunit": "^0.11|^0.12", + "phpstan/phpstan-strict-rules": "^0.11|^0.12" + } +} diff --git a/vendor/spomky-labs/base64url/src/Base64Url.php b/vendor/spomky-labs/base64url/src/Base64Url.php new file mode 100644 index 000000000..260215ae4 --- /dev/null +++ b/vendor/spomky-labs/base64url/src/Base64Url.php @@ -0,0 +1,56 @@ +supportedAlgorithms = $supportedAlgorithms; + $this->protectedHeader = $protectedHeader; + } + + /** + * {@inheritdoc} + */ + public function checkHeader($value): void + { + if (!is_string($value)) { + throw new InvalidHeaderException('"alg" must be a string.', self::HEADER_NAME, $value); + } + if (!in_array($value, $this->supportedAlgorithms, true)) { + throw new InvalidHeaderException('Unsupported algorithm.', self::HEADER_NAME, $value); + } + } + + public function supportedHeader(): string + { + return self::HEADER_NAME; + } + + public function protectedHeaderOnly(): bool + { + return $this->protectedHeader; + } +} diff --git a/vendor/web-token/jwt-checker/AudienceChecker.php b/vendor/web-token/jwt-checker/AudienceChecker.php new file mode 100644 index 000000000..aec6ccfd2 --- /dev/null +++ b/vendor/web-token/jwt-checker/AudienceChecker.php @@ -0,0 +1,93 @@ +audience = $audience; + $this->protectedHeader = $protectedHeader; + } + + /** + * {@inheritdoc} + */ + public function checkClaim($value): void + { + $this->checkValue($value, InvalidClaimException::class); + } + + /** + * {@inheritdoc} + */ + public function checkHeader($value): void + { + $this->checkValue($value, InvalidHeaderException::class); + } + + public function supportedClaim(): string + { + return self::CLAIM_NAME; + } + + public function supportedHeader(): string + { + return self::CLAIM_NAME; + } + + public function protectedHeaderOnly(): bool + { + return $this->protectedHeader; + } + + /** + * @param mixed $value + * + * @throws InvalidClaimException if the claim is invalid + * @throws InvalidHeaderException if the header is invalid + */ + private function checkValue($value, string $class): void + { + if (is_string($value) && $value !== $this->audience) { + throw new $class('Bad audience.', self::CLAIM_NAME, $value); + } + if (is_array($value) && !in_array($this->audience, $value, true)) { + throw new $class('Bad audience.', self::CLAIM_NAME, $value); + } + if (!is_array($value) && !is_string($value)) { + throw new $class('Bad audience.', self::CLAIM_NAME, $value); + } + } +} diff --git a/vendor/web-token/jwt-checker/ClaimChecker.php b/vendor/web-token/jwt-checker/ClaimChecker.php new file mode 100644 index 000000000..5de777e07 --- /dev/null +++ b/vendor/web-token/jwt-checker/ClaimChecker.php @@ -0,0 +1,32 @@ +add($checker); + } + } + + /** + * This method returns all checkers handled by this manager. + * + * @return ClaimChecker[] + */ + public function getCheckers(): array + { + return $this->checkers; + } + + /** + * This method checks all the claims passed as argument. + * All claims are checked against the claim checkers. + * If one fails, the InvalidClaimException is thrown. + * + * This method returns an array with all checked claims. + * It is up to the implementor to decide use the claims that have not been checked. + * + * @param string[] $mandatoryClaims + * + * @throws InvalidClaimException + * @throws MissingMandatoryClaimException + */ + public function check(array $claims, array $mandatoryClaims = []): array + { + $this->checkMandatoryClaims($mandatoryClaims, $claims); + $checkedClaims = []; + foreach ($this->checkers as $claim => $checker) { + if (array_key_exists($claim, $claims)) { + $checker->checkClaim($claims[$claim]); + $checkedClaims[$claim] = $claims[$claim]; + } + } + + return $checkedClaims; + } + + private function add(ClaimChecker $checker): void + { + $claim = $checker->supportedClaim(); + $this->checkers[$claim] = $checker; + } + + /** + * @param string[] $mandatoryClaims + * + * @throws MissingMandatoryClaimException + */ + private function checkMandatoryClaims(array $mandatoryClaims, array $claims): void + { + if (0 === count($mandatoryClaims)) { + return; + } + $diff = array_keys(array_diff_key(array_flip($mandatoryClaims), $claims)); + if (0 !== count($diff)) { + throw new MissingMandatoryClaimException(sprintf('The following claims are mandatory: %s.', implode(', ', $diff)), $diff); + } + } +} diff --git a/vendor/web-token/jwt-checker/ClaimCheckerManagerFactory.php b/vendor/web-token/jwt-checker/ClaimCheckerManagerFactory.php new file mode 100644 index 000000000..192b34245 --- /dev/null +++ b/vendor/web-token/jwt-checker/ClaimCheckerManagerFactory.php @@ -0,0 +1,71 @@ +checkers[$alias])) { + throw new InvalidArgumentException(sprintf('The claim checker with the alias "%s" is not supported.', $alias)); + } + $checkers[] = $this->checkers[$alias]; + } + + return new ClaimCheckerManager($checkers); + } + + /** + * This method adds a claim checker to this factory. + */ + public function add(string $alias, ClaimChecker $checker): void + { + $this->checkers[$alias] = $checker; + } + + /** + * Returns all claim checker aliases supported by this factory. + * + * @return string[] + */ + public function aliases(): array + { + return array_keys($this->checkers); + } + + /** + * Returns all claim checkers supported by this factory. + * + * @return ClaimChecker[] + */ + public function all(): array + { + return $this->checkers; + } +} diff --git a/vendor/web-token/jwt-checker/ClaimExceptionInterface.php b/vendor/web-token/jwt-checker/ClaimExceptionInterface.php new file mode 100644 index 000000000..5ffb5ee48 --- /dev/null +++ b/vendor/web-token/jwt-checker/ClaimExceptionInterface.php @@ -0,0 +1,23 @@ +allowedTimeDrift = $allowedTimeDrift; + $this->protectedHeaderOnly = $protectedHeaderOnly; + } + + /** + * {@inheritdoc} + * + * @throws InvalidClaimException if the claim "exp" is not valid + */ + public function checkClaim($value): void + { + if (!is_float($value) && !is_int($value)) { + throw new InvalidClaimException('"exp" must be an integer.', self::NAME, $value); + } + if (time() > $value + $this->allowedTimeDrift) { + throw new InvalidClaimException('The token expired.', self::NAME, $value); + } + } + + public function supportedClaim(): string + { + return self::NAME; + } + + /** + * @param mixed $value + * + * @throws InvalidHeaderException if the claim "exp" is not valid + */ + public function checkHeader($value): void + { + if (!is_float($value) && !is_int($value)) { + throw new InvalidHeaderException('"exp" must be an integer.', self::NAME, $value); + } + if (time() > $value + $this->allowedTimeDrift) { + throw new InvalidHeaderException('The token expired.', self::NAME, $value); + } + } + + public function supportedHeader(): string + { + return self::NAME; + } + + public function protectedHeaderOnly(): bool + { + return $this->protectedHeaderOnly; + } +} diff --git a/vendor/web-token/jwt-checker/HeaderChecker.php b/vendor/web-token/jwt-checker/HeaderChecker.php new file mode 100644 index 000000000..03c1c6978 --- /dev/null +++ b/vendor/web-token/jwt-checker/HeaderChecker.php @@ -0,0 +1,37 @@ +add($checker); + } + foreach ($tokenTypes as $tokenType) { + $this->addTokenTypeSupport($tokenType); + } + } + + /** + * This method returns all checkers handled by this manager. + * + * @return HeaderChecker[] + */ + public function getCheckers(): array + { + return $this->checkers; + } + + /** + * This method checks all the header parameters passed as argument. + * All header parameters are checked against the header parameter checkers. + * If one fails, the InvalidHeaderException is thrown. + * + * @param string[] $mandatoryHeaderParameters + * + * @throws InvalidArgumentException if the token format is not valid + */ + public function check(JWT $jwt, int $index, array $mandatoryHeaderParameters = []): void + { + foreach ($this->tokenTypes as $tokenType) { + if ($tokenType->supports($jwt)) { + $protected = []; + $unprotected = []; + $tokenType->retrieveTokenHeaders($jwt, $index, $protected, $unprotected); + $this->checkDuplicatedHeaderParameters($protected, $unprotected); + $this->checkMandatoryHeaderParameters($mandatoryHeaderParameters, $protected, $unprotected); + $this->checkHeaders($protected, $unprotected); + + return; + } + } + + throw new InvalidArgumentException('Unsupported token type.'); + } + + private function addTokenTypeSupport(TokenTypeSupport $tokenType): void + { + $this->tokenTypes[] = $tokenType; + } + + private function add(HeaderChecker $checker): void + { + $header = $checker->supportedHeader(); + $this->checkers[$header] = $checker; + } + + /** + * @throws InvalidArgumentException if the header contains duplicated entries + */ + private function checkDuplicatedHeaderParameters(array $header1, array $header2): void + { + $inter = array_intersect_key($header1, $header2); + if (0 !== count($inter)) { + throw new InvalidArgumentException(sprintf('The header contains duplicated entries: %s.', implode(', ', array_keys($inter)))); + } + } + + /** + * @param string[] $mandatoryHeaderParameters + * + * @throws MissingMandatoryHeaderParameterException if a mandatory header parameter is missing + */ + private function checkMandatoryHeaderParameters(array $mandatoryHeaderParameters, array $protected, array $unprotected): void + { + if (0 === count($mandatoryHeaderParameters)) { + return; + } + $diff = array_keys(array_diff_key(array_flip($mandatoryHeaderParameters), array_merge($protected, $unprotected))); + if (0 !== count($diff)) { + throw new MissingMandatoryHeaderParameterException(sprintf('The following header parameters are mandatory: %s.', implode(', ', $diff)), $diff); + } + } + + /** + * @throws InvalidHeaderException if a protected header parameter is not in the protected header + */ + private function checkHeaders(array $protected, array $header): void + { + $checkedHeaderParameters = []; + foreach ($this->checkers as $headerParameter => $checker) { + if ($checker->protectedHeaderOnly()) { + if (array_key_exists($headerParameter, $protected)) { + $checker->checkHeader($protected[$headerParameter]); + $checkedHeaderParameters[] = $headerParameter; + } elseif (array_key_exists($headerParameter, $header)) { + throw new InvalidHeaderException(sprintf('The header parameter "%s" must be protected.', $headerParameter), $headerParameter, $header[$headerParameter]); + } + } else { + if (array_key_exists($headerParameter, $protected)) { + $checker->checkHeader($protected[$headerParameter]); + $checkedHeaderParameters[] = $headerParameter; + } elseif (array_key_exists($headerParameter, $header)) { + $checker->checkHeader($header[$headerParameter]); + $checkedHeaderParameters[] = $headerParameter; + } + } + } + $this->checkCriticalHeader($protected, $header, $checkedHeaderParameters); + } + + /** + * @throws InvalidHeaderException if the "crit" parameter is not valid or if a critical header parameter cannot be verified + */ + private function checkCriticalHeader(array $protected, array $header, array $checkedHeaderParameters): void + { + if (array_key_exists('crit', $protected)) { + if (!is_array($protected['crit'])) { + throw new InvalidHeaderException('The header "crit" must be a list of header parameters.', 'crit', $protected['crit']); + } + $diff = array_diff($protected['crit'], $checkedHeaderParameters); + if (0 !== count($diff)) { + throw new InvalidHeaderException(sprintf('One or more header parameters are marked as critical, but they are missing or have not been checked: %s.', implode(', ', array_values($diff))), 'crit', $protected['crit']); + } + } elseif (array_key_exists('crit', $header)) { + throw new InvalidHeaderException('The header parameter "crit" must be protected.', 'crit', $header['crit']); + } + } +} diff --git a/vendor/web-token/jwt-checker/HeaderCheckerManagerFactory.php b/vendor/web-token/jwt-checker/HeaderCheckerManagerFactory.php new file mode 100644 index 000000000..860580b84 --- /dev/null +++ b/vendor/web-token/jwt-checker/HeaderCheckerManagerFactory.php @@ -0,0 +1,88 @@ +checkers[$alias])) { + throw new InvalidArgumentException(sprintf('The header checker with the alias "%s" is not supported.', $alias)); + } + $checkers[] = $this->checkers[$alias]; + } + + return new HeaderCheckerManager($checkers, $this->tokenTypes); + } + + /** + * This method adds a header parameter checker to this factory. + * The checker is uniquely identified by an alias. This allows the same header parameter checker to be added twice (or more) + * using several configuration options. + */ + public function add(string $alias, HeaderChecker $checker): void + { + $this->checkers[$alias] = $checker; + } + + /** + * This method adds a token type support to this factory. + */ + public function addTokenTypeSupport(TokenTypeSupport $tokenType): void + { + $this->tokenTypes[] = $tokenType; + } + + /** + * Returns all header parameter checker aliases supported by this factory. + * + * @return string[] + */ + public function aliases(): array + { + return array_keys($this->checkers); + } + + /** + * Returns all header parameter checkers supported by this factory. + * + * @return HeaderChecker[] + */ + public function all(): array + { + return $this->checkers; + } +} diff --git a/vendor/web-token/jwt-checker/InvalidClaimException.php b/vendor/web-token/jwt-checker/InvalidClaimException.php new file mode 100644 index 000000000..b834843fc --- /dev/null +++ b/vendor/web-token/jwt-checker/InvalidClaimException.php @@ -0,0 +1,61 @@ +claim = $claim; + $this->value = $value; + } + + /** + * Returns the claim that caused the exception. + */ + public function getClaim(): string + { + return $this->claim; + } + + /** + * Returns the claim value that caused the exception. + * + * @return mixed + */ + public function getValue() + { + return $this->value; + } +} diff --git a/vendor/web-token/jwt-checker/InvalidHeaderException.php b/vendor/web-token/jwt-checker/InvalidHeaderException.php new file mode 100644 index 000000000..50d1e9d3f --- /dev/null +++ b/vendor/web-token/jwt-checker/InvalidHeaderException.php @@ -0,0 +1,61 @@ +header = $header; + $this->value = $value; + } + + /** + * Returns the header parameter that caused the exception. + */ + public function getHeader(): string + { + return $this->header; + } + + /** + * Returns the header parameter value that caused the exception. + * + * @return mixed + */ + public function getValue() + { + return $this->value; + } +} diff --git a/vendor/web-token/jwt-checker/IssuedAtChecker.php b/vendor/web-token/jwt-checker/IssuedAtChecker.php new file mode 100644 index 000000000..d005957b4 --- /dev/null +++ b/vendor/web-token/jwt-checker/IssuedAtChecker.php @@ -0,0 +1,86 @@ +allowedTimeDrift = $allowedTimeDrift; + $this->protectedHeaderOnly = $protectedHeaderOnly; + } + + /** + * {@inheritdoc} + * + * @throws InvalidClaimException if the claim is invalid + */ + public function checkClaim($value): void + { + if (!is_float($value) && !is_int($value)) { + throw new InvalidClaimException('"iat" must be an integer.', self::NAME, $value); + } + if (time() < $value - $this->allowedTimeDrift) { + throw new InvalidClaimException('The JWT is issued in the future.', self::NAME, $value); + } + } + + public function supportedClaim(): string + { + return self::NAME; + } + + /** + * @param mixed $value + * + * @throws InvalidHeaderException if the header parameter is invalid + */ + public function checkHeader($value): void + { + if (!is_float($value) && !is_int($value)) { + throw new InvalidHeaderException('The header "iat" must be an integer.', self::NAME, $value); + } + if (time() < $value - $this->allowedTimeDrift) { + throw new InvalidHeaderException('The JWT is issued in the future.', self::NAME, $value); + } + } + + public function supportedHeader(): string + { + return self::NAME; + } + + public function protectedHeaderOnly(): bool + { + return $this->protectedHeaderOnly; + } +} diff --git a/vendor/web-token/jwt-checker/IssuerChecker.php b/vendor/web-token/jwt-checker/IssuerChecker.php new file mode 100644 index 000000000..614767701 --- /dev/null +++ b/vendor/web-token/jwt-checker/IssuerChecker.php @@ -0,0 +1,93 @@ +issuers = $issuer; + $this->protectedHeader = $protectedHeader; + } + + /** + * @param mixed $value + * + * @throws InvalidClaimException if the claim is invalid + */ + public function checkClaim($value): void + { + $this->checkValue($value, InvalidClaimException::class); + } + + /** + * @param mixed $value + * + * @throws InvalidHeaderException if the header parameter is invalid + */ + public function checkHeader($value): void + { + $this->checkValue($value, InvalidHeaderException::class); + } + + public function supportedClaim(): string + { + return self::CLAIM_NAME; + } + + public function supportedHeader(): string + { + return self::CLAIM_NAME; + } + + public function protectedHeaderOnly(): bool + { + return $this->protectedHeader; + } + + /** + * @param mixed $value + * + * @throws InvalidClaimException if the claim is invalid + * @throws InvalidHeaderException if the header parameter is invalid + */ + private function checkValue($value, string $class): void + { + if (!is_string($value)) { + throw new $class('Invalid value.', self::CLAIM_NAME, $value); + } + if (!in_array($value, $this->issuers, true)) { + throw new $class('Unknown issuer.', self::CLAIM_NAME, $value); + } + } +} diff --git a/vendor/web-token/jwt-checker/LICENSE b/vendor/web-token/jwt-checker/LICENSE new file mode 100644 index 000000000..37cf976b1 --- /dev/null +++ b/vendor/web-token/jwt-checker/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014-2019 Spomky-Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/web-token/jwt-checker/MissingMandatoryClaimException.php b/vendor/web-token/jwt-checker/MissingMandatoryClaimException.php new file mode 100644 index 000000000..275f92980 --- /dev/null +++ b/vendor/web-token/jwt-checker/MissingMandatoryClaimException.php @@ -0,0 +1,44 @@ +claims = $claims; + } + + /** + * @return string[] + */ + public function getClaims(): array + { + return $this->claims; + } +} diff --git a/vendor/web-token/jwt-checker/MissingMandatoryHeaderParameterException.php b/vendor/web-token/jwt-checker/MissingMandatoryHeaderParameterException.php new file mode 100644 index 000000000..97479b26f --- /dev/null +++ b/vendor/web-token/jwt-checker/MissingMandatoryHeaderParameterException.php @@ -0,0 +1,44 @@ +parameters = $parameters; + } + + /** + * @return string[] + */ + public function getParameters(): array + { + return $this->parameters; + } +} diff --git a/vendor/web-token/jwt-checker/NotBeforeChecker.php b/vendor/web-token/jwt-checker/NotBeforeChecker.php new file mode 100644 index 000000000..9a9e3b00d --- /dev/null +++ b/vendor/web-token/jwt-checker/NotBeforeChecker.php @@ -0,0 +1,88 @@ +allowedTimeDrift = $allowedTimeDrift; + $this->protectedHeaderOnly = $protectedHeaderOnly; + } + + /** + * {@inheritdoc} + * + * @throws InvalidClaimException if the claim "nbf" is not an integer + * @throws InvalidClaimException if the claim "nbf" restrict the use of the token + */ + public function checkClaim($value): void + { + if (!is_float($value) && !is_int($value)) { + throw new InvalidClaimException('"nbf" must be an integer.', self::NAME, $value); + } + if (time() < $value - $this->allowedTimeDrift) { + throw new InvalidClaimException('The JWT can not be used yet.', self::NAME, $value); + } + } + + public function supportedClaim(): string + { + return self::NAME; + } + + /** + * @param mixed $value + * + * @throws InvalidHeaderException if the claim "nbf" is not an integer + * @throws InvalidHeaderException if the claim "nbf" restrict the use of the token + */ + public function checkHeader($value): void + { + if (!is_float($value) && !is_int($value)) { + throw new InvalidHeaderException('"nbf" must be an integer.', self::NAME, $value); + } + if (time() < $value - $this->allowedTimeDrift) { + throw new InvalidHeaderException('The JWT can not be used yet.', self::NAME, $value); + } + } + + public function supportedHeader(): string + { + return self::NAME; + } + + public function protectedHeaderOnly(): bool + { + return $this->protectedHeaderOnly; + } +} diff --git a/vendor/web-token/jwt-checker/README.md b/vendor/web-token/jwt-checker/README.md new file mode 100644 index 000000000..7d0a0158d --- /dev/null +++ b/vendor/web-token/jwt-checker/README.md @@ -0,0 +1,15 @@ +PHP JWT Checker Component +========================= + +This repository is a sub repository of [the JWT Framework](https://github.com/web-token/jwt-framework) project and is READ ONLY. + +**Please do not submit any Pull Request here.** +You should go to [the main repository](https://github.com/web-token/jwt-framework) instead. + +# Documentation + +The official documentation is available as https://web-token.spomky-labs.com/ + +# Licence + +This software is release under [MIT licence](LICENSE). diff --git a/vendor/web-token/jwt-checker/Tests/AlgorithmHeaderCheckerTest.php b/vendor/web-token/jwt-checker/Tests/AlgorithmHeaderCheckerTest.php new file mode 100644 index 000000000..73eb0914c --- /dev/null +++ b/vendor/web-token/jwt-checker/Tests/AlgorithmHeaderCheckerTest.php @@ -0,0 +1,62 @@ +expectException(InvalidHeaderException::class); + $this->expectExceptionMessage('"alg" must be a string.'); + + $checker = new AlgorithmChecker(['foo']); + $checker->checkHeader(1); + } + + /** + * @test + */ + public function theAlgorithmHeaderIsNotAllowed(): void + { + $this->expectException(InvalidHeaderException::class); + $this->expectExceptionMessage('Unsupported algorithm.'); + + $checker = new AlgorithmChecker(['foo']); + $checker->checkHeader('bar'); + } + + /** + * @test + */ + public function theAlgorithmHeaderIsSupported(): void + { + $checker = new AlgorithmChecker(['foo']); + $checker->checkHeader('foo'); + static::assertFalse($checker->protectedHeaderOnly()); + static::assertEquals('alg', $checker->supportedHeader()); + } +} diff --git a/vendor/web-token/jwt-checker/Tests/AudienceClaimCheckerTest.php b/vendor/web-token/jwt-checker/Tests/AudienceClaimCheckerTest.php new file mode 100644 index 000000000..9c1fa937d --- /dev/null +++ b/vendor/web-token/jwt-checker/Tests/AudienceClaimCheckerTest.php @@ -0,0 +1,74 @@ +expectException(InvalidClaimException::class); + $this->expectExceptionMessage('Bad audience.'); + + $checker = new AudienceChecker('foo'); + $checker->checkClaim(1); + } + + /** + * @test + */ + public function theAudienceClaimIsNotKnown(): void + { + $this->expectException(InvalidClaimException::class); + $this->expectExceptionMessage('Bad audience.'); + + $checker = new AudienceChecker('foo'); + $checker->checkClaim('bar'); + } + + /** + * @test + */ + public function theAudienceClaimListDoesNotContainTheCurrentAudience(): void + { + $this->expectException(InvalidClaimException::class); + $this->expectExceptionMessage('Bad audience.'); + + $checker = new AudienceChecker('foo'); + $checker->checkClaim(['bar']); + } + + /** + * @test + */ + public function theAudienceClaimIsSupported(): void + { + $checker = new AudienceChecker('foo'); + $checker->checkClaim('foo'); + $checker->checkClaim(['foo']); + static::assertEquals('aud', $checker->supportedClaim()); + } +} diff --git a/vendor/web-token/jwt-checker/Tests/AudienceHeaderCheckerTest.php b/vendor/web-token/jwt-checker/Tests/AudienceHeaderCheckerTest.php new file mode 100644 index 000000000..5727eea1e --- /dev/null +++ b/vendor/web-token/jwt-checker/Tests/AudienceHeaderCheckerTest.php @@ -0,0 +1,75 @@ +expectException(InvalidHeaderException::class); + $this->expectExceptionMessage('Bad audience.'); + + $checker = new AudienceChecker('foo'); + $checker->checkHeader(1); + } + + /** + * @test + */ + public function theAudienceHeaderIsNotKnown(): void + { + $this->expectException(InvalidHeaderException::class); + $this->expectExceptionMessage('Bad audience.'); + + $checker = new AudienceChecker('foo'); + $checker->checkHeader('bar'); + } + + /** + * @test + */ + public function theAudienceHeaderListDoesNotContainTheCurrentAudience(): void + { + $this->expectException(InvalidHeaderException::class); + $this->expectExceptionMessage('Bad audience.'); + + $checker = new AudienceChecker('foo'); + $checker->checkHeader(['bar']); + } + + /** + * @test + */ + public function theAudienceHeaderIsSupported(): void + { + $checker = new AudienceChecker('foo'); + $checker->checkHeader('foo'); + $checker->checkHeader(['foo']); + static::assertFalse($checker->protectedHeaderOnly()); + static::assertEquals('aud', $checker->supportedHeader()); + } +} diff --git a/vendor/web-token/jwt-checker/Tests/ClaimCheckerManagerFactoryTest.php b/vendor/web-token/jwt-checker/Tests/ClaimCheckerManagerFactoryTest.php new file mode 100644 index 000000000..0bb3a674f --- /dev/null +++ b/vendor/web-token/jwt-checker/Tests/ClaimCheckerManagerFactoryTest.php @@ -0,0 +1,115 @@ +getClaimCheckerManagerFactory()->aliases()); + } + + /** + * @test + */ + public function theAliasDoesNotExist(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The claim checker with the alias "foo" is not supported.'); + + $this->getClaimCheckerManagerFactory()->create(['foo']); + } + + /** + * @test + */ + public function iCanCreateAClaimCheckerManager(): void + { + $manager = $this->getClaimCheckerManagerFactory()->create(['exp', 'iat', 'nbf', 'aud']); + static::assertCount(4, $manager->getCheckers()); + } + + /** + * @test + */ + public function iCanCheckValidPayloadClaims(): void + { + $payload = [ + 'exp' => time() + 3600, + 'iat' => time() - 1000, + 'nbf' => time() - 100, + 'foo' => 'bar', + ]; + $expected = $payload; + unset($expected['foo']); + $manager = $this->getClaimCheckerManagerFactory()->create(['exp', 'iat', 'nbf', 'aud']); + $result = $manager->check($payload); + static::assertEquals($expected, $result); + } + + /** + * @test + */ + public function theMandatoryClaimsAreNotSet(): void + { + $this->expectException(\Jose\Component\Checker\MissingMandatoryClaimException::class); + $this->expectExceptionMessage('The following claims are mandatory: bar.'); + + $payload = [ + 'exp' => time() + 3600, + 'iat' => time() - 1000, + 'nbf' => time() - 100, + 'foo' => 'bar', + ]; + $expected = $payload; + unset($expected['foo']); + $manager = $this->getClaimCheckerManagerFactory()->create(['exp', 'iat', 'nbf', 'aud']); + $manager->check($payload, ['exp', 'foo', 'bar']); + } + + private function getClaimCheckerManagerFactory(): ClaimCheckerManagerFactory + { + if (null === $this->claimCheckerManagerFactory) { + $this->claimCheckerManagerFactory = new ClaimCheckerManagerFactory(); + $this->claimCheckerManagerFactory->add('exp', new ExpirationTimeChecker()); + $this->claimCheckerManagerFactory->add('iat', new IssuedAtChecker()); + $this->claimCheckerManagerFactory->add('nbf', new NotBeforeChecker()); + $this->claimCheckerManagerFactory->add('aud', new AudienceChecker('My Service')); + } + + return $this->claimCheckerManagerFactory; + } +} diff --git a/vendor/web-token/jwt-checker/Tests/ClaimCheckerManagerTest.php b/vendor/web-token/jwt-checker/Tests/ClaimCheckerManagerTest.php new file mode 100644 index 000000000..8e35f9c7a --- /dev/null +++ b/vendor/web-token/jwt-checker/Tests/ClaimCheckerManagerTest.php @@ -0,0 +1,50 @@ + $checkers[0], + ]; + + $sut = new ClaimCheckerManager($checkers); + + static::assertEquals( + $expectedCheckers, + $sut->getCheckers() + ); + } +} diff --git a/vendor/web-token/jwt-checker/Tests/ExpirationTimeClaimCheckerTest.php b/vendor/web-token/jwt-checker/Tests/ExpirationTimeClaimCheckerTest.php new file mode 100644 index 000000000..ad692c405 --- /dev/null +++ b/vendor/web-token/jwt-checker/Tests/ExpirationTimeClaimCheckerTest.php @@ -0,0 +1,61 @@ +expectException(InvalidClaimException::class); + $this->expectExceptionMessage('"exp" must be an integer.'); + + $checker = new ExpirationTimeChecker(); + $checker->checkClaim('foo'); + } + + /** + * @test + */ + public function theExpirationTimeIsInThePast(): void + { + $this->expectException(InvalidClaimException::class); + $this->expectExceptionMessage('The token expired.'); + + $checker = new ExpirationTimeChecker(); + $checker->checkClaim(time() - 1); + } + + /** + * @test + */ + public function theExpirationTimeIsInTheFutur(): void + { + $checker = new ExpirationTimeChecker(); + $checker->checkClaim(time() + 3600); + static::assertEquals('exp', $checker->supportedClaim()); + } +} diff --git a/vendor/web-token/jwt-checker/Tests/HeaderCheckerManagerFactoryTest.php b/vendor/web-token/jwt-checker/Tests/HeaderCheckerManagerFactoryTest.php new file mode 100644 index 000000000..ac3812051 --- /dev/null +++ b/vendor/web-token/jwt-checker/Tests/HeaderCheckerManagerFactoryTest.php @@ -0,0 +1,183 @@ +getHeaderCheckerManagerFactory()->aliases()); + } + + /** + * @test + */ + public function aHeaderMustNotContainDuplicatedHeaderParameters(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The header contains duplicated entries: alg.'); + + $headerCheckerManager = $this->getHeaderCheckerManagerFactory()->create(['aud', 'iss']); + $payload = []; + $protected = ['alg' => 'foo']; + $unprotected = ['alg' => 'foo']; + $token = new Token(json_encode($payload), $protected, $unprotected); + + $headerCheckerManager->check($token, 0); + } + + /** + * @test + */ + public function theTokenHasCriticalHeaderNotSatisfied(): void + { + $this->expectException(InvalidHeaderException::class); + $this->expectExceptionMessage('One or more header parameters are marked as critical, but they are missing or have not been checked: alg.'); + + $headerCheckerManager = $this->getHeaderCheckerManagerFactory()->create(['aud', 'iss']); + $payload = []; + $protected = ['crit' => ['alg']]; + $unprotected = []; + $token = new Token(json_encode($payload), $protected, $unprotected); + + $headerCheckerManager->check($token, 0); + } + + /** + * @test + */ + public function theHeaderIsSuccessfullyChecked(): void + { + $headerCheckerManager = $this->getHeaderCheckerManagerFactory()->create(['aud', 'iss']); + $payload = []; + $protected = ['crit' => ['aud'], 'aud' => 'My Service']; + $unprotected = ['iss' => 'Another Service']; + $token = new Token(json_encode($payload), $protected, $unprotected); + $headerCheckerManager->check($token, 0); + } + + /** + * @test + */ + public function theCriticalHeaderParameterMustBeProtected(): void + { + $this->expectException(InvalidHeaderException::class); + $this->expectExceptionMessage('The header parameter "crit" must be protected.'); + + $headerCheckerManager = $this->getHeaderCheckerManagerFactory()->create(['aud', 'iss']); + $payload = []; + $protected = ['aud' => 'My Service']; + $unprotected = ['crit' => ['aud']]; + $token = new Token(json_encode($payload), $protected, $unprotected); + $headerCheckerManager->check($token, 0); + } + + /** + * @test + */ + public function theCriticalHeaderParameterMustBeAListOfHeaderParameters(): void + { + $this->expectException(InvalidHeaderException::class); + $this->expectExceptionMessage('The header "crit" must be a list of header parameters.'); + + $headerCheckerManager = $this->getHeaderCheckerManagerFactory()->create(['aud', 'iss']); + $payload = []; + $protected = ['aud' => 'My Service', 'crit' => true]; + $unprotected = []; + $token = new Token(json_encode($payload), $protected, $unprotected); + $headerCheckerManager->check($token, 0); + } + + /** + * @test + */ + public function theHeaderContainsUnknownParametersAndIsSuccessfullyChecked(): void + { + $headerCheckerManager = $this->getHeaderCheckerManagerFactory()->create(['aud', 'iss']); + $payload = []; + $protected = ['foo' => 'bar', 'iss' => 'Another Service']; + $unprotected = []; + $token = new Token(json_encode($payload), $protected, $unprotected); + $headerCheckerManager->check($token, 0); + } + + /** + * @test + */ + public function theHeaderDoesNotContainSomeMandatoryParameters(): void + { + $this->expectException(MissingMandatoryHeaderParameterException::class); + $this->expectExceptionMessage('The following header parameters are mandatory: mandatory.'); + + $headerCheckerManager = $this->getHeaderCheckerManagerFactory()->create(['aud', 'iss']); + $payload = []; + $protected = ['aud' => 'Audience', 'iss' => 'Another Service']; + $unprotected = ['foo' => 'bar']; + $token = new Token(json_encode($payload), $protected, $unprotected); + $headerCheckerManager->check($token, 0, ['aud', 'iss', 'mandatory']); + } + + /** + * @test + */ + public function iTryToCheckATokenThatIsNotSupported(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Unsupported token type.'); + + $headerCheckerManager = $this->getHeaderCheckerManagerFactory()->create(['aud', 'iss']); + $payload = []; + $protected = ['foo' => 'bar']; + $unprotected = []; + $token = new OtherToken(json_encode($payload), $protected, $unprotected); + $headerCheckerManager->check($token, 0); + } + + private function getHeaderCheckerManagerFactory(): HeaderCheckerManagerFactory + { + if (null === $this->headerCheckerManagerFactory) { + $this->headerCheckerManagerFactory = new HeaderCheckerManagerFactory(); + $this->headerCheckerManagerFactory->add('aud', new AudienceChecker('My Service', true)); + $this->headerCheckerManagerFactory->add('iss', new IssuerChecker(['Another Service'])); + $this->headerCheckerManagerFactory->addTokenTypeSupport(new TokenSupport()); + } + + return $this->headerCheckerManagerFactory; + } +} diff --git a/vendor/web-token/jwt-checker/Tests/IssuedAtClaimCheckerTest.php b/vendor/web-token/jwt-checker/Tests/IssuedAtClaimCheckerTest.php new file mode 100644 index 000000000..9900e1d70 --- /dev/null +++ b/vendor/web-token/jwt-checker/Tests/IssuedAtClaimCheckerTest.php @@ -0,0 +1,61 @@ +expectException(InvalidClaimException::class); + $this->expectExceptionMessage('"iat" must be an integer.'); + + $checker = new IssuedAtChecker(); + $checker->checkClaim('foo'); + } + + /** + * @test + */ + public function theIssuedAtClaimIsInTheFutur(): void + { + $this->expectException(InvalidClaimException::class); + $this->expectExceptionMessage('The JWT is issued in the future.'); + + $checker = new IssuedAtChecker(); + $checker->checkClaim(time() + 3600); + } + + /** + * @test + */ + public function theIssuedAtClaimIsInThePast(): void + { + $checker = new IssuedAtChecker(); + $checker->checkClaim(time() - 3600); + static::assertEquals('iat', $checker->supportedClaim()); + } +} diff --git a/vendor/web-token/jwt-checker/Tests/NotBeforeClaimCheckerTest.php b/vendor/web-token/jwt-checker/Tests/NotBeforeClaimCheckerTest.php new file mode 100644 index 000000000..d2dbe704b --- /dev/null +++ b/vendor/web-token/jwt-checker/Tests/NotBeforeClaimCheckerTest.php @@ -0,0 +1,60 @@ +expectException(\Jose\Component\Checker\InvalidClaimException::class); + $this->expectExceptionMessage('"nbf" must be an integer.'); + + $checker = new NotBeforeChecker(); + $checker->checkClaim('foo'); + } + + /** + * @test + */ + public function theNotBeforeClaimIsInTheFutur(): void + { + $this->expectException(\Jose\Component\Checker\InvalidClaimException::class); + $this->expectExceptionMessage('The JWT can not be used yet.'); + + $checker = new NotBeforeChecker(); + $checker->checkClaim(time() + 3600); + } + + /** + * @test + */ + public function theNotBeforeClaimIsInThePast(): void + { + $checker = new NotBeforeChecker(); + $checker->checkClaim(time() - 3600); + static::assertEquals('nbf', $checker->supportedClaim()); + } +} diff --git a/vendor/web-token/jwt-checker/Tests/Stub/OtherToken.php b/vendor/web-token/jwt-checker/Tests/Stub/OtherToken.php new file mode 100644 index 000000000..4394860f9 --- /dev/null +++ b/vendor/web-token/jwt-checker/Tests/Stub/OtherToken.php @@ -0,0 +1,56 @@ +payload = $payload; + $this->protectedHeader = $protectedHeader; + $this->unprotectedHeader = $unprotectedHeader; + } + + public function getPayload(): ?string + { + return $this->payload; + } + + public function getProtectedHeader(): array + { + return $this->protectedHeader; + } + + public function getUnprotectedHeader(): array + { + return $this->unprotectedHeader; + } +} diff --git a/vendor/web-token/jwt-checker/Tests/Stub/Token.php b/vendor/web-token/jwt-checker/Tests/Stub/Token.php new file mode 100644 index 000000000..56021af0a --- /dev/null +++ b/vendor/web-token/jwt-checker/Tests/Stub/Token.php @@ -0,0 +1,56 @@ +payload = $payload; + $this->protectedHeader = $protectedHeader; + $this->unprotectedHeader = $unprotectedHeader; + } + + public function getPayload(): ?string + { + return $this->payload; + } + + public function getProtectedHeader(): array + { + return $this->protectedHeader; + } + + public function getUnprotectedHeader(): array + { + return $this->unprotectedHeader; + } +} diff --git a/vendor/web-token/jwt-checker/Tests/Stub/TokenSupport.php b/vendor/web-token/jwt-checker/Tests/Stub/TokenSupport.php new file mode 100644 index 000000000..5edb3830c --- /dev/null +++ b/vendor/web-token/jwt-checker/Tests/Stub/TokenSupport.php @@ -0,0 +1,38 @@ +getProtectedHeader(); + $unprotectedHeader = $jwt->getUnprotectedHeader(); + } + + public function supports(JWT $jwt): bool + { + return $jwt instanceof Token; + } +} diff --git a/vendor/web-token/jwt-checker/Tests/UnencodedPayloadHeaderCheckerTest.php b/vendor/web-token/jwt-checker/Tests/UnencodedPayloadHeaderCheckerTest.php new file mode 100644 index 000000000..c9506691b --- /dev/null +++ b/vendor/web-token/jwt-checker/Tests/UnencodedPayloadHeaderCheckerTest.php @@ -0,0 +1,50 @@ +expectException(\Jose\Component\Checker\InvalidHeaderException::class); + $this->expectExceptionMessage('"b64" must be a boolean.'); + + $checker = new UnencodedPayloadChecker(); + $checker->checkHeader('foo'); + } + + /** + * @test + */ + public function theB64HeaderIsABoolean(): void + { + $checker = new UnencodedPayloadChecker(); + $checker->checkHeader(true); + $checker->checkHeader(false); + static::assertTrue($checker->protectedHeaderOnly()); + static::assertEquals('b64', $checker->supportedHeader()); + } +} diff --git a/vendor/web-token/jwt-checker/TokenTypeSupport.php b/vendor/web-token/jwt-checker/TokenTypeSupport.php new file mode 100644 index 000000000..14062fd51 --- /dev/null +++ b/vendor/web-token/jwt-checker/TokenTypeSupport.php @@ -0,0 +1,32 @@ + + + + + ./Tests/ + + + + + + ./ + + ./vendor + ./Tests + ./src + + + + diff --git a/vendor/web-token/jwt-core/.github/CONTRIBUTING.md b/vendor/web-token/jwt-core/.github/CONTRIBUTING.md new file mode 100644 index 000000000..fc360e5d8 --- /dev/null +++ b/vendor/web-token/jwt-core/.github/CONTRIBUTING.md @@ -0,0 +1,4 @@ +# Contributing + +This repository is a sub repository of [the JWT Framework](https://github.com/web-token/jwt-framework) project and is READ ONLY. +Please do not submit any Pull Requests here. It will be automatically closed. diff --git a/vendor/web-token/jwt-core/.github/FUNDING.yml b/vendor/web-token/jwt-core/.github/FUNDING.yml new file mode 100644 index 000000000..7e2ca0e7e --- /dev/null +++ b/vendor/web-token/jwt-core/.github/FUNDING.yml @@ -0,0 +1 @@ +patreon: FlorentMorselli diff --git a/vendor/web-token/jwt-core/.github/PULL_REQUEST_TEMPLATE.md b/vendor/web-token/jwt-core/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..d4ff96cb4 --- /dev/null +++ b/vendor/web-token/jwt-core/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,3 @@ +Please do not submit any Pull Requests here. It will be automatically closed. + +You should submit it here: https://github.com/web-token/jwt-framework/pulls diff --git a/vendor/web-token/jwt-core/Algorithm.php b/vendor/web-token/jwt-core/Algorithm.php new file mode 100644 index 000000000..87e445bb6 --- /dev/null +++ b/vendor/web-token/jwt-core/Algorithm.php @@ -0,0 +1,29 @@ +add($algorithm); + } + } + + /** + * Returns true if the algorithm is supported. + * + * @param string $algorithm The algorithm + */ + public function has(string $algorithm): bool + { + return array_key_exists($algorithm, $this->algorithms); + } + + /** + * Returns the list of names of supported algorithms. + * + * @return string[] + */ + public function list(): array + { + return array_keys($this->algorithms); + } + + /** + * Returns the algorithm if supported, otherwise throw an exception. + * + * @param string $algorithm The algorithm + * + * @throws InvalidArgumentException if the algorithm is not supported + */ + public function get(string $algorithm): Algorithm + { + if (!$this->has($algorithm)) { + throw new InvalidArgumentException(sprintf('The algorithm "%s" is not supported.', $algorithm)); + } + + return $this->algorithms[$algorithm]; + } + + /** + * Adds an algorithm to the manager. + */ + public function add(Algorithm $algorithm): void + { + $name = $algorithm->name(); + $this->algorithms[$name] = $algorithm; + } +} diff --git a/vendor/web-token/jwt-core/AlgorithmManagerFactory.php b/vendor/web-token/jwt-core/AlgorithmManagerFactory.php new file mode 100644 index 000000000..34e763136 --- /dev/null +++ b/vendor/web-token/jwt-core/AlgorithmManagerFactory.php @@ -0,0 +1,80 @@ +algorithms[$alias] = $algorithm; + } + + /** + * Returns the list of aliases. + * + * @return string[] + */ + public function aliases(): array + { + return array_keys($this->algorithms); + } + + /** + * Returns all algorithms supported by this factory. + * This is an associative array. Keys are the aliases of the algorithms. + * + * @return Algorithm[] + */ + public function all(): array + { + return $this->algorithms; + } + + /** + * Create an algorithm manager using the given aliases. + * + * @param string[] $aliases + * + * @throws InvalidArgumentException if the alias is invalid or is not supported + */ + public function create(array $aliases): AlgorithmManager + { + $algorithms = []; + foreach ($aliases as $alias) { + if (!is_string($alias)) { + throw new InvalidArgumentException('Invalid alias'); + } + if (!isset($this->algorithms[$alias])) { + throw new InvalidArgumentException(sprintf('The algorithm with the alias "%s" is not supported.', $alias)); + } + $algorithms[] = $this->algorithms[$alias]; + } + + return new AlgorithmManager($algorithms); + } +} diff --git a/vendor/web-token/jwt-core/JWK.php b/vendor/web-token/jwt-core/JWK.php new file mode 100644 index 000000000..f04115890 --- /dev/null +++ b/vendor/web-token/jwt-core/JWK.php @@ -0,0 +1,143 @@ +values = $values; + } + + /** + * Creates a JWK object using the given Json string. + * + * @throws InvalidArgumentException if the data is not valid + * + * @return JWK + */ + public static function createFromJson(string $json): self + { + $data = json_decode($json, true); + if (!is_array($data)) { + throw new InvalidArgumentException('Invalid argument.'); + } + + return new self($data); + } + + /** + * Returns the values to be serialized. + */ + public function jsonSerialize(): array + { + return $this->values; + } + + /** + * Get the value with a specific key. + * + * @param string $key The key + * + * @throws InvalidArgumentException if the key does not exist + * + * @return null|mixed + */ + public function get(string $key) + { + if (!$this->has($key)) { + throw new InvalidArgumentException(sprintf('The value identified by "%s" does not exist.', $key)); + } + + return $this->values[$key]; + } + + /** + * Returns true if the JWK has the value identified by. + * + * @param string $key The key + */ + public function has(string $key): bool + { + return array_key_exists($key, $this->values); + } + + /** + * Get all values stored in the JWK object. + * + * @return array Values of the JWK object + */ + public function all(): array + { + return $this->values; + } + + /** + * Returns the thumbprint of the key. + * + * @see https://tools.ietf.org/html/rfc7638 + * + * @throws InvalidArgumentException if the hashing function is not supported + */ + public function thumbprint(string $hash_algorithm): string + { + if (!in_array($hash_algorithm, hash_algos(), true)) { + throw new InvalidArgumentException(sprintf('The hash algorithm "%s" is not supported.', $hash_algorithm)); + } + $values = array_intersect_key($this->values, array_flip(['kty', 'n', 'e', 'crv', 'x', 'y', 'k'])); + ksort($values); + $input = json_encode($values, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + + return Base64Url::encode(hash($hash_algorithm, $input, true)); + } + + /** + * Returns the associated public key. + * This method has no effect for: + * - public keys + * - shared keys + * - unknown keys. + * + * Known keys are "oct", "RSA", "EC" and "OKP". + * + * @return JWK + */ + public function toPublic(): self + { + $values = array_diff_key($this->values, array_flip(['p', 'd', 'q', 'dp', 'dq', 'qi'])); + + return new self($values); + } +} diff --git a/vendor/web-token/jwt-core/JWKSet.php b/vendor/web-token/jwt-core/JWKSet.php new file mode 100644 index 000000000..2cf470f4c --- /dev/null +++ b/vendor/web-token/jwt-core/JWKSet.php @@ -0,0 +1,338 @@ + $key) { + if (!$key instanceof JWK) { + throw new InvalidArgumentException('Invalid list. Should only contains JWK objects'); + } + + if ($key->has('kid')) { + unset($keys[$k]); + $this->keys[$key->get('kid')] = $key; + } else { + $this->keys[] = $key; + } + } + } + + /** + * Creates a JWKSet object using the given values. + * + * @throws InvalidArgumentException if the keyset is not valid + * + * @return JWKSet + */ + public static function createFromKeyData(array $data): self + { + if (!isset($data['keys'])) { + throw new InvalidArgumentException('Invalid data.'); + } + if (!is_array($data['keys'])) { + throw new InvalidArgumentException('Invalid data.'); + } + + $jwkset = new self([]); + foreach ($data['keys'] as $key) { + $jwk = new JWK($key); + if ($jwk->has('kid')) { + $jwkset->keys[$jwk->get('kid')] = $jwk; + } else { + $jwkset->keys[] = $jwk; + } + } + + return $jwkset; + } + + /** + * Creates a JWKSet object using the given Json string. + * + * @throws InvalidArgumentException if the data is not valid + * + * @return JWKSet + */ + public static function createFromJson(string $json): self + { + $data = json_decode($json, true); + if (!is_array($data)) { + throw new InvalidArgumentException('Invalid argument.'); + } + + return self::createFromKeyData($data); + } + + /** + * Returns an array of keys stored in the key set. + * + * @return JWK[] + */ + public function all(): array + { + return $this->keys; + } + + /** + * Add key to store in the key set. + * This method is immutable and will return a new object. + * + * @return JWKSet + */ + public function with(JWK $jwk): self + { + $clone = clone $this; + + if ($jwk->has('kid')) { + $clone->keys[$jwk->get('kid')] = $jwk; + } else { + $clone->keys[] = $jwk; + } + + return $clone; + } + + /** + * Remove key from the key set. + * This method is immutable and will return a new object. + * + * @param int|string $key Key to remove from the key set + * + * @return JWKSet + */ + public function without($key): self + { + if (!$this->has($key)) { + return $this; + } + + $clone = clone $this; + unset($clone->keys[$key]); + + return $clone; + } + + /** + * Returns true if the key set contains a key with the given index. + * + * @param int|string $index + */ + public function has($index): bool + { + return array_key_exists($index, $this->keys); + } + + /** + * Returns the key with the given index. Throws an exception if the index is not present in the key store. + * + * @param int|string $index + * + * @throws InvalidArgumentException if the index is not defined + */ + public function get($index): JWK + { + if (!$this->has($index)) { + throw new InvalidArgumentException('Undefined index.'); + } + + return $this->keys[$index]; + } + + /** + * Returns the values to be serialized. + */ + public function jsonSerialize(): array + { + return ['keys' => array_values($this->keys)]; + } + + /** + * Returns the number of keys in the key set. + * + * @param int $mode + */ + public function count($mode = COUNT_NORMAL): int + { + return count($this->keys, $mode); + } + + /** + * Try to find a key that fits on the selected requirements. + * Returns null if not found. + * + * @param string $type Must be 'sig' (signature) or 'enc' (encryption) + * @param null|Algorithm $algorithm Specifies the algorithm to be used + * @param array $restrictions More restrictions such as 'kid' or 'kty' + * + * @throws InvalidArgumentException if the key type is not valid (must be "sig" or "enc") + */ + public function selectKey(string $type, ?Algorithm $algorithm = null, array $restrictions = []): ?JWK + { + if (!in_array($type, ['enc', 'sig'], true)) { + throw new InvalidArgumentException('Allowed key types are "sig" or "enc".'); + } + + $result = []; + foreach ($this->keys as $key) { + $ind = 0; + + $can_use = $this->canKeyBeUsedFor($type, $key); + if (false === $can_use) { + continue; + } + $ind += $can_use; + + $alg = $this->canKeyBeUsedWithAlgorithm($algorithm, $key); + if (false === $alg) { + continue; + } + $ind += $alg; + + if (false === $this->doesKeySatisfyRestrictions($restrictions, $key)) { + continue; + } + + $result[] = ['key' => $key, 'ind' => $ind]; + } + + if (0 === count($result)) { + return null; + } + + usort($result, [$this, 'sortKeys']); + + return $result[0]['key']; + } + + /** + * Internal method only. Should not be used. + * + * @internal + * @internal + */ + public static function sortKeys(array $a, array $b): int + { + if ($a['ind'] === $b['ind']) { + return 0; + } + + return ($a['ind'] > $b['ind']) ? -1 : 1; + } + + /** + * Internal method only. Should not be used. + * + * @internal + */ + public function getIterator(): Traversable + { + return new ArrayIterator($this->keys); + } + + /** + * @throws InvalidArgumentException if the key does not fulfill with the "key_ops" constraint + * + * @return bool|int + */ + private function canKeyBeUsedFor(string $type, JWK $key) + { + if ($key->has('use')) { + return $type === $key->get('use') ? 1 : false; + } + if ($key->has('key_ops')) { + $key_ops = $key->get('key_ops'); + if (!is_array($key_ops)) { + throw new InvalidArgumentException('Invalid key parameter "key_ops". Should be a list of key operations'); + } + + return $type === self::convertKeyOpsToKeyUse($key_ops) ? 1 : false; + } + + return 0; + } + + /** + * @return bool|int + */ + private function canKeyBeUsedWithAlgorithm(?Algorithm $algorithm, JWK $key) + { + if (null === $algorithm) { + return 0; + } + if (!in_array($key->get('kty'), $algorithm->allowedKeyTypes(), true)) { + return false; + } + if ($key->has('alg')) { + return $algorithm->name() === $key->get('alg') ? 2 : false; + } + + return 1; + } + + private function doesKeySatisfyRestrictions(array $restrictions, JWK $key): bool + { + foreach ($restrictions as $k => $v) { + if (!$key->has($k) || $v !== $key->get($k)) { + return false; + } + } + + return true; + } + + /** + * @throws InvalidArgumentException if the key operation is not supported + */ + private static function convertKeyOpsToKeyUse(array $key_ops): string + { + switch (true) { + case in_array('verify', $key_ops, true): + case in_array('sign', $key_ops, true): + return 'sig'; + case in_array('encrypt', $key_ops, true): + case in_array('decrypt', $key_ops, true): + case in_array('wrapKey', $key_ops, true): + case in_array('unwrapKey', $key_ops, true): + case in_array('deriveKey', $key_ops, true): + case in_array('deriveBits', $key_ops, true): + return 'enc'; + default: + throw new InvalidArgumentException(sprintf('Unsupported key operation value "%s"', $key_ops)); + } + } +} diff --git a/vendor/web-token/jwt-core/JWT.php b/vendor/web-token/jwt-core/JWT.php new file mode 100644 index 000000000..d3a6065f3 --- /dev/null +++ b/vendor/web-token/jwt-core/JWT.php @@ -0,0 +1,23 @@ +getAlgorithmManagerFactory()->aliases()); + static::assertEquals(['foo'], array_keys($this->getAlgorithmManagerFactory()->all())); + } + + /** + * @test + * @covers \Jose\Component\Core\AlgorithmManager + */ + public function iCannotCreateAnAlgorithmManagerWithABadArgument(): void + { + $this->expectException(TypeError::class); + + new AlgorithmManager(['foo']); + } + + /** + * @test + * @covers \Jose\Component\Core\AlgorithmManager + */ + public function iCannotGetAnAlgorithmThatDoesNotExist(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The algorithm "HS384" is not supported.'); + + $manager = new AlgorithmManager([new FooAlgorithm()]); + + static::assertEquals(['foo'], $manager->list()); + static::assertTrue($manager->has('foo')); + static::assertFalse($manager->has('HS384')); + $manager->get('HS384'); + } + + private function getAlgorithmManagerFactory(): AlgorithmManagerFactory + { + if (null === $this->algorithmManagerFactory) { + $this->algorithmManagerFactory = new AlgorithmManagerFactory(); + $this->algorithmManagerFactory->add('foo', new FooAlgorithm()); + } + + return $this->algorithmManagerFactory; + } +} diff --git a/vendor/web-token/jwt-core/Tests/FooAlgorithm.php b/vendor/web-token/jwt-core/Tests/FooAlgorithm.php new file mode 100644 index 000000000..33f661acb --- /dev/null +++ b/vendor/web-token/jwt-core/Tests/FooAlgorithm.php @@ -0,0 +1,29 @@ +getPublicKeySet(); + + $jwk = $jwkset->selectKey('enc'); + static::assertInstanceOf(JWK::class, $jwk); + } + + /** + * @test + */ + public function iCannotSelectAKeyFromAKeySetWithUnsupportedUsageParameter(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Allowed key types are "sig" or "enc".'); + + $jwkset = $this->getPublicKeySet(); + $jwkset->selectKey('foo'); + } + + /** + * @test + */ + public function iCannotCreateAKeySetWithBadArguments(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid data.'); + + JWKSet::createFromKeyData(['keys' => true]); + } + + /** + * @test + */ + public function iCanGetAllKeysInAKeySet(): void + { + $jwkset = $this->getPublicKeySet(); + static::assertEquals(3, count($jwkset->all())); + } + + /** + * @test + */ + public function iCanAddKeysInAKeySet(): void + { + $jwkset = $this->getPublicKeySet(); + $new_jwkset = $jwkset->with(new JWK(['kty' => 'none'])); + static::assertEquals(4, count($new_jwkset->all())); + static::assertNotSame($jwkset, $new_jwkset); + } + + /** + * @test + */ + public function iCanSelectAKeyWithAlgorithm(): void + { + $jwkset = $this->getPublicKeySet(); + + $jwk = $jwkset->selectKey('enc', new FooAlgorithm()); + static::assertInstanceOf(JWK::class, $jwk); + static::assertEquals( + [ + 'kid' => '71ee230371d19630bc17fb90ccf20ae632ad8cf8', + 'kty' => 'FOO', + 'alg' => 'foo', + 'use' => 'enc', + ], + $jwk->all() + ); + } + + /** + * @test + */ + public function iCanSelectAKeyWithAlgorithmAndKeyId(): void + { + $jwkset = $this->getPublicKeySet(); + + $jwk = $jwkset->selectKey('sig', new FooAlgorithm(), ['kid' => '02491f945c951adf156f370788e8ccdabf8877a8']); + static::assertInstanceOf(JWK::class, $jwk); + static::assertEquals( + [ + 'kid' => '02491f945c951adf156f370788e8ccdabf8877a8', + 'kty' => 'FOO', + 'alg' => 'foo', + 'use' => 'sig', + ], + $jwk->all() + ); + } + + /** + * @test + */ + public function iCanSelectAKeyWithWithKeyId(): void + { + $jwkset = $this->getPublicKeySet(); + + $jwk = $jwkset->selectKey('sig', null, ['kid' => '02491f945c951adf156f370788e8ccdabf8877a8']); + static::assertInstanceOf(JWK::class, $jwk); + static::assertEquals( + [ + 'kid' => '02491f945c951adf156f370788e8ccdabf8877a8', + 'kty' => 'FOO', + 'alg' => 'foo', + 'use' => 'sig', + ], + $jwk->all() + ); + } + + /** + * @test + */ + public function theKeySetDoesNotContainsSuitableAKeyThatFitsOnTheRequirements(): void + { + $jwkset = $this->getPublicKeySet(); + + $jwk = $jwkset->selectKey('enc', null, ['kid' => '02491f945c951adf156f370788e8ccdabf8877a8']); + static::assertNull($jwk); + } + + /** + * @test + */ + public function iCanCreateAKeySetUsingValues(): void + { + $values = ['keys' => [[ + 'kid' => '71ee230371d19630bc17fb90ccf20ae632ad8cf8', + 'kty' => 'FOO', + 'alg' => 'foo', + 'use' => 'sig', + ]]]; + $jwkset = JWKSet::createFromKeyData($values); + static::assertEquals(1, count($jwkset)); + static::assertTrue($jwkset->has('71ee230371d19630bc17fb90ccf20ae632ad8cf8')); + static::assertFalse($jwkset->has(0)); + } + + /** + * @test + */ + public function keySet(): void + { + $jwk1 = new JWK([ + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => 'f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU', + 'y' => 'x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0', + 'use' => 'sign', + 'key_ops' => ['sign'], + 'alg' => 'ES256', + 'kid' => '0123456789', + ]); + + $jwk2 = new JWK([ + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => 'f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU', + 'y' => 'x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0', + 'd' => 'jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI', + 'use' => 'sign', + 'key_ops' => ['verify'], + 'alg' => 'ES256', + 'kid' => '9876543210', + ]); + + $jwkset = new JWKSet([$jwk1]); + $jwkset = $jwkset->with($jwk2); + + static::assertEquals('{"keys":[{"kty":"EC","crv":"P-256","x":"f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU","y":"x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0","use":"sign","key_ops":["sign"],"alg":"ES256","kid":"0123456789"},{"kty":"EC","crv":"P-256","x":"f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU","y":"x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0","d":"jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI","use":"sign","key_ops":["verify"],"alg":"ES256","kid":"9876543210"}]}', json_encode($jwkset)); + static::assertEquals(2, count($jwkset)); + static::assertEquals(2, $jwkset->count()); + static::assertTrue($jwkset->has('0123456789')); + static::assertTrue($jwkset->has('9876543210')); + static::assertFalse($jwkset->has(0)); + + foreach ($jwkset as $key) { + static::assertEquals('EC', $key->get('kty')); + } + + static::assertEquals('9876543210', $jwkset->get('9876543210')->get('kid')); + $jwkset = $jwkset->without('9876543210'); + $jwkset = $jwkset->without('9876543210'); + + static::assertEquals(1, count($jwkset)); + static::assertEquals(1, $jwkset->count()); + + $jwkset = $jwkset->without('0123456789'); + static::assertEquals(0, $jwkset->count()); + } + + /** + * @test + */ + public function keySet2(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Undefined index.'); + + $jwk1 = new JWK([ + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => 'f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU', + 'y' => 'x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0', + 'use' => 'sign', + 'key_ops' => ['sign'], + 'alg' => 'ES256', + 'kid' => '0123456789', + ]); + + $jwk2 = new JWK([ + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => 'f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU', + 'y' => 'x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0', + 'd' => 'jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI', + 'use' => 'sign', + 'key_ops' => ['verify'], + 'alg' => 'ES256', + 'kid' => '9876543210', + ]); + + $jwkset = new JWKSet([$jwk1, $jwk2]); + + $jwkset->get(2); + } + + private function getPublicKeySet(): JWKSet + { + $keys = ['keys' => [ + [ + 'kid' => '71ee230371d19630bc17fb90ccf20ae632ad8cf8', + 'kty' => 'FOO', + 'alg' => 'foo', + 'use' => 'enc', + ], + [ + 'kid' => '02491f945c951adf156f370788e8ccdabf8877a8', + 'kty' => 'FOO', + 'alg' => 'foo', + 'use' => 'sig', + ], + [ + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => 'f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU', + 'y' => 'x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0', + ], + ]]; + + return JWKSet::createFromKeyData($keys); + } +} diff --git a/vendor/web-token/jwt-core/Tests/JWKTest.php b/vendor/web-token/jwt-core/Tests/JWKTest.php new file mode 100644 index 000000000..16070d97b --- /dev/null +++ b/vendor/web-token/jwt-core/Tests/JWKTest.php @@ -0,0 +1,147 @@ + 'EC', + 'crv' => 'P-256', + 'x' => 'f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU', + 'y' => 'x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0', + 'use' => 'sig', + 'key_ops' => ['sign'], + 'alg' => 'ES256', + 'bar' => 'plic', + ]); + + static::assertEquals('EC', $jwk->get('kty')); + static::assertEquals('ES256', $jwk->get('alg')); + static::assertEquals('sig', $jwk->get('use')); + static::assertFalse($jwk->has('kid')); + static::assertEquals(['sign'], $jwk->get('key_ops')); + static::assertEquals('P-256', $jwk->get('crv')); + static::assertFalse($jwk->has('x5u')); + static::assertFalse($jwk->has('x5c')); + static::assertFalse($jwk->has('x5t')); + static::assertFalse($jwk->has('x5t#256')); + static::assertEquals('f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU', $jwk->get('x')); + static::assertEquals('x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0', $jwk->get('y')); + static::assertEquals('{"kty":"EC","crv":"P-256","x":"f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU","y":"x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0","use":"sig","key_ops":["sign"],"alg":"ES256","bar":"plic"}', json_encode($jwk)); + static::assertEquals('oKIywvGUpTVTyxMQ3bwIIeQUudfr_CkLMjCE19ECD-U', $jwk->thumbprint('sha256')); + static::assertEquals('EMMMl6Rj75mqhcABihxxl_VCN9s', $jwk->thumbprint('sha1')); + static::assertEquals('dqwHnan4iJ1_eEll-o4Egw', $jwk->thumbprint('md5')); + } + + /** + * @test + */ + public function iCannotGetTheThumbprintOfTheKeyWhenIUseAnUnsupportedHashingAlgorithm(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The hash algorithm "foo" is not supported.'); + + $jwk = new JWK([ + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => 'f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU', + 'y' => 'x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0', + 'use' => 'sig', + 'key_ops' => ['sign'], + 'alg' => 'ES256', + 'bar' => 'plic', + ]); + + $jwk->thumbprint('foo'); + } + + /** + * @test + */ + public function iMustSetAtLeastTheKtyParameter(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The parameter "kty" is mandatory.'); + + new JWK([]); + } + + /** + * @test + */ + public function iCannotGetAParameterThatDoesNotExist(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The value identified by "ABCD" does not exist.'); + + $jwk = new JWK([ + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => 'f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU', + 'y' => 'x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0', + 'use' => 'sign', + 'key_ops' => ['sign'], + 'alg' => 'ES256', + 'bar' => 'plic', + ]); + + $jwk->get('ABCD'); + } + + /** + * @test + */ + public function iCanConvertAPrivateKeyIntoPublicKey(): void + { + $private = new JWK([ + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => 'f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU', + 'y' => 'x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0', + 'd' => 'jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI', + 'use' => 'sign', + 'key_ops' => ['verify'], + 'alg' => 'ES256', + 'kid' => '9876543210', + ]); + + $public = $private->toPublic(); + + static::assertEquals(json_encode([ + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => 'f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU', + 'y' => 'x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0', + 'use' => 'sign', + 'key_ops' => ['verify'], + 'alg' => 'ES256', + 'kid' => '9876543210', + ]), json_encode($public)); + } +} diff --git a/vendor/web-token/jwt-core/Tests/JsonConverterTest.php b/vendor/web-token/jwt-core/Tests/JsonConverterTest.php new file mode 100644 index 000000000..c3266b6f7 --- /dev/null +++ b/vendor/web-token/jwt-core/Tests/JsonConverterTest.php @@ -0,0 +1,35 @@ + 'BAR'])); + static::assertEquals(['foo' => 'BAR'], JsonConverter::decode('{"foo":"BAR"}')); + } +} diff --git a/vendor/web-token/jwt-core/Util/BigInteger.php b/vendor/web-token/jwt-core/Util/BigInteger.php new file mode 100644 index 000000000..a4f54d566 --- /dev/null +++ b/vendor/web-token/jwt-core/Util/BigInteger.php @@ -0,0 +1,223 @@ +value = $value; + } + + /** + * @return BigInteger + */ + public static function createFromBinaryString(string $value): self + { + $data = current(unpack('H*', $value)); + + return new self(BrickBigInteger::fromBase($data, 16)); + } + + /** + * @return BigInteger + */ + public static function createFromDecimal(int $value): self + { + return new self(BrickBigInteger::of($value)); + } + + /** + * @return BigInteger + */ + public static function createFromBigInteger(BrickBigInteger $value): self + { + return new self($value); + } + + /** + * Converts a BigInteger to a binary string. + */ + public function toBytes(): string + { + if ($this->value->isEqualTo(BrickBigInteger::zero())) { + return ''; + } + + $temp = $this->value->toBase(16); + $temp = 0 !== (mb_strlen($temp, '8bit') & 1) ? '0'.$temp : $temp; + $temp = hex2bin($temp); + + return ltrim($temp, chr(0)); + } + + /** + * Adds two BigIntegers. + * + * @param BigInteger $y + * + * @return BigInteger + */ + public function add(self $y): self + { + $value = $this->value->plus($y->value); + + return new self($value); + } + + /** + * Subtracts two BigIntegers. + * + * @param BigInteger $y + * + * @return BigInteger + */ + public function subtract(self $y): self + { + $value = $this->value->minus($y->value); + + return new self($value); + } + + /** + * Multiplies two BigIntegers. + * + * @param BigInteger $x + * + * @return BigInteger + */ + public function multiply(self $x): self + { + $value = $this->value->multipliedBy($x->value); + + return new self($value); + } + + /** + * Divides two BigIntegers. + * + * @param BigInteger $x + * + * @return BigInteger + */ + public function divide(self $x): self + { + $value = $this->value->dividedBy($x->value); + + return new self($value); + } + + /** + * Performs modular exponentiation. + * + * @param BigInteger $e + * @param BigInteger $n + * + * @return BigInteger + */ + public function modPow(self $e, self $n): self + { + $value = $this->value->modPow($e->value, $n->value); + + return new self($value); + } + + /** + * Performs modular exponentiation. + * + * @param BigInteger $d + * + * @return BigInteger + */ + public function mod(self $d): self + { + $value = $this->value->mod($d->value); + + return new self($value); + } + + public function modInverse(BigInteger $m): BigInteger + { + return new self($this->value->modInverse($m->value)); + } + + /** + * Compares two numbers. + * + * @param BigInteger $y + */ + public function compare(self $y): int + { + return $this->value->compareTo($y->value); + } + + /** + * @param BigInteger $y + */ + public function equals(self $y): bool + { + return $this->value->isEqualTo($y->value); + } + + /** + * @param BigInteger $y + * + * @return BigInteger + */ + public static function random(self $y): self + { + return new self(BrickBigInteger::randomRange(0, $y->value)); + } + + /** + * @param BigInteger $y + * + * @return BigInteger + */ + public function gcd(self $y): self + { + return new self($this->value->gcd($y->value)); + } + + /** + * @param BigInteger $y + */ + public function lowerThan(self $y): bool + { + return $this->value->isLessThan($y->value); + } + + public function isEven(): bool + { + return $this->value->isEven(); + } + + public function get(): BrickBigInteger + { + return $this->value; + } +} diff --git a/vendor/web-token/jwt-core/Util/ECKey.php b/vendor/web-token/jwt-core/Util/ECKey.php new file mode 100644 index 000000000..6127434e8 --- /dev/null +++ b/vendor/web-token/jwt-core/Util/ECKey.php @@ -0,0 +1,335 @@ +has('d')) { + return self::convertPrivateKeyToPEM($jwk); + } + + return self::convertPublicKeyToPEM($jwk); + } + + /** + * @throws InvalidArgumentException if the curve is not supported + */ + public static function convertPublicKeyToPEM(JWK $jwk): string + { + switch ($jwk->get('crv')) { + case 'P-256': + $der = self::p256PublicKey(); + + break; + case 'secp256k1': + $der = self::p256KPublicKey(); + + break; + case 'P-384': + $der = self::p384PublicKey(); + + break; + case 'P-521': + $der = self::p521PublicKey(); + + break; + default: + throw new InvalidArgumentException('Unsupported curve.'); + } + $der .= self::getKey($jwk); + $pem = '-----BEGIN PUBLIC KEY-----'.PHP_EOL; + $pem .= chunk_split(base64_encode($der), 64, PHP_EOL); + $pem .= '-----END PUBLIC KEY-----'.PHP_EOL; + + return $pem; + } + + /** + * @throws InvalidArgumentException if the curve is not supported + */ + public static function convertPrivateKeyToPEM(JWK $jwk): string + { + switch ($jwk->get('crv')) { + case 'P-256': + $der = self::p256PrivateKey($jwk); + + break; + case 'secp256k1': + $der = self::p256KPrivateKey($jwk); + + break; + case 'P-384': + $der = self::p384PrivateKey($jwk); + + break; + case 'P-521': + $der = self::p521PrivateKey($jwk); + + break; + default: + throw new InvalidArgumentException('Unsupported curve.'); + } + $der .= self::getKey($jwk); + $pem = '-----BEGIN EC PRIVATE KEY-----'.PHP_EOL; + $pem .= chunk_split(base64_encode($der), 64, PHP_EOL); + $pem .= '-----END EC PRIVATE KEY-----'.PHP_EOL; + + return $pem; + } + + /** + * Creates a EC key with the given curve and additional values. + * + * @param string $curve The curve + * @param array $values values to configure the key + */ + public static function createECKey(string $curve, array $values = []): JWK + { + $jwk = self::createECKeyUsingOpenSSL($curve); + $values = array_merge($values, $jwk); + + return new JWK($values); + } + + /** + * @throws InvalidArgumentException if the curve is not supported + */ + private static function getNistCurveSize(string $curve): int + { + switch ($curve) { + case 'P-256': + case 'secp256k1': + return 256; + case 'P-384': + return 384; + case 'P-521': + return 521; + default: + throw new InvalidArgumentException(sprintf('The curve "%s" is not supported.', $curve)); + } + } + + /** + * @throws RuntimeException if the extension OpenSSL is not available + * @throws RuntimeException if the key cannot be created + */ + private static function createECKeyUsingOpenSSL(string $curve): array + { + if (!extension_loaded('openssl')) { + throw new RuntimeException('Please install the OpenSSL extension'); + } + $key = openssl_pkey_new([ + 'curve_name' => self::getOpensslCurveName($curve), + 'private_key_type' => OPENSSL_KEYTYPE_EC, + ]); + if (false === $key) { + throw new RuntimeException('Unable to create the key'); + } + $result = openssl_pkey_export($key, $out); + if (false === $result) { + throw new RuntimeException('Unable to create the key'); + } + $res = openssl_pkey_get_private($out); + if (false === $res) { + throw new RuntimeException('Unable to create the key'); + } + $details = openssl_pkey_get_details($res); + $nistCurveSize = self::getNistCurveSize($curve); + + return [ + 'kty' => 'EC', + 'crv' => $curve, + 'd' => Base64Url::encode(str_pad($details['ec']['d'], (int) ceil($nistCurveSize / 8), "\0", STR_PAD_LEFT)), + 'x' => Base64Url::encode(str_pad($details['ec']['x'], (int) ceil($nistCurveSize / 8), "\0", STR_PAD_LEFT)), + 'y' => Base64Url::encode(str_pad($details['ec']['y'], (int) ceil($nistCurveSize / 8), "\0", STR_PAD_LEFT)), + ]; + } + + /** + * @throws InvalidArgumentException if the curve is not supported + */ + private static function getOpensslCurveName(string $curve): string + { + switch ($curve) { + case 'P-256': + return 'prime256v1'; + case 'secp256k1': + return 'secp256k1'; + case 'P-384': + return 'secp384r1'; + case 'P-521': + return 'secp521r1'; + default: + throw new InvalidArgumentException(sprintf('The curve "%s" is not supported.', $curve)); + } + } + + private static function p256PublicKey(): string + { + return pack( + 'H*', + '3059' // SEQUENCE, length 89 + .'3013' // SEQUENCE, length 19 + .'0607' // OID, length 7 + .'2a8648ce3d0201' // 1.2.840.10045.2.1 = EC Public Key + .'0608' // OID, length 8 + .'2a8648ce3d030107' // 1.2.840.10045.3.1.7 = P-256 Curve + .'0342' // BIT STRING, length 66 + .'00' // prepend with NUL - pubkey will follow + ); + } + + private static function p256KPublicKey(): string + { + return pack( + 'H*', + '3056' // SEQUENCE, length 86 + .'3010' // SEQUENCE, length 16 + .'0607' // OID, length 7 + .'2a8648ce3d0201' // 1.2.840.10045.2.1 = EC Public Key + .'0605' // OID, length 8 + .'2B8104000A' // 1.3.132.0.10 secp256k1 + .'0342' // BIT STRING, length 66 + .'00' // prepend with NUL - pubkey will follow + ); + } + + private static function p384PublicKey(): string + { + return pack( + 'H*', + '3076' // SEQUENCE, length 118 + .'3010' // SEQUENCE, length 16 + .'0607' // OID, length 7 + .'2a8648ce3d0201' // 1.2.840.10045.2.1 = EC Public Key + .'0605' // OID, length 5 + .'2b81040022' // 1.3.132.0.34 = P-384 Curve + .'0362' // BIT STRING, length 98 + .'00' // prepend with NUL - pubkey will follow + ); + } + + private static function p521PublicKey(): string + { + return pack( + 'H*', + '30819b' // SEQUENCE, length 154 + .'3010' // SEQUENCE, length 16 + .'0607' // OID, length 7 + .'2a8648ce3d0201' // 1.2.840.10045.2.1 = EC Public Key + .'0605' // OID, length 5 + .'2b81040023' // 1.3.132.0.35 = P-521 Curve + .'038186' // BIT STRING, length 134 + .'00' // prepend with NUL - pubkey will follow + ); + } + + private static function p256PrivateKey(JWK $jwk): string + { + $d = unpack('H*', str_pad(Base64Url::decode($jwk->get('d')), 32, "\0", STR_PAD_LEFT))[1]; + + return pack( + 'H*', + '3077' // SEQUENCE, length 87+length($d)=32 + .'020101' // INTEGER, 1 + .'0420' // OCTET STRING, length($d) = 32 + .$d + .'a00a' // TAGGED OBJECT #0, length 10 + .'0608' // OID, length 8 + .'2a8648ce3d030107' // 1.3.132.0.34 = P-256 Curve + .'a144' // TAGGED OBJECT #1, length 68 + .'0342' // BIT STRING, length 66 + .'00' // prepend with NUL - pubkey will follow + ); + } + + private static function p256KPrivateKey(JWK $jwk): string + { + $d = unpack('H*', str_pad(Base64Url::decode($jwk->get('d')), 32, "\0", STR_PAD_LEFT))[1]; + + return pack( + 'H*', + '3074' // SEQUENCE, length 84+length($d)=32 + .'020101' // INTEGER, 1 + .'0420' // OCTET STRING, length($d) = 32 + .$d + .'a007' // TAGGED OBJECT #0, length 7 + .'0605' // OID, length 5 + .'2b8104000a' // 1.3.132.0.10 secp256k1 + .'a144' // TAGGED OBJECT #1, length 68 + .'0342' // BIT STRING, length 66 + .'00' // prepend with NUL - pubkey will follow + ); + } + + private static function p384PrivateKey(JWK $jwk): string + { + $d = unpack('H*', str_pad(Base64Url::decode($jwk->get('d')), 48, "\0", STR_PAD_LEFT))[1]; + + return pack( + 'H*', + '3081a4' // SEQUENCE, length 116 + length($d)=48 + .'020101' // INTEGER, 1 + .'0430' // OCTET STRING, length($d) = 30 + .$d + .'a007' // TAGGED OBJECT #0, length 7 + .'0605' // OID, length 5 + .'2b81040022' // 1.3.132.0.34 = P-384 Curve + .'a164' // TAGGED OBJECT #1, length 100 + .'0362' // BIT STRING, length 98 + .'00' // prepend with NUL - pubkey will follow + ); + } + + private static function p521PrivateKey(JWK $jwk): string + { + $d = unpack('H*', str_pad(Base64Url::decode($jwk->get('d')), 66, "\0", STR_PAD_LEFT))[1]; + + return pack( + 'H*', + '3081dc' // SEQUENCE, length 154 + length($d)=66 + .'020101' // INTEGER, 1 + .'0442' // OCTET STRING, length(d) = 66 + .$d + .'a007' // TAGGED OBJECT #0, length 7 + .'0605' // OID, length 5 + .'2b81040023' // 1.3.132.0.35 = P-521 Curve + .'a18189' // TAGGED OBJECT #1, length 137 + .'038186' // BIT STRING, length 134 + .'00' // prepend with NUL - pubkey will follow + ); + } + + private static function getKey(JWK $jwk): string + { + $nistCurveSize = self::getNistCurveSize($jwk->get('crv')); + $length = (int) ceil($nistCurveSize / 8); + + return + "\04" + .str_pad(Base64Url::decode($jwk->get('x')), $length, "\0", STR_PAD_LEFT) + .str_pad(Base64Url::decode($jwk->get('y')), $length, "\0", STR_PAD_LEFT); + } +} diff --git a/vendor/web-token/jwt-core/Util/ECSignature.php b/vendor/web-token/jwt-core/Util/ECSignature.php new file mode 100644 index 000000000..d5d0fe4ed --- /dev/null +++ b/vendor/web-token/jwt-core/Util/ECSignature.php @@ -0,0 +1,132 @@ + self::ASN1_MAX_SINGLE_BYTE ? self::ASN1_LENGTH_2BYTES : ''; + + return hex2bin( + self::ASN1_SEQUENCE + .$lengthPrefix.dechex($totalLength) + .self::ASN1_INTEGER.dechex($lengthR).$pointR + .self::ASN1_INTEGER.dechex($lengthS).$pointS + ); + } + + /** + * @throws InvalidArgumentException if the signature is not an ASN.1 sequence + */ + public static function fromAsn1(string $signature, int $length): string + { + $message = bin2hex($signature); + $position = 0; + + if (self::ASN1_SEQUENCE !== self::readAsn1Content($message, $position, self::BYTE_SIZE)) { + throw new InvalidArgumentException('Invalid data. Should start with a sequence.'); + } + + if (self::ASN1_LENGTH_2BYTES === self::readAsn1Content($message, $position, self::BYTE_SIZE)) { + $position += self::BYTE_SIZE; + } + + $pointR = self::retrievePositiveInteger(self::readAsn1Integer($message, $position)); + $pointS = self::retrievePositiveInteger(self::readAsn1Integer($message, $position)); + + return hex2bin(str_pad($pointR, $length, '0', STR_PAD_LEFT).str_pad($pointS, $length, '0', STR_PAD_LEFT)); + } + + private static function octetLength(string $data): int + { + return (int) (mb_strlen($data, '8bit') / self::BYTE_SIZE); + } + + private static function preparePositiveInteger(string $data): string + { + if (mb_substr($data, 0, self::BYTE_SIZE, '8bit') > self::ASN1_BIG_INTEGER_LIMIT) { + return self::ASN1_NEGATIVE_INTEGER.$data; + } + + while (0 === mb_strpos($data, self::ASN1_NEGATIVE_INTEGER, 0, '8bit') + && mb_substr($data, 2, self::BYTE_SIZE, '8bit') <= self::ASN1_BIG_INTEGER_LIMIT) { + $data = mb_substr($data, 2, null, '8bit'); + } + + return $data; + } + + private static function readAsn1Content(string $message, int &$position, int $length): string + { + $content = mb_substr($message, $position, $length, '8bit'); + $position += $length; + + return $content; + } + + /** + * @throws InvalidArgumentException if the data is not an integer + */ + private static function readAsn1Integer(string $message, int &$position): string + { + if (self::ASN1_INTEGER !== self::readAsn1Content($message, $position, self::BYTE_SIZE)) { + throw new InvalidArgumentException('Invalid data. Should contain an integer.'); + } + + $length = (int) hexdec(self::readAsn1Content($message, $position, self::BYTE_SIZE)); + + return self::readAsn1Content($message, $position, $length * self::BYTE_SIZE); + } + + private static function retrievePositiveInteger(string $data): string + { + while (0 === mb_strpos($data, self::ASN1_NEGATIVE_INTEGER, 0, '8bit') + && mb_substr($data, 2, self::BYTE_SIZE, '8bit') > self::ASN1_BIG_INTEGER_LIMIT) { + $data = mb_substr($data, 2, null, '8bit'); + } + + return $data; + } +} diff --git a/vendor/web-token/jwt-core/Util/Hash.php b/vendor/web-token/jwt-core/Util/Hash.php new file mode 100644 index 000000000..ef038c53c --- /dev/null +++ b/vendor/web-token/jwt-core/Util/Hash.php @@ -0,0 +1,103 @@ +hash = $hash; + $this->length = $length; + $this->t = $t; + } + + /** + * @return Hash + */ + public static function sha1(): self + { + return new self('sha1', 20, "\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14"); + } + + /** + * @return Hash + */ + public static function sha256(): self + { + return new self('sha256', 32, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20"); + } + + /** + * @return Hash + */ + public static function sha384(): self + { + return new self('sha384', 48, "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30"); + } + + /** + * @return Hash + */ + public static function sha512(): self + { + return new self('sha512', 64, "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40"); + } + + public function getLength(): int + { + return $this->length; + } + + /** + * Compute the HMAC. + */ + public function hash(string $text): string + { + return hash($this->hash, $text, true); + } + + public function name(): string + { + return $this->hash; + } + + public function t(): string + { + return $this->t; + } +} diff --git a/vendor/web-token/jwt-core/Util/JsonConverter.php b/vendor/web-token/jwt-core/Util/JsonConverter.php new file mode 100644 index 000000000..7ac60bb72 --- /dev/null +++ b/vendor/web-token/jwt-core/Util/JsonConverter.php @@ -0,0 +1,48 @@ +getCode(), $throwable); + } + } + + /** + * @throws RuntimeException if the payload cannot be decoded + * + * @return mixed + */ + public static function decode(string $payload) + { + try { + return json_decode($payload, true, 512, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + } catch (Throwable $throwable) { + throw new RuntimeException('Invalid content.', $throwable->getCode(), $throwable); + } + } +} diff --git a/vendor/web-token/jwt-core/Util/KeyChecker.php b/vendor/web-token/jwt-core/Util/KeyChecker.php new file mode 100644 index 000000000..e0fdb7cc6 --- /dev/null +++ b/vendor/web-token/jwt-core/Util/KeyChecker.php @@ -0,0 +1,113 @@ +has('use')) { + self::checkUsage($key, $usage); + } + if ($key->has('key_ops')) { + self::checkOperation($key, $usage); + } + } + + /** + * @throws InvalidArgumentException if the key is not suitable for the selected algorithm + */ + public static function checkKeyAlgorithm(JWK $key, string $algorithm): void + { + if (!$key->has('alg')) { + return; + } + if ($key->get('alg') !== $algorithm) { + throw new InvalidArgumentException(sprintf('Key is only allowed for algorithm "%s".', $key->get('alg'))); + } + } + + /** + * @throws InvalidArgumentException if the key is not suitable for the selected operation + */ + private static function checkOperation(JWK $key, string $usage): void + { + $ops = $key->get('key_ops'); + if (!is_array($ops)) { + throw new InvalidArgumentException('Invalid key parameter "key_ops". Should be a list of key operations'); + } + switch ($usage) { + case 'verification': + if (!in_array('verify', $ops, true)) { + throw new InvalidArgumentException('Key cannot be used to verify a signature'); + } + + break; + case 'signature': + if (!in_array('sign', $ops, true)) { + throw new InvalidArgumentException('Key cannot be used to sign'); + } + + break; + case 'encryption': + if (!in_array('encrypt', $ops, true) && !in_array('wrapKey', $ops, true) && !in_array('deriveKey', $ops, true)) { + throw new InvalidArgumentException('Key cannot be used to encrypt'); + } + + break; + case 'decryption': + if (!in_array('decrypt', $ops, true) && !in_array('unwrapKey', $ops, true) && !in_array('deriveBits', $ops, true)) { + throw new InvalidArgumentException('Key cannot be used to decrypt'); + } + + break; + default: + throw new InvalidArgumentException('Unsupported key usage.'); + } + } + + /** + * @throws InvalidArgumentException if the key is not suitable for the selected operation + */ + private static function checkUsage(JWK $key, string $usage): void + { + $use = $key->get('use'); + switch ($usage) { + case 'verification': + case 'signature': + if ('sig' !== $use) { + throw new InvalidArgumentException('Key cannot be used to sign or verify a signature.'); + } + + break; + case 'encryption': + case 'decryption': + if ('enc' !== $use) { + throw new InvalidArgumentException('Key cannot be used to encrypt or decrypt.'); + } + + break; + default: + throw new InvalidArgumentException('Unsupported key usage.'); + } + } +} diff --git a/vendor/web-token/jwt-core/Util/RSAKey.php b/vendor/web-token/jwt-core/Util/RSAKey.php new file mode 100644 index 000000000..d3ac1ce32 --- /dev/null +++ b/vendor/web-token/jwt-core/Util/RSAKey.php @@ -0,0 +1,310 @@ +values = $data->all(); + $this->populateBigIntegers(); + $this->private = array_key_exists('d', $this->values); + } + + /** + * @return RSAKey + */ + public static function createFromJWK(JWK $jwk): self + { + return new self($jwk); + } + + public function getModulus(): BigInteger + { + return $this->modulus; + } + + public function getModulusLength(): int + { + return $this->modulus_length; + } + + public function getExponent(): BigInteger + { + $d = $this->getPrivateExponent(); + if (null !== $d) { + return $d; + } + + return $this->getPublicExponent(); + } + + public function getPublicExponent(): BigInteger + { + return $this->public_exponent; + } + + public function getPrivateExponent(): ?BigInteger + { + return $this->private_exponent; + } + + /** + * @return BigInteger[] + */ + public function getPrimes(): array + { + return $this->primes; + } + + /** + * @return BigInteger[] + */ + public function getExponents(): array + { + return $this->exponents; + } + + public function getCoefficient(): ?BigInteger + { + return $this->coefficient; + } + + public function isPublic(): bool + { + return !array_key_exists('d', $this->values); + } + + /** + * @param RSAKey $private + * + * @return RSAKey + */ + public static function toPublic(self $private): self + { + $data = $private->toArray(); + $keys = ['p', 'd', 'q', 'dp', 'dq', 'qi']; + foreach ($keys as $key) { + if (array_key_exists($key, $data)) { + unset($data[$key]); + } + } + + return new self(new JWK($data)); + } + + public function toArray(): array + { + return $this->values; + } + + public function toPEM(): string + { + if (null === $this->sequence) { + $this->sequence = new Sequence(); + if (array_key_exists('d', $this->values)) { + $this->initPrivateKey(); + } else { + $this->initPublicKey(); + } + } + $result = '-----BEGIN '.($this->private ? 'RSA PRIVATE' : 'PUBLIC').' KEY-----'.PHP_EOL; + $result .= chunk_split(base64_encode($this->sequence->getBinary()), 64, PHP_EOL); + $result .= '-----END '.($this->private ? 'RSA PRIVATE' : 'PUBLIC').' KEY-----'.PHP_EOL; + + return $result; + } + + /** + * Exponentiate with or without Chinese Remainder Theorem. + * Operation with primes 'p' and 'q' is appox. 2x faster. + * + * @param RSAKey $key + * + * @throws RuntimeException if the exponentiation cannot be achieved + */ + public static function exponentiate(self $key, BigInteger $c): BigInteger + { + if ($c->compare(BigInteger::createFromDecimal(0)) < 0 || $c->compare($key->getModulus()) > 0) { + throw new RuntimeException(); + } + if ($key->isPublic() || null === $key->getCoefficient() || 0 === count($key->getPrimes()) || 0 === count($key->getExponents())) { + return $c->modPow($key->getExponent(), $key->getModulus()); + } + + $p = $key->getPrimes()[0]; + $q = $key->getPrimes()[1]; + $dP = $key->getExponents()[0]; + $dQ = $key->getExponents()[1]; + $qInv = $key->getCoefficient(); + + $m1 = $c->modPow($dP, $p); + $m2 = $c->modPow($dQ, $q); + $h = $qInv->multiply($m1->subtract($m2)->add($p))->mod($p); + + return $m2->add($h->multiply($q)); + } + + private function populateBigIntegers(): void + { + $this->modulus = $this->convertBase64StringToBigInteger($this->values['n']); + $this->modulus_length = mb_strlen($this->getModulus()->toBytes(), '8bit'); + $this->public_exponent = $this->convertBase64StringToBigInteger($this->values['e']); + + if (!$this->isPublic()) { + $this->private_exponent = $this->convertBase64StringToBigInteger($this->values['d']); + + if (array_key_exists('p', $this->values) && array_key_exists('q', $this->values)) { + $this->primes = [ + $this->convertBase64StringToBigInteger($this->values['p']), + $this->convertBase64StringToBigInteger($this->values['q']), + ]; + if (array_key_exists('dp', $this->values) && array_key_exists('dq', $this->values) && array_key_exists('qi', $this->values)) { + $this->exponents = [ + $this->convertBase64StringToBigInteger($this->values['dp']), + $this->convertBase64StringToBigInteger($this->values['dq']), + ]; + $this->coefficient = $this->convertBase64StringToBigInteger($this->values['qi']); + } + } + } + } + + private function convertBase64StringToBigInteger(string $value): BigInteger + { + return BigInteger::createFromBinaryString(Base64Url::decode($value)); + } + + private function initPublicKey(): void + { + $oid_sequence = new Sequence(); + $oid_sequence->addChild(new ObjectIdentifier('1.2.840.113549.1.1.1')); + $oid_sequence->addChild(new NullObject()); + $this->sequence->addChild($oid_sequence); + $n = new Integer($this->fromBase64ToInteger($this->values['n'])); + $e = new Integer($this->fromBase64ToInteger($this->values['e'])); + $key_sequence = new Sequence(); + $key_sequence->addChild($n); + $key_sequence->addChild($e); + $key_bit_string = new BitString(bin2hex($key_sequence->getBinary())); + $this->sequence->addChild($key_bit_string); + } + + private function initPrivateKey(): void + { + $this->sequence->addChild(new Integer(0)); + $oid_sequence = new Sequence(); + $oid_sequence->addChild(new ObjectIdentifier('1.2.840.113549.1.1.1')); + $oid_sequence->addChild(new NullObject()); + $this->sequence->addChild($oid_sequence); + $v = new Integer(0); + $n = new Integer($this->fromBase64ToInteger($this->values['n'])); + $e = new Integer($this->fromBase64ToInteger($this->values['e'])); + $d = new Integer($this->fromBase64ToInteger($this->values['d'])); + $p = new Integer($this->fromBase64ToInteger($this->values['p'])); + $q = new Integer($this->fromBase64ToInteger($this->values['q'])); + $dp = array_key_exists('dp', $this->values) ? new Integer($this->fromBase64ToInteger($this->values['dp'])) : new Integer(0); + $dq = array_key_exists('dq', $this->values) ? new Integer($this->fromBase64ToInteger($this->values['dq'])) : new Integer(0); + $qi = array_key_exists('qi', $this->values) ? new Integer($this->fromBase64ToInteger($this->values['qi'])) : new Integer(0); + $key_sequence = new Sequence(); + $key_sequence->addChild($v); + $key_sequence->addChild($n); + $key_sequence->addChild($e); + $key_sequence->addChild($d); + $key_sequence->addChild($p); + $key_sequence->addChild($q); + $key_sequence->addChild($dp); + $key_sequence->addChild($dq); + $key_sequence->addChild($qi); + $key_octet_string = new OctetString(bin2hex($key_sequence->getBinary())); + $this->sequence->addChild($key_octet_string); + } + + /** + * @param string $value + * + * @return string + */ + private function fromBase64ToInteger($value) + { + $hex = current(unpack('H*', Base64Url::decode($value))); + + return \Brick\Math\BigInteger::fromBase($hex, 16)->toBase(10); + } +} diff --git a/vendor/web-token/jwt-core/composer.json b/vendor/web-token/jwt-core/composer.json new file mode 100644 index 000000000..01e67c1ad --- /dev/null +++ b/vendor/web-token/jwt-core/composer.json @@ -0,0 +1,49 @@ +{ + "name": "web-token/jwt-core", + "description": "Core component of the JWT Framework.", + "type": "library", + "license": "MIT", + "keywords": ["JWS", "JWT", "JWE", "JWA", "JWK", "JWKSet", "Jot", "Jose", "RFC7515", "RFC7516", "RFC7517", "RFC7518", "RFC7519", "RFC7520", "Bundle", "Symfony"], + "homepage": "https://github.com/web-token", + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + },{ + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-framework/contributors" + } + ], + "autoload": { + "psr-4": { + "Jose\\Component\\Core\\": "" + } + }, + "require": { + "php": ">=7.2", + "ext-json": "*", + "ext-mbstring": "*", + "brick/math": "^0.8.17|^0.9", + "fgrosse/phpasn1": "^2.0", + "spomky-labs/base64url": "^1.0|^2.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.0" + }, + "conflict": { + "spomky-labs/jose": "*" + }, + "extra": { + "branch-alias": { + "v1.0": "1.0.x-dev", + "v1.1": "1.1.x-dev", + "v1.2": "1.2.x-dev", + "v1.3": "1.3.x-dev", + "v2.0": "2.0.x-dev", + "v2.1": "2.1.x-dev" + } + }, + "config": { + "sort-packages": true + } +} diff --git a/vendor/web-token/jwt-core/phpunit.xml.dist b/vendor/web-token/jwt-core/phpunit.xml.dist new file mode 100644 index 000000000..c8b3143f3 --- /dev/null +++ b/vendor/web-token/jwt-core/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + ./Tests/ + + + + + + ./ + + ./vendor + ./Tests + ./src + + + + diff --git a/vendor/web-token/jwt-easy/.github/CONTRIBUTING.md b/vendor/web-token/jwt-easy/.github/CONTRIBUTING.md new file mode 100644 index 000000000..fc360e5d8 --- /dev/null +++ b/vendor/web-token/jwt-easy/.github/CONTRIBUTING.md @@ -0,0 +1,4 @@ +# Contributing + +This repository is a sub repository of [the JWT Framework](https://github.com/web-token/jwt-framework) project and is READ ONLY. +Please do not submit any Pull Requests here. It will be automatically closed. diff --git a/vendor/web-token/jwt-easy/.github/FUNDING.yml b/vendor/web-token/jwt-easy/.github/FUNDING.yml new file mode 100644 index 000000000..7e2ca0e7e --- /dev/null +++ b/vendor/web-token/jwt-easy/.github/FUNDING.yml @@ -0,0 +1 @@ +patreon: FlorentMorselli diff --git a/vendor/web-token/jwt-easy/.github/PULL_REQUEST_TEMPLATE.md b/vendor/web-token/jwt-easy/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..d4ff96cb4 --- /dev/null +++ b/vendor/web-token/jwt-easy/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,3 @@ +Please do not submit any Pull Requests here. It will be automatically closed. + +You should submit it here: https://github.com/web-token/jwt-framework/pulls diff --git a/vendor/web-token/jwt-easy/AbstractBuilder.php b/vendor/web-token/jwt-easy/AbstractBuilder.php new file mode 100644 index 000000000..b3d2e44a3 --- /dev/null +++ b/vendor/web-token/jwt-easy/AbstractBuilder.php @@ -0,0 +1,157 @@ +jwt = new JWT(); + $this->algorithms = (new AlgorithmProvider($this->getAlgorithmMap())) + ->getAvailableAlgorithms() + ; + } + + public function payload(array $payload): self + { + $clone = clone $this; + $clone->jwt->claims->replace($payload); + + return $clone; + } + + public function iss(string $iss, bool $inHeader = false): self + { + return $this->claim('iss', $iss, $inHeader); + } + + public function sub(string $sub, bool $inHeader = false): self + { + return $this->claim('sub', $sub, $inHeader); + } + + public function aud(string $aud, bool $inHeader = false): self + { + $audience = $this->jwt->claims->has('aud') ? $this->jwt->claims->get('aud') : []; + $audience[] = $aud; + + return $this->claim('aud', $audience, $inHeader); + } + + public function jti(string $jti, bool $inHeader = false): self + { + return $this->claim('jti', $jti, $inHeader); + } + + public function exp(int $exp, bool $inHeader = false): self + { + return $this->claim('exp', $exp, $inHeader); + } + + public function iat(?int $iat = null, bool $inHeader = false): self + { + $iat = $iat ?? time(); + + return $this->claim('iat', $iat, $inHeader); + } + + public function nbf(?int $nbf = null, bool $inHeader = false): self + { + $nbf = $nbf ?? time(); + + return $this->claim('nbf', $nbf, $inHeader); + } + + /** + * @param Algorithm\SignatureAlgorithm|string $alg + * + * @throws InvalidArgumentException if the algorithm is not a string or an instance of Jose\Component\Core\Algorithm + */ + public function alg($alg): self + { + $clone = clone $this; + switch (true) { + case $alg instanceof JoseAlgorithm: + $clone->algorithms[] = $alg; + $clone->jwt->header->set('alg', $alg->name()); + + break; + case is_string($alg): + $clone->jwt->header->set('alg', $alg); + + break; + default: + throw new InvalidArgumentException('Invalid parameter "alg". Shall be a string or an algorithm instance.'); + } + + return $clone; + } + + public function cty(string $cty): self + { + return $this->header('cty', $cty); + } + + public function typ(string $typ): self + { + return $this->header('typ', $typ); + } + + public function crit(array $crit): self + { + return $this->header('crit', $crit); + } + + /** + * @param mixed $value + */ + public function claim(string $key, $value, bool $inHeader = false): self + { + $clone = clone $this; + $clone->jwt->claims->set($key, $value); + if ($inHeader) { + $clone->jwt->header->set($key, $value); + } + + return $clone; + } + + /** + * @param mixed $value + */ + public function header(string $key, $value): self + { + $clone = clone $this; + $clone->jwt->header->set($key, $value); + + return $clone; + } + + abstract protected function getAlgorithmMap(): array; +} diff --git a/vendor/web-token/jwt-easy/AbstractLoader.php b/vendor/web-token/jwt-easy/AbstractLoader.php new file mode 100644 index 000000000..7bcf01b2d --- /dev/null +++ b/vendor/web-token/jwt-easy/AbstractLoader.php @@ -0,0 +1,287 @@ +token = $token; + $this->jwkset = new JWKSet([]); + $this->claimCheckers = []; + + $this->algorithms = (new AlgorithmProvider($this->getAlgorithmMap())) + ->getAvailableAlgorithms() + ; + } + + /** + * @param string[] $mandatoryClaims + */ + public function mandatory(array $mandatoryClaims): self + { + $clone = clone $this; + $clone->mandatoryClaims = $mandatoryClaims; + + return $clone; + } + + public function aud(string $aud, bool $inHeader = false): self + { + return $this->claim('aud', new Checker\AudienceChecker($aud, true), $inHeader); + } + + public function iss(string $iss, bool $inHeader = false): self + { + return $this->claim('iss', new Checker\IssuerChecker([$iss], true), $inHeader); + } + + public function jti(string $jti, bool $inHeader = false): self + { + return $this->claim('jti', $jti, $inHeader); + } + + public function sub(string $sub, bool $inHeader = false): self + { + return $this->claim('sub', $sub, $inHeader); + } + + /** + * @param null|array|callable|Checker\ClaimChecker $checker + */ + public function claim(string $key, $checker, bool $inHeader = false): self + { + $clone = clone $this; + if (false === $checker) { + unset($clone->claimCheckers[$key]); + + return $clone; + } + + switch (true) { + case $checker instanceof Checker\ClaimChecker: + break; + case is_callable($checker): + $checker = new CallableChecker($key, $checker); + + break; + case is_array($checker): + $checker = new CallableChecker($key, static function ($value) use ($checker) {return in_array($value, $checker, true); }); + + break; + default: + $checker = new CallableChecker($key, static function ($value) use ($checker) {return $value === $checker; }); + } + + $clone->claimCheckers[$key] = $checker; + if ($inHeader) { + return $clone->header($key, $checker); + } + + return $clone; + } + + /** + * @param false|int $leeway + * + * @throws InvalidArgumentException if the leeway is negative, not an integer or not false + */ + public function exp($leeway = 0, bool $inHeader = false): self + { + if (false === $leeway) { + $clone = clone $this; + unset($clone->claimCheckers['exp']); + + return $clone; + } + if (!is_int($leeway) or $leeway < 0) { + throw new InvalidArgumentException('First parameter for "exp" claim is invalid. Set false to disable or a positive integer.'); + } + + return $this->claim('exp', new Checker\ExpirationTimeChecker($leeway), $inHeader); + } + + /** + * @param false|int $leeway + * + * @throws InvalidArgumentException if the leeway is negative, not an integer or not false + */ + public function nbf($leeway = 0, bool $inHeader = false): self + { + if (false === $leeway) { + $clone = clone $this; + unset($clone->claimCheckers['nbf']); + + return $clone; + } + if (!is_int($leeway) or $leeway < 0) { + throw new InvalidArgumentException('First parameter for "nbf" claim is invalid. Set false to disable or a positive integer.'); + } + + return $this->claim('nbf', new Checker\NotBeforeChecker($leeway, true), $inHeader); + } + + /** + * @param false|int $leeway + * + * @throws InvalidArgumentException if the leeway is negative, not an integer or not false + */ + public function iat($leeway = 0, bool $inHeader = false): self + { + if (false === $leeway) { + $clone = clone $this; + unset($clone->claimCheckers['iat']); + + return $clone; + } + if (!is_int($leeway) or $leeway < 0) { + throw new InvalidArgumentException('First parameter for "iat" claim is invalid. Set false to disable or a positive integer.'); + } + + return $this->claim('iat', new Checker\IssuedAtChecker($leeway, true), $inHeader); + } + + /** + * @param Algorithm|string $alg + * + * @throws InvalidArgumentException if the algorithm is not a string or an instance of Jose\Component\Core\Algorithm + */ + public function alg($alg): self + { + $clone = clone $this; + switch (true) { + case is_string($alg): + $clone->allowedAlgorithms[] = $alg; + + return $clone; + case $alg instanceof Algorithm: + $clone->algorithms[$alg->name()] = $alg; + $clone->allowedAlgorithms[] = $alg->name(); + + return $clone; + default: + throw new InvalidArgumentException('Invalid parameter "alg". Shall be a string or an algorithm instance.'); + } + } + + /** + * @param Algorithm[]|string[] $algs + */ + public function algs($algs): self + { + $clone = clone $this; + foreach ($algs as $alg) { + $clone = $clone->alg($alg); + } + + return $clone; + } + + /** + * @param array|callable|Checker\HeaderChecker|false|mixed $checker + */ + public function header(string $key, $checker): self + { + $clone = clone $this; + if (false === $checker) { + unset($clone->headerCheckers[$key]); + + return $clone; + } + + switch (true) { + case $checker instanceof Checker\HeaderChecker: + break; + case is_callable($checker): + $checker = new CallableChecker($key, $checker); + + break; + case is_array($checker): + $checker = new CallableChecker($key, static function ($value) use ($checker) {return in_array($value, $checker, true); }); + + break; + default: + $checker = new CallableChecker($key, static function ($value) use ($checker) {return $value === $checker; }); + } + + $clone->headerCheckers[$key] = $checker; + + return $clone; + } + + public function key(JWK $jwk): self + { + $clone = clone $this; + $jwkset = $this->jwkset->with($jwk); + $clone->jwkset = $jwkset; + + return $clone; + } + + public function keyset(JWKSet $jwkset): self + { + $clone = clone $this; + $clone->jwkset = $jwkset; + + return $clone; + } + + abstract protected function getAlgorithmMap(): array; +} diff --git a/vendor/web-token/jwt-easy/AlgorithmProvider.php b/vendor/web-token/jwt-easy/AlgorithmProvider.php new file mode 100644 index 000000000..3509a7eb0 --- /dev/null +++ b/vendor/web-token/jwt-easy/AlgorithmProvider.php @@ -0,0 +1,59 @@ +algorithmClasses = $algorithmClasses; + foreach ($algorithmClasses as $algorithmClass) { + $this->addClass($algorithmClass); + } + } + + public function getAlgorithmClasses(): array + { + return $this->algorithmClasses; + } + + public function getAvailableAlgorithms(): array + { + return $this->algorithms; + } + + private function addClass(string $algorithmClass): void + { + if (class_exists($algorithmClass)) { + try { + $this->algorithms[] = new $algorithmClass(); + } catch (Throwable $throwable) { + //does nothing + } + } + } +} diff --git a/vendor/web-token/jwt-easy/Build.php b/vendor/web-token/jwt-easy/Build.php new file mode 100644 index 000000000..c5971d739 --- /dev/null +++ b/vendor/web-token/jwt-easy/Build.php @@ -0,0 +1,27 @@ +key = $key; + $this->callable = $callable; + } + + /** + * @param mixed $value + * + * @throws InvalidClaimException if the claim is invalid + */ + public function checkClaim($value): void + { + $callable = $this->callable; + $isValid = $callable($value); + if (!$isValid) { + throw new InvalidClaimException(sprintf('Invalid claim "%s"', $this->key), $this->key, $value); + } + } + + public function supportedClaim(): string + { + return $this->key; + } + + /** + * {@inheritdoc} + */ + public function checkHeader($value): void + { + $callable = $this->callable; + $isValid = $callable($value); + if (!$isValid) { + throw new InvalidHeaderException(sprintf('Invalid header "%s"', $this->key), $this->key, $value); + } + } + + public function supportedHeader(): string + { + return $this->key; + } + + public function protectedHeaderOnly(): bool + { + return true; + } +} diff --git a/vendor/web-token/jwt-easy/ContentEncryptionAlgorithmChecker.php b/vendor/web-token/jwt-easy/ContentEncryptionAlgorithmChecker.php new file mode 100644 index 000000000..c7606cf5f --- /dev/null +++ b/vendor/web-token/jwt-easy/ContentEncryptionAlgorithmChecker.php @@ -0,0 +1,72 @@ +supportedAlgorithms = $supportedAlgorithms; + $this->protectedHeader = $protectedHeader; + } + + /** + * {@inheritdoc} + * + * @throws InvalidHeaderException if the header is invalid + */ + public function checkHeader($value): void + { + if (!is_string($value)) { + throw new InvalidHeaderException('"enc" must be a string.', self::HEADER_NAME, $value); + } + if (!in_array($value, $this->supportedAlgorithms, true)) { + throw new InvalidHeaderException('Unsupported algorithm.', self::HEADER_NAME, $value); + } + } + + public function supportedHeader(): string + { + return self::HEADER_NAME; + } + + public function protectedHeaderOnly(): bool + { + return $this->protectedHeader; + } +} diff --git a/vendor/web-token/jwt-easy/Decrypt.php b/vendor/web-token/jwt-easy/Decrypt.php new file mode 100644 index 000000000..368bc0d1a --- /dev/null +++ b/vendor/web-token/jwt-easy/Decrypt.php @@ -0,0 +1,149 @@ +compressionMethods = [ + new Deflate(), + ]; + } + + public static function token(string $token): self + { + return new self($token); + } + + /** + * @param Algorithm|string $enc + * + * @throws InvalidArgumentException if the encryption algorithm is invalid + */ + public function enc($enc): self + { + $clone = clone $this; + switch (true) { + case is_string($enc): + $clone->allowedContentEncryptionAlgorithms[] = $enc; + + return $clone; + case $enc instanceof Algorithm: + $clone->algorithms[$enc->name()] = $enc; + $clone->allowedContentEncryptionAlgorithms[] = $enc->name(); + + return $clone; + default: + throw new InvalidArgumentException('Invalid parameter "enc". Shall be a string or an algorithm instance.'); + } + } + + /** + * @param Algorithm[]|string[] $encs + */ + public function encs($encs): self + { + $clone = clone $this; + foreach ($encs as $enc) { + $clone = $clone->enc($enc); + } + + return $clone; + } + + public function run(): JWT + { + if (0 !== count($this->allowedAlgorithms)) { + $this->headerCheckers[] = new Checker\AlgorithmChecker($this->allowedAlgorithms, true); + } + if (0 !== count($this->allowedContentEncryptionAlgorithms)) { + $this->headerCheckers[] = new ContentEncryptionAlgorithmChecker($this->allowedContentEncryptionAlgorithms, true); + } + $jwe = (new CompactSerializer())->unserialize($this->token); + $headerChecker = new Checker\HeaderCheckerManager($this->headerCheckers, [new JWETokenSupport()]); + $headerChecker->check($jwe, 0); + + $verifier = new JWEDecrypter( + new AlgorithmManager($this->algorithms), + new AlgorithmManager($this->algorithms), + new CompressionMethodManager($this->compressionMethods) + ); + $verifier->decryptUsingKeySet($jwe, $this->jwkset, 0); + + $jwt = new JWT(); + $jwt->header->replace($jwe->getSharedProtectedHeader()); + $jwt->claims->replace(JsonConverter::decode($jwe->getPayload())); + + $claimChecker = new Checker\ClaimCheckerManager($this->claimCheckers); + $claimChecker->check($jwt->claims->all(), $this->mandatoryClaims); + + return $jwt; + } + + protected function getAlgorithmMap(): array + { + return [ + KeyEncryption\A128GCMKW::class, + KeyEncryption\A192GCMKW::class, + KeyEncryption\A256GCMKW::class, + KeyEncryption\A128KW::class, + KeyEncryption\A192KW::class, + KeyEncryption\A256KW::class, + KeyEncryption\Dir::class, + KeyEncryption\ECDHES::class, + KeyEncryption\ECDHESA128KW::class, + KeyEncryption\ECDHESA192KW::class, + KeyEncryption\ECDHESA256KW::class, + KeyEncryption\PBES2HS256A128KW::class, + KeyEncryption\PBES2HS384A192KW::class, + KeyEncryption\PBES2HS512A256KW::class, + KeyEncryption\RSA15::class, + KeyEncryption\RSAOAEP::class, + KeyEncryption\RSAOAEP256::class, + ContentEncryption\A128GCM::class, + ContentEncryption\A192GCM::class, + ContentEncryption\A256GCM::class, + ContentEncryption\A128CBCHS256::class, + ContentEncryption\A192CBCHS384::class, + ContentEncryption\A256CBCHS512::class, + ]; + } +} diff --git a/vendor/web-token/jwt-easy/JWEBuilder.php b/vendor/web-token/jwt-easy/JWEBuilder.php new file mode 100644 index 000000000..c176b6ee7 --- /dev/null +++ b/vendor/web-token/jwt-easy/JWEBuilder.php @@ -0,0 +1,141 @@ +compressionMethods = [ + new Deflate(), + ]; + } + + /** + * @param Algorithm|string $enc + * + * @throws InvalidArgumentException if the header parameter "enc" is invalid + */ + public function enc($enc): self + { + $clone = clone $this; + switch (true) { + case $enc instanceof Algorithm: + $clone->algorithms[] = $enc; + $clone->jwt->header->set('enc', $enc->name()); + + break; + case is_string($enc): + $clone->jwt->header->set('enc', $enc); + + break; + default: + throw new InvalidArgumentException('Invalid algorithm'); + } + + return $clone; + } + + /** + * @param CompressionMethod|string $zip + * + * @throws InvalidArgumentException if the header parameter "zip" is invalid + */ + public function zip($zip): self + { + $clone = clone $this; + switch (true) { + case $zip instanceof CompressionMethod: + $clone->compressionMethods[] = $zip; + $clone->jwt->header->set('zip', $zip->name()); + + break; + case is_string($zip): + $clone->jwt->header->set('zip', $zip); + + break; + default: + throw new InvalidArgumentException('Invalid compression method'); + } + + return $clone; + } + + public function encrypt(JWK $jwk): string + { + $builder = new JoseBuilder( + new AlgorithmManager($this->algorithms), + new AlgorithmManager($this->algorithms), + new CompressionMethodManager($this->compressionMethods) + ); + $jwe = $builder + ->create() + ->withPayload(JsonConverter::encode($this->jwt->claims->all())) + ->withSharedProtectedHeader($this->jwt->header->all()) + ->addRecipient($jwk) + ->build() + ; + + return (new CompactSerializer())->serialize($jwe); + } + + protected function getAlgorithmMap(): array + { + return [ + KeyEncryption\A128GCMKW::class, + KeyEncryption\A192GCMKW::class, + KeyEncryption\A256GCMKW::class, + KeyEncryption\A128KW::class, + KeyEncryption\A192KW::class, + KeyEncryption\A256KW::class, + KeyEncryption\Dir::class, + KeyEncryption\ECDHES::class, + KeyEncryption\ECDHESA128KW::class, + KeyEncryption\ECDHESA192KW::class, + KeyEncryption\ECDHESA256KW::class, + KeyEncryption\PBES2HS256A128KW::class, + KeyEncryption\PBES2HS384A192KW::class, + KeyEncryption\PBES2HS512A256KW::class, + KeyEncryption\RSA15::class, + KeyEncryption\RSAOAEP::class, + KeyEncryption\RSAOAEP256::class, + ContentEncryption\A128GCM::class, + ContentEncryption\A192GCM::class, + ContentEncryption\A256GCM::class, + ContentEncryption\A128CBCHS256::class, + ContentEncryption\A192CBCHS384::class, + ContentEncryption\A256CBCHS512::class, + ]; + } +} diff --git a/vendor/web-token/jwt-easy/JWSBuilder.php b/vendor/web-token/jwt-easy/JWSBuilder.php new file mode 100644 index 000000000..4f5c2f158 --- /dev/null +++ b/vendor/web-token/jwt-easy/JWSBuilder.php @@ -0,0 +1,56 @@ +algorithms)); + $jws = $builder + ->create() + ->withPayload(JsonConverter::encode($this->jwt->claims->all())) + ->addSignature($jwk, $this->jwt->header->all()) + ->build() + ; + + return (new CompactSerializer())->serialize($jws); + } + + protected function getAlgorithmMap(): array + { + return [ + Algorithm\HS256::class, + Algorithm\HS384::class, + Algorithm\HS512::class, + Algorithm\RS256::class, + Algorithm\RS384::class, + Algorithm\RS512::class, + Algorithm\PS256::class, + Algorithm\PS384::class, + Algorithm\PS512::class, + Algorithm\ES256::class, + Algorithm\ES384::class, + Algorithm\ES512::class, + Algorithm\EdDSA::class, + ]; + } +} diff --git a/vendor/web-token/jwt-easy/JWT.php b/vendor/web-token/jwt-easy/JWT.php new file mode 100644 index 000000000..c5f923554 --- /dev/null +++ b/vendor/web-token/jwt-easy/JWT.php @@ -0,0 +1,33 @@ +claims = new ParameterBag(); + $this->header = new ParameterBag(); + } +} diff --git a/vendor/web-token/jwt-easy/LICENSE b/vendor/web-token/jwt-easy/LICENSE new file mode 100644 index 000000000..37cf976b1 --- /dev/null +++ b/vendor/web-token/jwt-easy/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014-2019 Spomky-Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/web-token/jwt-easy/Load.php b/vendor/web-token/jwt-easy/Load.php new file mode 100644 index 000000000..5245ef6bc --- /dev/null +++ b/vendor/web-token/jwt-easy/Load.php @@ -0,0 +1,27 @@ +get($name); + } + array_unshift($arguments, $name); + + return call_user_func_array([$this, 'set'], $arguments); + } + + public function all(): array + { + return $this->parameters; + } + + public function keys(): array + { + return array_keys($this->parameters); + } + + public function replace(array $parameters): void + { + $this->parameters = $parameters; + } + + /** + * @throws InvalidArgumentException if the parameters are invalid + */ + public function add(array $parameters): void + { + $replaced = array_replace($this->parameters, $parameters); + if (null === $replaced) { + throw new InvalidArgumentException('Invalid parameters'); + } + $this->parameters = $replaced; + } + + /** + * @throws InvalidArgumentException if the selected parameter is missing + * + * @return mixed + */ + public function get(string $key) + { + if (!array_key_exists($key, $this->parameters)) { + throw new InvalidArgumentException(sprintf('Parameter "%s" is missing', $key)); + } + + return $this->parameters[$key]; + } + + /** + * @param mixed $value The value + */ + public function set(string $key, $value): void + { + $this->parameters[$key] = $value; + } + + public function has(string $key): bool + { + return array_key_exists($key, $this->parameters); + } + + public function remove(string $key): void + { + unset($this->parameters[$key]); + } + + public function getIterator(): ArrayIterator + { + return new ArrayIterator($this->parameters); + } + + public function count(): int + { + return count($this->parameters); + } +} diff --git a/vendor/web-token/jwt-easy/README.md b/vendor/web-token/jwt-easy/README.md new file mode 100644 index 000000000..dc2ff743f --- /dev/null +++ b/vendor/web-token/jwt-easy/README.md @@ -0,0 +1,15 @@ +Easy Toolset For JWT-Framework +=============================== + +This repository is a sub repository of [the JWT Framework](https://github.com/web-token/jwt-framework) project and is READ ONLY. + +**Please do not submit any Pull Request here.** +You should go to [the main repository](https://github.com/web-token/jwt-framework) instead. + +# Documentation + +The official documentation is available as https://web-token.spomky-labs.com/ + +# Licence + +This software is release under [MIT licence](LICENSE). diff --git a/vendor/web-token/jwt-easy/Tests/AlgorithmProviderTest.php b/vendor/web-token/jwt-easy/Tests/AlgorithmProviderTest.php new file mode 100644 index 000000000..db4f29e5e --- /dev/null +++ b/vendor/web-token/jwt-easy/Tests/AlgorithmProviderTest.php @@ -0,0 +1,168 @@ +getAlgorithmClasses()); + } + + /** + * @test + * + * @throws Exception + * @throws ExpectationFailedException + */ + public function itReturnsTheAvailableAlgorithms(): void + { + $algorithmProvider = new AlgorithmProvider(self::ALL_ALGORITHMS); + foreach ($algorithmProvider->getAvailableAlgorithms() as $algorithm) { + static::assertContains(get_class($algorithm), self::ALL_ALGORITHMS); + } + } + + /** + * @test + * + * @throws ExpectationFailedException + * @throws \Exception + */ + public function itAllowsNonExistingClasses(): void + { + $nonExistingClassName = 'NonExistingClass'.bin2hex(random_bytes(31)); + $algorithmProvider = new AlgorithmProvider([$nonExistingClassName]); + + static::assertSame([$nonExistingClassName], $algorithmProvider->getAlgorithmClasses()); + static::assertSame([], $algorithmProvider->getAvailableAlgorithms()); + } + + /** + * @test + * + * @throws ExpectationFailedException + */ + public function itCanHandleClassesWithExceptions(): void + { + $test = [$this->createAlgorithmClassWithExceptionMock()]; + $algorithmProvider = new AlgorithmProvider($test); + + static::assertSame($test, $algorithmProvider->getAlgorithmClasses()); + static::assertSame([], $algorithmProvider->getAvailableAlgorithms()); + } + + private function createAlgorithmClassWithExceptionMock(): string + { + $mockClass = new class() implements Algorithm\SignatureAlgorithm { + /** @var bool */ + private static $throw; + + public function __construct() + { + if (null === self::$throw) { + self::$throw = true; + + return; + } + + throw new BadFunctionCallException('should not be called'); + } + + public function name(): string + { + throw new BadFunctionCallException('should not be called'); + } + + public function allowedKeyTypes(): array + { + throw new BadFunctionCallException('should not be called'); + } + + public function sign(JWK $key, string $input): string + { + throw new BadFunctionCallException('should not be called'); + } + + public function verify(JWK $key, string $input, string $signature): bool + { + throw new BadFunctionCallException('should not be called'); + } + }; + + return get_class($mockClass); + } +} diff --git a/vendor/web-token/jwt-easy/Tests/EncryptionTest.php b/vendor/web-token/jwt-easy/Tests/EncryptionTest.php new file mode 100644 index 000000000..5af575f28 --- /dev/null +++ b/vendor/web-token/jwt-easy/Tests/EncryptionTest.php @@ -0,0 +1,141 @@ +exp($time + 3600) + ->iat($time) + ->nbf($time) + ->jti('0123456789', true) + ->iss('issuer') + ->aud('audience1') + ->aud('audience2') + ->sub('subject') + ->alg('RSA-OAEP-256') + ->enc('A256GCM') + ->zip('DEF') + ->claim('is_root', true) + ->claim('roles', ['ROLE1' => true, 'ROLE2' => 0.007]) + ->crit(['alg', 'enc']) + ->encrypt($this->rsaKey()) + ; + + $jwt = Load::jwe($jwe) + ->algs(['RSA-OAEP', 'RSA-OAEP-256']) + ->encs(['A128GCM', 'A256GCM']) + ->exp() + ->iat() + ->nbf() + ->aud('audience1') + ->iss('issuer') + ->sub('subject') + ->jti('0123456789') + ->key($this->rsaKey()) + ->run() + ; + + static::assertEquals($time, $jwt->claims->iat()); + static::assertEquals($time, $jwt->claims->nbf()); + static::assertEquals($time + 3600, $jwt->claims->exp()); + static::assertEquals('0123456789', $jwt->claims->jti()); + static::assertEquals('issuer', $jwt->claims->iss()); + static::assertEquals('subject', $jwt->claims->sub()); + static::assertEquals(['audience1', 'audience2'], $jwt->claims->aud()); + static::assertEquals(true, $jwt->claims->is_root()); + static::assertEquals(['ROLE1' => true, 'ROLE2' => 0.007], $jwt->claims->roles()); + + static::assertEquals(['jti' => '0123456789', 'alg' => 'RSA-OAEP-256', 'enc' => 'A256GCM', 'crit' => ['alg', 'enc'], 'zip' => 'DEF'], $jwt->header->all()); + static::assertEquals('RSA-OAEP-256', $jwt->header->alg()); + static::assertEquals('A256GCM', $jwt->header->enc()); + static::assertEquals('0123456789', $jwt->header->jti()); + } + + /** + * @test + */ + public function jweCanBeCreatedWithCustomAlgorithm(): void + { + $time = time(); + $jwe = Build::jwe() + ->exp($time + 3600) + ->iat($time) + ->nbf($time) + ->jti('0123456789') + ->alg(new RSAOAEP512()) + ->enc(new A256CCM_16_128()) + ->encrypt($this->rsaKey()) + ; + + $jwt = Load::jwe($jwe) + ->algs(['RSA-OAEP', new RSAOAEP512()]) + ->encs(['A128GCM', new A256CCM_16_128()]) + ->exp() + ->iat() + ->nbf() + ->aud('audience1') + ->iss('issuer') + ->sub('subject') + ->jti('0123456789') + ->key($this->rsaKey()) + ->run() + ; + static::assertEquals($time, $jwt->claims->iat()); + static::assertEquals($time, $jwt->claims->nbf()); + static::assertEquals($time + 3600, $jwt->claims->exp()); + static::assertEquals('0123456789', $jwt->claims->jti()); + + static::assertEquals('RSA-OAEP-512', $jwt->header->alg()); + static::assertEquals('A256CCM-16-128', $jwt->header->enc()); + } + + private function rsaKey(): JWK + { + return new JWK([ + 'kty' => 'RSA', + 'kid' => 'bilbo.baggins@hobbiton.example', + 'use' => 'enc', + 'n' => 'n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw', + 'e' => 'AQAB', + 'd' => 'bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78eiZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRldY7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-bMwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDjd18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOcOpBrQzwQ', + 'p' => '3Slxg_DwTXJcb6095RoXygQCAZ5RnAvZlno1yhHtnUex_fp7AZ_9nRaO7HX_-SFfGQeutao2TDjDAWU4Vupk8rw9JR0AzZ0N2fvuIAmr_WCsmGpeNqQnev1T7IyEsnh8UMt-n5CafhkikzhEsrmndH6LxOrvRJlsPp6Zv8bUq0k', + 'q' => 'uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc', + 'dp' => 'B8PVvXkvJrj2L-GYQ7v3y9r6Kw5g9SahXBwsWUzp19TVlgI-YV85q1NIb1rxQtD-IsXXR3-TanevuRPRt5OBOdiMGQp8pbt26gljYfKU_E9xn-RULHz0-ed9E9gXLKD4VGngpz-PfQ_q29pk5xWHoJp009Qf1HvChixRX59ehik', + 'dq' => 'CLDmDGduhylc9o7r84rEUVn7pzQ6PF83Y-iBZx5NT-TpnOZKF1pErAMVeKzFEl41DlHHqqBLSM0W1sOFbwTxYWZDm6sI6og5iTbwQGIC3gnJKbi_7k_vJgGHwHxgPaX2PnvP-zyEkDERuf-ry4c_Z11Cq9AqC2yeL6kdKT1cYF8', + 'qi' => '3PiqvXQN0zwMeE-sBvZgi289XP9XCQF3VWqPzMKnIgQp7_Tugo6-NZBKCQsMf3HaEGBjTVJs_jcK8-TRXvaKe-7ZMaQj8VfBdYkssbu0NKDDhjJ-GtiseaDVWt7dcH0cfwxgFUHpQh7FoCrjFJ6h6ZEpMF6xmujs4qMpPz8aaI4', + ]); + } +} diff --git a/vendor/web-token/jwt-easy/Tests/ParameterBagTest.php b/vendor/web-token/jwt-easy/Tests/ParameterBagTest.php new file mode 100644 index 000000000..afb47d351 --- /dev/null +++ b/vendor/web-token/jwt-easy/Tests/ParameterBagTest.php @@ -0,0 +1,41 @@ +iss('iss'); + $bag->alg('alg'); + $bag->aud(['aud']); + + static::assertEquals(['aud'], $bag->aud()); + static::assertEquals('iss', $bag->get('iss')); + static::assertEquals('alg', $bag->get('alg')); + } +} diff --git a/vendor/web-token/jwt-easy/Tests/SignatureTest.php b/vendor/web-token/jwt-easy/Tests/SignatureTest.php new file mode 100644 index 000000000..14bf5af0b --- /dev/null +++ b/vendor/web-token/jwt-easy/Tests/SignatureTest.php @@ -0,0 +1,268 @@ +exp($time + 3600) + ->iat($time) + ->nbf($time) + ->jti('0123456789', true) + ->alg('RS512') + ->iss('issuer') + ->aud('audience1') + ->aud('audience2') + ->sub('subject') + ->claim('is_root', true) + ->claim('roles', ['ROLE1' => true, 'ROLE2' => 0.007]) + ->crit(['alg']) + ->sign($this->rsaKey()) + ; + + $jwt = Load::jws($jws) + ->algs(['RS256', 'RS512']) + ->exp() + ->iat() + ->nbf() + ->aud('audience1') + ->iss('issuer') + ->sub('subject') + ->jti('0123456789') + ->key($this->rsaKey()) + ->run() + ; + + static::assertEquals($time, $jwt->claims->iat()); + static::assertEquals($time, $jwt->claims->nbf()); + static::assertEquals($time + 3600, $jwt->claims->exp()); + static::assertEquals('0123456789', $jwt->claims->jti()); + static::assertEquals('issuer', $jwt->claims->iss()); + static::assertEquals('subject', $jwt->claims->sub()); + static::assertEquals(['audience1', 'audience2'], $jwt->claims->aud()); + static::assertEquals(true, $jwt->claims->is_root()); + static::assertEquals(['ROLE1' => true, 'ROLE2' => 0.007], $jwt->claims->roles()); + + static::assertEquals(['jti' => '0123456789', 'alg' => 'RS512', 'crit' => ['alg']], $jwt->header->all()); + static::assertEquals('RS512', $jwt->header->alg()); + static::assertEquals('0123456789', $jwt->header->jti()); + } + + /** + * @test + */ + public function invalidSignatureRejectsTheToken(): void + { + $this->expectException(Exception::class); + $this->expectExceptionMessage('Invalid signature'); + $time = time(); + $jws = Build::jws() + ->exp($time + 3600) + ->iat($time) + ->nbf($time) + ->jti('0123456789', true) + ->alg('HS256') + ->iss('issuer') + ->aud('audience1') + ->aud('audience2') + ->sub('subject') + ->claim('is_root', true) + ->claim('roles', ['ROLE1' => true, 'ROLE2' => 0.007]) + ->crit(['alg']) + ->sign(new JWK(['kty' => 'oct', 'k' => 'foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo'])) + ; + + Load::jws($jws) + ->algs(['HS256']) + ->exp() + ->iat() + ->nbf() + ->aud('audience1') + ->iss('issuer') + ->sub('subject') + ->jti('0123456789') + ->key(new JWK(['kty' => 'oct', 'k' => 'BARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBAR'])) + ->run() + ; + } + + /** + * @test + */ + public function algorithmIsNotAllowed(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The algorithm "none" is not supported.'); + $time = time(); + $jws = Build::jws() + ->exp($time + 3600) + ->iat($time) + ->nbf($time) + ->jti('0123456789', true) + ->alg('none') + ->iss('issuer') + ->aud('audience1') + ->aud('audience2') + ->sub('subject') + ->claim('is_root', true) + ->claim('roles', ['ROLE1' => true, 'ROLE2' => 0.007]) + ->crit(['alg']) + ->sign($this->noneKey()) + ; + + Load::jws($jws) + ->algs(['HS256']) + ->exp() + ->iat() + ->nbf() + ->aud('audience1') + ->iss('issuer') + ->sub('subject') + ->jti('0123456789') + ->key(new JWK(['kty' => 'oct', 'k' => 'BARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBAR'])) + ->run() + ; + } + + /** + * @test + */ + public function tokenExpired(): void + { + $this->expectException(InvalidClaimException::class); + $this->expectExceptionMessage('The token expired.'); + $time = time(); + $jws = Build::jws() + ->exp($time - 1) + ->iat($time) + ->nbf($time) + ->jti('0123456789', true) + ->alg('RS256') + ->iss('issuer') + ->aud('audience1') + ->aud('audience2') + ->sub('subject') + ->claim('is_root', true) + ->claim('roles', ['ROLE1' => true, 'ROLE2' => 0.007]) + ->crit(['alg']) + ->sign($this->rsaKey()) + ; + + Load::jws($jws) + ->algs(['RS256']) + ->exp() + ->iat() + ->nbf() + ->aud('audience1') + ->iss('issuer') + ->sub('subject') + ->jti('0123456789') + ->key($this->rsaKey()) + ->run() + ; + } + + /** + * @test + */ + public function jwsCanBeCreatedWithCustomAlgorithm(): void + { + $time = time(); + $jws = Build::jws() + ->exp($time + 3600) + ->iat($time) + ->nbf($time) + ->jti('0123456789') + ->alg(new HS1()) + ->sign($this->octKey()) + ; + + $jwt = Load::jws($jws) + ->algs(['RS256', new HS1()]) + ->exp() + ->iat() + ->nbf() + ->aud('audience1') + ->iss('issuer') + ->sub('subject') + ->jti('0123456789') + ->key($this->octKey()) + ->run() + ; + + static::assertEquals($time, $jwt->claims->iat()); + static::assertEquals($time, $jwt->claims->nbf()); + static::assertEquals($time + 3600, $jwt->claims->exp()); + static::assertEquals('0123456789', $jwt->claims->jti()); + + static::assertEquals('HS1', $jwt->header->alg()); + } + + private function rsaKey(): JWK + { + return new JWK([ + 'kty' => 'RSA', + 'kid' => 'bilbo.baggins@hobbiton.example', + 'use' => 'sig', + 'n' => 'n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw', + 'e' => 'AQAB', + 'd' => 'bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78eiZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRldY7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-bMwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDjd18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOcOpBrQzwQ', + 'p' => '3Slxg_DwTXJcb6095RoXygQCAZ5RnAvZlno1yhHtnUex_fp7AZ_9nRaO7HX_-SFfGQeutao2TDjDAWU4Vupk8rw9JR0AzZ0N2fvuIAmr_WCsmGpeNqQnev1T7IyEsnh8UMt-n5CafhkikzhEsrmndH6LxOrvRJlsPp6Zv8bUq0k', + 'q' => 'uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc', + 'dp' => 'B8PVvXkvJrj2L-GYQ7v3y9r6Kw5g9SahXBwsWUzp19TVlgI-YV85q1NIb1rxQtD-IsXXR3-TanevuRPRt5OBOdiMGQp8pbt26gljYfKU_E9xn-RULHz0-ed9E9gXLKD4VGngpz-PfQ_q29pk5xWHoJp009Qf1HvChixRX59ehik', + 'dq' => 'CLDmDGduhylc9o7r84rEUVn7pzQ6PF83Y-iBZx5NT-TpnOZKF1pErAMVeKzFEl41DlHHqqBLSM0W1sOFbwTxYWZDm6sI6og5iTbwQGIC3gnJKbi_7k_vJgGHwHxgPaX2PnvP-zyEkDERuf-ry4c_Z11Cq9AqC2yeL6kdKT1cYF8', + 'qi' => '3PiqvXQN0zwMeE-sBvZgi289XP9XCQF3VWqPzMKnIgQp7_Tugo6-NZBKCQsMf3HaEGBjTVJs_jcK8-TRXvaKe-7ZMaQj8VfBdYkssbu0NKDDhjJ-GtiseaDVWt7dcH0cfwxgFUHpQh7FoCrjFJ6h6ZEpMF6xmujs4qMpPz8aaI4', + ]); + } + + private function octKey(): JWK + { + return new JWK([ + 'kty' => 'oct', + 'k' => 'foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo', + ]); + } + + private function noneKey(): JWK + { + return new JWK([ + 'kty' => 'none', + ]); + } +} diff --git a/vendor/web-token/jwt-easy/Validate.php b/vendor/web-token/jwt-easy/Validate.php new file mode 100644 index 000000000..0b3736a82 --- /dev/null +++ b/vendor/web-token/jwt-easy/Validate.php @@ -0,0 +1,75 @@ +allowedAlgorithms)) { + $this->headerCheckers[] = new Checker\AlgorithmChecker($this->allowedAlgorithms, true); + } + $jws = (new CompactSerializer())->unserialize($this->token); + $headerChecker = new Checker\HeaderCheckerManager($this->headerCheckers, [new JWSTokenSupport()]); + $headerChecker->check($jws, 0); + + $verifier = new JWSVerifier(new AlgorithmManager($this->algorithms)); + if (!$verifier->verifyWithKeySet($jws, $this->jwkset, 0)) { + throw new Exception('Invalid signature'); + } + + $jwt = new JWT(); + $jwt->header->replace($jws->getSignature(0)->getProtectedHeader()); + $jwt->claims->replace(JsonConverter::decode($jws->getPayload())); + + $claimChecker = new Checker\ClaimCheckerManager($this->claimCheckers); + $claimChecker->check($jwt->claims->all(), $this->mandatoryClaims); + + return $jwt; + } + + protected function getAlgorithmMap(): array + { + return [ + Algorithm\HS256::class, + Algorithm\HS384::class, + Algorithm\HS512::class, + Algorithm\RS256::class, + Algorithm\RS384::class, + Algorithm\RS512::class, + Algorithm\PS256::class, + Algorithm\PS384::class, + Algorithm\PS512::class, + Algorithm\ES256::class, + Algorithm\ES384::class, + Algorithm\ES512::class, + Algorithm\EdDSA::class, + ]; + } +} diff --git a/vendor/web-token/jwt-easy/composer.json b/vendor/web-token/jwt-easy/composer.json new file mode 100644 index 000000000..aec670ec5 --- /dev/null +++ b/vendor/web-token/jwt-easy/composer.json @@ -0,0 +1,57 @@ +{ + "name": "web-token/jwt-easy", + "description": "Easy toolset to use the JWT Framework.", + "type": "library", + "license": "MIT", + "keywords": ["JWS", "JWT", "JWE", "JWA", "JWK", "JWKSet", "Jot", "Jose", "RFC7515", "RFC7516", "RFC7517", "RFC7518", "RFC7519", "RFC7520", "Bundle", "Symfony"], + "homepage": "https://github.com/web-token", + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + },{ + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-framework/contributors" + } + ], + "autoload": { + "psr-4": { + "Jose\\Easy\\": "" + } + }, + "require": { + "web-token/jwt-encryption": "^2.1", + "web-token/jwt-signature": "^2.1", + "web-token/jwt-checker": "^2.1" + }, + "suggest": { + "web-token/jwt-encryption-algorithm-aescbc": "Adds AES-CBC based encryption algorithms", + "web-token/jwt-encryption-algorithm-aesgcm": "Adds AES-GCM based encryption algorithms", + "web-token/jwt-encryption-algorithm-aesgcmkw": "Adds AES-GCM Key Wrapping based encryption algorithms", + "web-token/jwt-encryption-algorithm-aeskw": "Adds AES Key Wrapping based encryption algorithms", + "web-token/jwt-encryption-algorithm-dir": "Adds Direct encryption algorithm", + "web-token/jwt-encryption-algorithm-ecdh-es": "Adds ECDH-ES based encryption algorithms", + "web-token/jwt-encryption-algorithm-pbes2": "Adds PBES2 based encryption algorithms", + "web-token/jwt-encryption-algorithm-rsa": "Adds RSA based encryption algorithms", + "web-token/jwt-signature-algorithm-ecdsa": "Adds ECDSA based signature algorithms", + "web-token/jwt-signature-algorithm-eddsa": "Adds EdDSA based signature algorithms", + "web-token/jwt-signature-algorithm-none": "Adds none signature algorithms", + "web-token/jwt-signature-algorithm-hmac": "Adds HMAC based signature algorithms", + "web-token/jwt-signature-algorithm-rsa": "Adds RSA based signature algorithms" + }, + "require-dev": { + "phpunit/phpunit": "^8.0" + }, + "extra": { + "branch-alias": { + "v1.0": "1.0.x-dev", + "v1.1": "1.1.x-dev", + "v1.2": "1.2.x-dev", + "v1.3": "1.3.x-dev", + "v2.0": "2.0.x-dev" + } + }, + "config": { + "sort-packages": true + } +} diff --git a/vendor/web-token/jwt-easy/phpunit.xml.dist b/vendor/web-token/jwt-easy/phpunit.xml.dist new file mode 100644 index 000000000..c8b3143f3 --- /dev/null +++ b/vendor/web-token/jwt-easy/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + ./Tests/ + + + + + + ./ + + ./vendor + ./Tests + ./src + + + + diff --git a/vendor/web-token/jwt-encryption/.github/CONTRIBUTING.md b/vendor/web-token/jwt-encryption/.github/CONTRIBUTING.md new file mode 100644 index 000000000..fc360e5d8 --- /dev/null +++ b/vendor/web-token/jwt-encryption/.github/CONTRIBUTING.md @@ -0,0 +1,4 @@ +# Contributing + +This repository is a sub repository of [the JWT Framework](https://github.com/web-token/jwt-framework) project and is READ ONLY. +Please do not submit any Pull Requests here. It will be automatically closed. diff --git a/vendor/web-token/jwt-encryption/.github/FUNDING.yml b/vendor/web-token/jwt-encryption/.github/FUNDING.yml new file mode 100644 index 000000000..7e2ca0e7e --- /dev/null +++ b/vendor/web-token/jwt-encryption/.github/FUNDING.yml @@ -0,0 +1 @@ +patreon: FlorentMorselli diff --git a/vendor/web-token/jwt-encryption/.github/PULL_REQUEST_TEMPLATE.md b/vendor/web-token/jwt-encryption/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..d4ff96cb4 --- /dev/null +++ b/vendor/web-token/jwt-encryption/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,3 @@ +Please do not submit any Pull Requests here. It will be automatically closed. + +You should submit it here: https://github.com/web-token/jwt-framework/pulls diff --git a/vendor/web-token/jwt-encryption/Algorithm/ContentEncryptionAlgorithm.php b/vendor/web-token/jwt-encryption/Algorithm/ContentEncryptionAlgorithm.php new file mode 100644 index 000000000..f5047ee30 --- /dev/null +++ b/vendor/web-token/jwt-encryption/Algorithm/ContentEncryptionAlgorithm.php @@ -0,0 +1,54 @@ +add($method); + } + } + + /** + * Returns true if the givn compression method is supported. + */ + public function has(string $name): bool + { + return array_key_exists($name, $this->compressionMethods); + } + + /** + * This method returns the compression method with the given name. + * Throws an exception if the method is not supported. + * + * @param string $name The name of the compression method + * + * @throws InvalidArgumentException if the compression method is not supported + */ + public function get(string $name): CompressionMethod + { + if (!$this->has($name)) { + throw new InvalidArgumentException(sprintf('The compression method "%s" is not supported.', $name)); + } + + return $this->compressionMethods[$name]; + } + + /** + * Returns the list of compression method names supported by the manager. + * + * @return string[] + */ + public function list(): array + { + return array_keys($this->compressionMethods); + } + + /** + * Add the given compression method to the manager. + */ + protected function add(CompressionMethod $compressionMethod): void + { + $name = $compressionMethod->name(); + $this->compressionMethods[$name] = $compressionMethod; + } +} diff --git a/vendor/web-token/jwt-encryption/Compression/CompressionMethodManagerFactory.php b/vendor/web-token/jwt-encryption/Compression/CompressionMethodManagerFactory.php new file mode 100644 index 000000000..869da3d35 --- /dev/null +++ b/vendor/web-token/jwt-encryption/Compression/CompressionMethodManagerFactory.php @@ -0,0 +1,75 @@ +compressionMethods[$alias] = $compressionMethod; + } + + /** + * Returns the list of compression method aliases supported by the factory. + * + * @return string[] + */ + public function aliases(): array + { + return array_keys($this->compressionMethods); + } + + /** + * Returns all compression methods supported by this factory. + * + * @return CompressionMethod[] + */ + public function all(): array + { + return $this->compressionMethods; + } + + /** + * Creates a compression method manager using the compression methods identified by the given aliases. + * If one of the aliases does not exist, an exception is thrown. + * + * @param string[] $aliases + * + * @throws InvalidArgumentException if the compression method alias is not supported + */ + public function create(array $aliases): CompressionMethodManager + { + $compressionMethods = []; + foreach ($aliases as $alias) { + if (!isset($this->compressionMethods[$alias])) { + throw new InvalidArgumentException(sprintf('The compression method with the alias "%s" is not supported.', $alias)); + } + $compressionMethods[] = $this->compressionMethods[$alias]; + } + + return new CompressionMethodManager($compressionMethods); + } +} diff --git a/vendor/web-token/jwt-encryption/Compression/Deflate.php b/vendor/web-token/jwt-encryption/Compression/Deflate.php new file mode 100644 index 000000000..fdfaf580e --- /dev/null +++ b/vendor/web-token/jwt-encryption/Compression/Deflate.php @@ -0,0 +1,72 @@ + 9) { + throw new InvalidArgumentException('The compression level can be given as 0 for no compression up to 9 for maximum compression. If -1 given, the default compression level will be the default compression level of the zlib library.'); + } + $this->compressionLevel = $compressionLevel; + } + + public function name(): string + { + return 'DEF'; + } + + /** + * @throws InvalidArgumentException if the compression failed + */ + public function compress(string $data): string + { + try { + return gzdeflate($data, $this->getCompressionLevel()); + } catch (Throwable $throwable) { + throw new InvalidArgumentException('Unable to compress data.', $throwable->getCode(), $throwable); + } + } + + /** + * @throws InvalidArgumentException if the decompression failed + */ + public function uncompress(string $data): string + { + try { + return gzinflate($data); + } catch (Throwable $throwable) { + throw new InvalidArgumentException('Unable to uncompress data.', $throwable->getCode(), $throwable); + } + } + + private function getCompressionLevel(): int + { + return $this->compressionLevel; + } +} diff --git a/vendor/web-token/jwt-encryption/JWE.php b/vendor/web-token/jwt-encryption/JWE.php new file mode 100644 index 000000000..ed1bc23e8 --- /dev/null +++ b/vendor/web-token/jwt-encryption/JWE.php @@ -0,0 +1,279 @@ +ciphertext = $ciphertext; + $this->iv = $iv; + $this->aad = $aad; + $this->tag = $tag; + $this->sharedHeader = $sharedHeader; + $this->sharedProtectedHeader = $sharedProtectedHeader; + $this->encodedSharedProtectedHeader = $encodedSharedProtectedHeader; + $this->recipients = $recipients; + } + + public function getPayload(): ?string + { + return $this->payload; + } + + /** + * Set the payload. + * This method is immutable and a new object will be returned. + * + * @return JWE + */ + public function withPayload(string $payload): self + { + $clone = clone $this; + $clone->payload = $payload; + + return $clone; + } + + /** + * Returns the number of recipients associated with the JWS. + */ + public function countRecipients(): int + { + return count($this->recipients); + } + + /** + * Returns true is the JWE has already been encrypted. + */ + public function isEncrypted(): bool + { + return null !== $this->getCiphertext(); + } + + /** + * Returns the recipients associated with the JWS. + * + * @return Recipient[] + */ + public function getRecipients(): array + { + return $this->recipients; + } + + /** + * Returns the recipient object at the given index. + * + * @throws InvalidArgumentException if the recipient ID does not exist + */ + public function getRecipient(int $id): Recipient + { + if (!isset($this->recipients[$id])) { + throw new InvalidArgumentException('The recipient does not exist.'); + } + + return $this->recipients[$id]; + } + + /** + * Returns the ciphertext. This method will return null is the JWE has not yet been encrypted. + * + * @return null|string The cyphertext + */ + public function getCiphertext(): ?string + { + return $this->ciphertext; + } + + /** + * Returns the Additional Authentication Data if available. + */ + public function getAAD(): ?string + { + return $this->aad; + } + + /** + * Returns the Initialization Vector if available. + */ + public function getIV(): ?string + { + return $this->iv; + } + + /** + * Returns the tag if available. + */ + public function getTag(): ?string + { + return $this->tag; + } + + /** + * Returns the encoded shared protected header. + */ + public function getEncodedSharedProtectedHeader(): string + { + return $this->encodedSharedProtectedHeader ?? ''; + } + + /** + * Returns the shared protected header. + */ + public function getSharedProtectedHeader(): array + { + return $this->sharedProtectedHeader; + } + + /** + * Returns the shared protected header parameter identified by the given key. + * Throws an exception is the the parameter is not available. + * + * @param string $key The key + * + * @throws InvalidArgumentException if the shared protected header parameter does not exist + * + * @return null|mixed + */ + public function getSharedProtectedHeaderParameter(string $key) + { + if (!$this->hasSharedProtectedHeaderParameter($key)) { + throw new InvalidArgumentException(sprintf('The shared protected header "%s" does not exist.', $key)); + } + + return $this->sharedProtectedHeader[$key]; + } + + /** + * Returns true if the shared protected header has the parameter identified by the given key. + * + * @param string $key The key + */ + public function hasSharedProtectedHeaderParameter(string $key): bool + { + return array_key_exists($key, $this->sharedProtectedHeader); + } + + /** + * Returns the shared header. + */ + public function getSharedHeader(): array + { + return $this->sharedHeader; + } + + /** + * Returns the shared header parameter identified by the given key. + * Throws an exception is the the parameter is not available. + * + * @param string $key The key + * + * @throws InvalidArgumentException if the shared header parameter does not exist + * + * @return null|mixed + */ + public function getSharedHeaderParameter(string $key) + { + if (!$this->hasSharedHeaderParameter($key)) { + throw new InvalidArgumentException(sprintf('The shared header "%s" does not exist.', $key)); + } + + return $this->sharedHeader[$key]; + } + + /** + * Returns true if the shared header has the parameter identified by the given key. + * + * @param string $key The key + */ + public function hasSharedHeaderParameter(string $key): bool + { + return array_key_exists($key, $this->sharedHeader); + } + + /** + * This method splits the JWE into a list of JWEs. + * It is only useful when the JWE contains more than one recipient (JSON General Serialization). + * + * @return JWE[] + */ + public function split(): array + { + $result = []; + foreach ($this->recipients as $recipient) { + $result[] = new self( + $this->ciphertext, + $this->iv, + $this->tag, + $this->aad, + $this->sharedHeader, + $this->sharedProtectedHeader, + $this->encodedSharedProtectedHeader, + [$recipient] + ); + } + + return $result; + } +} diff --git a/vendor/web-token/jwt-encryption/JWEBuilder.php b/vendor/web-token/jwt-encryption/JWEBuilder.php new file mode 100644 index 000000000..32be4c0fe --- /dev/null +++ b/vendor/web-token/jwt-encryption/JWEBuilder.php @@ -0,0 +1,535 @@ +keyEncryptionAlgorithmManager = $keyEncryptionAlgorithmManager; + $this->contentEncryptionAlgorithmManager = $contentEncryptionAlgorithmManager; + $this->compressionManager = $compressionManager; + } + + /** + * Reset the current data. + * + * @return JWEBuilder + */ + public function create(): self + { + $this->payload = null; + $this->aad = null; + $this->recipients = []; + $this->sharedProtectedHeader = []; + $this->sharedHeader = []; + $this->compressionMethod = null; + $this->contentEncryptionAlgorithm = null; + $this->keyManagementMode = null; + + return $this; + } + + /** + * Returns the key encryption algorithm manager. + */ + public function getKeyEncryptionAlgorithmManager(): AlgorithmManager + { + return $this->keyEncryptionAlgorithmManager; + } + + /** + * Returns the content encryption algorithm manager. + */ + public function getContentEncryptionAlgorithmManager(): AlgorithmManager + { + return $this->contentEncryptionAlgorithmManager; + } + + /** + * Returns the compression method manager. + */ + public function getCompressionMethodManager(): CompressionMethodManager + { + return $this->compressionManager; + } + + /** + * Set the payload of the JWE to build. + * + * @throws InvalidArgumentException if the payload is not encoded in UTF-8 + * + * @return JWEBuilder + */ + public function withPayload(string $payload): self + { + if ('UTF-8' !== mb_detect_encoding($payload, 'UTF-8', true)) { + throw new InvalidArgumentException('The payload must be encoded in UTF-8'); + } + $clone = clone $this; + $clone->payload = $payload; + + return $clone; + } + + /** + * Set the Additional Authenticated Data of the JWE to build. + * + * @return JWEBuilder + */ + public function withAAD(?string $aad): self + { + $clone = clone $this; + $clone->aad = $aad; + + return $clone; + } + + /** + * Set the shared protected header of the JWE to build. + * + * @return JWEBuilder + */ + public function withSharedProtectedHeader(array $sharedProtectedHeader): self + { + $this->checkDuplicatedHeaderParameters($sharedProtectedHeader, $this->sharedHeader); + foreach ($this->recipients as $recipient) { + $this->checkDuplicatedHeaderParameters($sharedProtectedHeader, $recipient->getHeader()); + } + $clone = clone $this; + $clone->sharedProtectedHeader = $sharedProtectedHeader; + + return $clone; + } + + /** + * Set the shared header of the JWE to build. + * + * @return JWEBuilder + */ + public function withSharedHeader(array $sharedHeader): self + { + $this->checkDuplicatedHeaderParameters($this->sharedProtectedHeader, $sharedHeader); + foreach ($this->recipients as $recipient) { + $this->checkDuplicatedHeaderParameters($sharedHeader, $recipient->getHeader()); + } + $clone = clone $this; + $clone->sharedHeader = $sharedHeader; + + return $clone; + } + + /** + * Adds a recipient to the JWE to build. + * + * @throws InvalidArgumentException if key management modes are incompatible + * @throws InvalidArgumentException if the compression method is invalid + * + * @return JWEBuilder + */ + public function addRecipient(JWK $recipientKey, array $recipientHeader = []): self + { + $this->checkDuplicatedHeaderParameters($this->sharedProtectedHeader, $recipientHeader); + $this->checkDuplicatedHeaderParameters($this->sharedHeader, $recipientHeader); + $clone = clone $this; + $completeHeader = array_merge($clone->sharedHeader, $recipientHeader, $clone->sharedProtectedHeader); + $clone->checkAndSetContentEncryptionAlgorithm($completeHeader); + $keyEncryptionAlgorithm = $clone->getKeyEncryptionAlgorithm($completeHeader); + if (null === $clone->keyManagementMode) { + $clone->keyManagementMode = $keyEncryptionAlgorithm->getKeyManagementMode(); + } else { + if (!$clone->areKeyManagementModesCompatible($clone->keyManagementMode, $keyEncryptionAlgorithm->getKeyManagementMode())) { + throw new InvalidArgumentException('Foreign key management mode forbidden.'); + } + } + + $compressionMethod = $clone->getCompressionMethod($completeHeader); + if (null !== $compressionMethod) { + if (null === $clone->compressionMethod) { + $clone->compressionMethod = $compressionMethod; + } elseif ($clone->compressionMethod->name() !== $compressionMethod->name()) { + throw new InvalidArgumentException('Incompatible compression method.'); + } + } + if (null === $compressionMethod && null !== $clone->compressionMethod) { + throw new InvalidArgumentException('Inconsistent compression method.'); + } + $clone->checkKey($keyEncryptionAlgorithm, $recipientKey); + $clone->recipients[] = [ + 'key' => $recipientKey, + 'header' => $recipientHeader, + 'key_encryption_algorithm' => $keyEncryptionAlgorithm, + ]; + + return $clone; + } + + /** + * Builds the JWE. + * + * @throws LogicException if no payload is set + * @throws LogicException if there are no recipient + */ + public function build(): JWE + { + if (null === $this->payload) { + throw new LogicException('Payload not set.'); + } + if (0 === count($this->recipients)) { + throw new LogicException('No recipient.'); + } + + $additionalHeader = []; + $cek = $this->determineCEK($additionalHeader); + + $recipients = []; + foreach ($this->recipients as $recipient) { + $recipient = $this->processRecipient($recipient, $cek, $additionalHeader); + $recipients[] = $recipient; + } + + if (0 !== count($additionalHeader) && 1 === count($this->recipients)) { + $sharedProtectedHeader = array_merge($additionalHeader, $this->sharedProtectedHeader); + } else { + $sharedProtectedHeader = $this->sharedProtectedHeader; + } + $encodedSharedProtectedHeader = 0 === count($sharedProtectedHeader) ? '' : Base64Url::encode(JsonConverter::encode($sharedProtectedHeader)); + + list($ciphertext, $iv, $tag) = $this->encryptJWE($cek, $encodedSharedProtectedHeader); + + return new JWE($ciphertext, $iv, $tag, $this->aad, $this->sharedHeader, $sharedProtectedHeader, $encodedSharedProtectedHeader, $recipients); + } + + /** + * @throws InvalidArgumentException if the content encryption algorithm is not valid + */ + private function checkAndSetContentEncryptionAlgorithm(array $completeHeader): void + { + $contentEncryptionAlgorithm = $this->getContentEncryptionAlgorithm($completeHeader); + if (null === $this->contentEncryptionAlgorithm) { + $this->contentEncryptionAlgorithm = $contentEncryptionAlgorithm; + } elseif ($contentEncryptionAlgorithm->name() !== $this->contentEncryptionAlgorithm->name()) { + throw new InvalidArgumentException('Inconsistent content encryption algorithm'); + } + } + + /** + * @throws InvalidArgumentException if the key encryption algorithm is not valid + */ + private function processRecipient(array $recipient, string $cek, array &$additionalHeader): Recipient + { + $completeHeader = array_merge($this->sharedHeader, $recipient['header'], $this->sharedProtectedHeader); + $keyEncryptionAlgorithm = $recipient['key_encryption_algorithm']; + if (!$keyEncryptionAlgorithm instanceof KeyEncryptionAlgorithm) { + throw new InvalidArgumentException('The key encryption algorithm is not valid'); + } + $encryptedContentEncryptionKey = $this->getEncryptedKey($completeHeader, $cek, $keyEncryptionAlgorithm, $additionalHeader, $recipient['key'], $recipient['sender_key'] ?? null); + $recipientHeader = $recipient['header']; + if (0 !== count($additionalHeader) && 1 !== count($this->recipients)) { + $recipientHeader = array_merge($recipientHeader, $additionalHeader); + $additionalHeader = []; + } + + return new Recipient($recipientHeader, $encryptedContentEncryptionKey); + } + + /** + * @throws InvalidArgumentException if the content encryption algorithm is not valid + */ + private function encryptJWE(string $cek, string $encodedSharedProtectedHeader): array + { + if (!$this->contentEncryptionAlgorithm instanceof ContentEncryptionAlgorithm) { + throw new InvalidArgumentException('The content encryption algorithm is not valid'); + } + $iv_size = $this->contentEncryptionAlgorithm->getIVSize(); + $iv = $this->createIV($iv_size); + $payload = $this->preparePayload(); + $tag = null; + $ciphertext = $this->contentEncryptionAlgorithm->encryptContent($payload, $cek, $iv, $this->aad, $encodedSharedProtectedHeader, $tag); + + return [$ciphertext, $iv, $tag]; + } + + /** + * @return string + */ + private function preparePayload(): ?string + { + $prepared = $this->payload; + if (null === $this->compressionMethod) { + return $prepared; + } + + return $this->compressionMethod->compress($prepared); + } + + /** + * @throws InvalidArgumentException if the key encryption algorithm is not supported + */ + private function getEncryptedKey(array $completeHeader, string $cek, KeyEncryptionAlgorithm $keyEncryptionAlgorithm, array &$additionalHeader, JWK $recipientKey, ?JWK $senderKey): ?string + { + if ($keyEncryptionAlgorithm instanceof KeyEncryption) { + return $this->getEncryptedKeyFromKeyEncryptionAlgorithm($completeHeader, $cek, $keyEncryptionAlgorithm, $recipientKey, $additionalHeader); + } + if ($keyEncryptionAlgorithm instanceof KeyWrapping) { + return $this->getEncryptedKeyFromKeyWrappingAlgorithm($completeHeader, $cek, $keyEncryptionAlgorithm, $recipientKey, $additionalHeader); + } + if ($keyEncryptionAlgorithm instanceof KeyAgreementWithKeyWrapping) { + return $this->getEncryptedKeyFromKeyAgreementAndKeyWrappingAlgorithm($completeHeader, $cek, $keyEncryptionAlgorithm, $additionalHeader, $recipientKey, $senderKey); + } + if ($keyEncryptionAlgorithm instanceof KeyAgreement) { + return null; + } + if ($keyEncryptionAlgorithm instanceof DirectEncryption) { + return null; + } + + throw new InvalidArgumentException('Unsupported key encryption algorithm.'); + } + + /** + * @throws InvalidArgumentException if the content encryption algorithm is invalid + */ + private function getEncryptedKeyFromKeyAgreementAndKeyWrappingAlgorithm(array $completeHeader, string $cek, KeyAgreementWithKeyWrapping $keyEncryptionAlgorithm, array &$additionalHeader, JWK $recipientKey, ?JWK $senderKey): string + { + if (null === $this->contentEncryptionAlgorithm) { + throw new InvalidArgumentException('Invalid content encryption algorithm'); + } + + return $keyEncryptionAlgorithm->wrapAgreementKey($recipientKey, $senderKey, $cek, $this->contentEncryptionAlgorithm->getCEKSize(), $completeHeader, $additionalHeader); + } + + private function getEncryptedKeyFromKeyEncryptionAlgorithm(array $completeHeader, string $cek, KeyEncryption $keyEncryptionAlgorithm, JWK $recipientKey, array &$additionalHeader): string + { + return $keyEncryptionAlgorithm->encryptKey($recipientKey, $cek, $completeHeader, $additionalHeader); + } + + private function getEncryptedKeyFromKeyWrappingAlgorithm(array $completeHeader, string $cek, KeyWrapping $keyEncryptionAlgorithm, JWK $recipientKey, array &$additionalHeader): string + { + return $keyEncryptionAlgorithm->wrapKey($recipientKey, $cek, $completeHeader, $additionalHeader); + } + + /** + * @throws InvalidArgumentException if the content encryption algorithm is invalid + * @throws InvalidArgumentException if the key type is not valid + * @throws InvalidArgumentException if the key management mode is not supported + */ + private function checkKey(KeyEncryptionAlgorithm $keyEncryptionAlgorithm, JWK $recipientKey): void + { + if (null === $this->contentEncryptionAlgorithm) { + throw new InvalidArgumentException('Invalid content encryption algorithm'); + } + + KeyChecker::checkKeyUsage($recipientKey, 'encryption'); + if ('dir' !== $keyEncryptionAlgorithm->name()) { + KeyChecker::checkKeyAlgorithm($recipientKey, $keyEncryptionAlgorithm->name()); + } else { + KeyChecker::checkKeyAlgorithm($recipientKey, $this->contentEncryptionAlgorithm->name()); + } + } + + private function determineCEK(array &$additionalHeader): string + { + if (null === $this->contentEncryptionAlgorithm) { + throw new InvalidArgumentException('Invalid content encryption algorithm'); + } + + switch ($this->keyManagementMode) { + case KeyEncryption::MODE_ENCRYPT: + case KeyEncryption::MODE_WRAP: + return $this->createCEK($this->contentEncryptionAlgorithm->getCEKSize()); + case KeyEncryption::MODE_AGREEMENT: + if (1 !== count($this->recipients)) { + throw new LogicException('Unable to encrypt for multiple recipients using key agreement algorithms.'); + } + /** @var JWK $key */ + $recipientKey = $this->recipients[0]['key']; + $senderKey = $this->recipients[0]['sender_key'] ?? null; + $algorithm = $this->recipients[0]['key_encryption_algorithm']; + if (!$algorithm instanceof KeyAgreement) { + throw new InvalidArgumentException('Invalid content encryption algorithm'); + } + $completeHeader = array_merge($this->sharedHeader, $this->recipients[0]['header'], $this->sharedProtectedHeader); + + return $algorithm->getAgreementKey($this->contentEncryptionAlgorithm->getCEKSize(), $this->contentEncryptionAlgorithm->name(), $recipientKey, $senderKey, $completeHeader, $additionalHeader); + case KeyEncryption::MODE_DIRECT: + if (1 !== count($this->recipients)) { + throw new LogicException('Unable to encrypt for multiple recipients using key agreement algorithms.'); + } + /** @var JWK $key */ + $key = $this->recipients[0]['key']; + if ('oct' !== $key->get('kty')) { + throw new RuntimeException('Wrong key type.'); + } + + return Base64Url::decode($key->get('k')); + default: + throw new InvalidArgumentException(sprintf('Unsupported key management mode "%s".', $this->keyManagementMode)); + } + } + + private function getCompressionMethod(array $completeHeader): ?CompressionMethod + { + if (!array_key_exists('zip', $completeHeader)) { + return null; + } + + return $this->compressionManager->get($completeHeader['zip']); + } + + private function areKeyManagementModesCompatible(string $current, string $new): bool + { + $agree = KeyEncryptionAlgorithm::MODE_AGREEMENT; + $dir = KeyEncryptionAlgorithm::MODE_DIRECT; + $enc = KeyEncryptionAlgorithm::MODE_ENCRYPT; + $wrap = KeyEncryptionAlgorithm::MODE_WRAP; + $supportedKeyManagementModeCombinations = [$enc.$enc => true, $enc.$wrap => true, $wrap.$enc => true, $wrap.$wrap => true, $agree.$agree => false, $agree.$dir => false, $agree.$enc => false, $agree.$wrap => false, $dir.$agree => false, $dir.$dir => false, $dir.$enc => false, $dir.$wrap => false, $enc.$agree => false, $enc.$dir => false, $wrap.$agree => false, $wrap.$dir => false]; + + if (array_key_exists($current.$new, $supportedKeyManagementModeCombinations)) { + return $supportedKeyManagementModeCombinations[$current.$new]; + } + + return false; + } + + private function createCEK(int $size): string + { + return random_bytes($size / 8); + } + + private function createIV(int $size): string + { + return random_bytes($size / 8); + } + + /** + * @throws InvalidArgumentException if the header parameter "alg" is missing + * @throws InvalidArgumentException if the header parameter "alg" is not supported or not a key encryption algorithm + */ + private function getKeyEncryptionAlgorithm(array $completeHeader): KeyEncryptionAlgorithm + { + if (!isset($completeHeader['alg'])) { + throw new InvalidArgumentException('Parameter "alg" is missing.'); + } + $keyEncryptionAlgorithm = $this->keyEncryptionAlgorithmManager->get($completeHeader['alg']); + if (!$keyEncryptionAlgorithm instanceof KeyEncryptionAlgorithm) { + throw new InvalidArgumentException(sprintf('The key encryption algorithm "%s" is not supported or not a key encryption algorithm instance.', $completeHeader['alg'])); + } + + return $keyEncryptionAlgorithm; + } + + /** + * @throws InvalidArgumentException if the header parameter "enc" is missing + * @throws InvalidArgumentException if the header parameter "enc" is not supported or not a content encryption algorithm + */ + private function getContentEncryptionAlgorithm(array $completeHeader): ContentEncryptionAlgorithm + { + if (!isset($completeHeader['enc'])) { + throw new InvalidArgumentException('Parameter "enc" is missing.'); + } + $contentEncryptionAlgorithm = $this->contentEncryptionAlgorithmManager->get($completeHeader['enc']); + if (!$contentEncryptionAlgorithm instanceof ContentEncryptionAlgorithm) { + throw new InvalidArgumentException(sprintf('The content encryption algorithm "%s" is not supported or not a content encryption algorithm instance.', $completeHeader['enc'])); + } + + return $contentEncryptionAlgorithm; + } + + /** + * @throws InvalidArgumentException if the header contains duplicated entries + */ + private function checkDuplicatedHeaderParameters(array $header1, array $header2): void + { + $inter = array_intersect_key($header1, $header2); + if (0 !== count($inter)) { + throw new InvalidArgumentException(sprintf('The header contains duplicated entries: %s.', implode(', ', array_keys($inter)))); + } + } +} diff --git a/vendor/web-token/jwt-encryption/JWEBuilderFactory.php b/vendor/web-token/jwt-encryption/JWEBuilderFactory.php new file mode 100644 index 000000000..80748090f --- /dev/null +++ b/vendor/web-token/jwt-encryption/JWEBuilderFactory.php @@ -0,0 +1,55 @@ +algorithmManagerFactory = $algorithmManagerFactory; + $this->compressionMethodManagerFactory = $compressionMethodManagerFactory; + } + + /** + * Creates a JWE Builder object using the given key encryption algorithms, content encryption algorithms and compression methods. + * + * @param string[] $keyEncryptionAlgorithms + * @param string[] $contentEncryptionAlgorithm + * @param string[] $compressionMethods + */ + public function create(array $keyEncryptionAlgorithms, array $contentEncryptionAlgorithm, array $compressionMethods): JWEBuilder + { + $keyEncryptionAlgorithmManager = $this->algorithmManagerFactory->create($keyEncryptionAlgorithms); + $contentEncryptionAlgorithmManager = $this->algorithmManagerFactory->create($contentEncryptionAlgorithm); + $compressionMethodManager = $this->compressionMethodManagerFactory->create($compressionMethods); + + return new JWEBuilder($keyEncryptionAlgorithmManager, $contentEncryptionAlgorithmManager, $compressionMethodManager); + } +} diff --git a/vendor/web-token/jwt-encryption/JWEDecrypter.php b/vendor/web-token/jwt-encryption/JWEDecrypter.php new file mode 100644 index 000000000..d8d3a1961 --- /dev/null +++ b/vendor/web-token/jwt-encryption/JWEDecrypter.php @@ -0,0 +1,269 @@ +keyEncryptionAlgorithmManager = $keyEncryptionAlgorithmManager; + $this->contentEncryptionAlgorithmManager = $contentEncryptionAlgorithmManager; + $this->compressionMethodManager = $compressionMethodManager; + } + + /** + * Returns the key encryption algorithm manager. + */ + public function getKeyEncryptionAlgorithmManager(): AlgorithmManager + { + return $this->keyEncryptionAlgorithmManager; + } + + /** + * Returns the content encryption algorithm manager. + */ + public function getContentEncryptionAlgorithmManager(): AlgorithmManager + { + return $this->contentEncryptionAlgorithmManager; + } + + /** + * Returns the compression method manager. + */ + public function getCompressionMethodManager(): CompressionMethodManager + { + return $this->compressionMethodManager; + } + + /** + * This method will try to decrypt the given JWE and recipient using a JWK. + * + * @param JWE $jwe A JWE object to decrypt + * @param JWK $jwk The key used to decrypt the input + * @param int $recipient The recipient used to decrypt the token + */ + public function decryptUsingKey(JWE &$jwe, JWK $jwk, int $recipient, ?JWK $senderKey = null): bool + { + $jwkset = new JWKSet([$jwk]); + + return $this->decryptUsingKeySet($jwe, $jwkset, $recipient, $senderKey); + } + + /** + * This method will try to decrypt the given JWE and recipient using a JWKSet. + * + * @param JWE $jwe A JWE object to decrypt + * @param JWKSet $jwkset The key set used to decrypt the input + * @param JWK $jwk The key used to decrypt the token in case of success + * @param int $recipient The recipient used to decrypt the token in case of success + * + * @throws InvalidArgumentException if no key is set is the keyset + * @throws InvalidArgumentException if the token has no recipients + */ + public function decryptUsingKeySet(JWE &$jwe, JWKSet $jwkset, int $recipient, JWK &$jwk = null, ?JWK $senderKey = null): bool + { + if (0 === $jwkset->count()) { + throw new InvalidArgumentException('No key in the key set.'); + } + if (null !== $jwe->getPayload()) { + return true; + } + if (0 === $jwe->countRecipients()) { + throw new InvalidArgumentException('The JWE does not contain any recipient.'); + } + + $plaintext = $this->decryptRecipientKey($jwe, $jwkset, $recipient, $jwk, $senderKey); + if (null !== $plaintext) { + $jwe = $jwe->withPayload($plaintext); + + return true; + } + + return false; + } + + private function decryptRecipientKey(JWE $jwe, JWKSet $jwkset, int $i, JWK &$successJwk = null, ?JWK $senderKey = null): ?string + { + $recipient = $jwe->getRecipient($i); + $completeHeader = array_merge($jwe->getSharedProtectedHeader(), $jwe->getSharedHeader(), $recipient->getHeader()); + $this->checkCompleteHeader($completeHeader); + + $key_encryption_algorithm = $this->getKeyEncryptionAlgorithm($completeHeader); + $content_encryption_algorithm = $this->getContentEncryptionAlgorithm($completeHeader); + + $this->checkIvSize($jwe->getIV(), $content_encryption_algorithm->getIVSize()); + + foreach ($jwkset as $recipientKey) { + try { + KeyChecker::checkKeyUsage($recipientKey, 'decryption'); + if ('dir' !== $key_encryption_algorithm->name()) { + KeyChecker::checkKeyAlgorithm($recipientKey, $key_encryption_algorithm->name()); + } else { + KeyChecker::checkKeyAlgorithm($recipientKey, $content_encryption_algorithm->name()); + } + $cek = $this->decryptCEK($key_encryption_algorithm, $content_encryption_algorithm, $recipientKey, $senderKey, $recipient, $completeHeader); + if (null !== $cek) { + $this->checkCekSize($cek, $key_encryption_algorithm, $content_encryption_algorithm); + $payload = $this->decryptPayload($jwe, $cek, $content_encryption_algorithm, $completeHeader); + $successJwk = $recipientKey; + + return $payload; + } + } catch (Throwable $e) { + //We do nothing, we continue with other keys + continue; + } + } + + return null; + } + + /** + * @throws InvalidArgumentException if the Content Encryption Key size is invalid + */ + private function checkCekSize(string $cek, KeyEncryptionAlgorithm $keyEncryptionAlgorithm, ContentEncryptionAlgorithm $algorithm): void + { + if ($keyEncryptionAlgorithm instanceof DirectEncryption || $keyEncryptionAlgorithm instanceof KeyAgreement) { + return; + } + if (mb_strlen($cek, '8bit') * 8 !== $algorithm->getCEKSize()) { + throw new InvalidArgumentException('Invalid CEK size'); + } + } + + /** + * @throws InvalidArgumentException if the IV size is invalid + */ + private function checkIvSize(?string $iv, int $requiredIvSize): void + { + if (null === $iv && 0 !== $requiredIvSize) { + throw new InvalidArgumentException('Invalid IV size'); + } + if (is_string($iv) && mb_strlen($iv, '8bit') !== $requiredIvSize / 8) { + throw new InvalidArgumentException('Invalid IV size'); + } + } + + /** + * @throws InvalidArgumentException if the CEK creation method is not supported + */ + private function decryptCEK(Algorithm $key_encryption_algorithm, ContentEncryptionAlgorithm $content_encryption_algorithm, JWK $recipientKey, ?JWK $senderKey, Recipient $recipient, array $completeHeader): ?string + { + if ($key_encryption_algorithm instanceof DirectEncryption) { + return $key_encryption_algorithm->getCEK($recipientKey); + } + if ($key_encryption_algorithm instanceof KeyAgreement) { + return $key_encryption_algorithm->getAgreementKey($content_encryption_algorithm->getCEKSize(), $content_encryption_algorithm->name(), $recipientKey, $senderKey, $completeHeader); + } + if ($key_encryption_algorithm instanceof KeyAgreementWithKeyWrapping) { + return $key_encryption_algorithm->unwrapAgreementKey($recipientKey, $senderKey, $recipient->getEncryptedKey(), $content_encryption_algorithm->getCEKSize(), $completeHeader); + } + if ($key_encryption_algorithm instanceof KeyEncryption) { + return $key_encryption_algorithm->decryptKey($recipientKey, $recipient->getEncryptedKey(), $completeHeader); + } + if ($key_encryption_algorithm instanceof KeyWrapping) { + return $key_encryption_algorithm->unwrapKey($recipientKey, $recipient->getEncryptedKey(), $completeHeader); + } + + throw new InvalidArgumentException('Unsupported CEK generation'); + } + + private function decryptPayload(JWE $jwe, string $cek, ContentEncryptionAlgorithm $content_encryption_algorithm, array $completeHeader): string + { + $payload = $content_encryption_algorithm->decryptContent($jwe->getCiphertext(), $cek, $jwe->getIV(), $jwe->getAAD(), $jwe->getEncodedSharedProtectedHeader(), $jwe->getTag()); + + return $this->decompressIfNeeded($payload, $completeHeader); + } + + private function decompressIfNeeded(string $payload, array $completeHeaders): string + { + if (array_key_exists('zip', $completeHeaders)) { + $compression_method = $this->compressionMethodManager->get($completeHeaders['zip']); + $payload = $compression_method->uncompress($payload); + } + + return $payload; + } + + /** + * @throws InvalidArgumentException if a header parameter is missing + */ + private function checkCompleteHeader(array $completeHeaders): void + { + foreach (['enc', 'alg'] as $key) { + if (!isset($completeHeaders[$key])) { + throw new InvalidArgumentException(sprintf("Parameter '%s' is missing.", $key)); + } + } + } + + /** + * @throws InvalidArgumentException if the key encryption algorithm is not supported or does not implement the KeyEncryptionAlgorithm interface + */ + private function getKeyEncryptionAlgorithm(array $completeHeaders): KeyEncryptionAlgorithm + { + $key_encryption_algorithm = $this->keyEncryptionAlgorithmManager->get($completeHeaders['alg']); + if (!$key_encryption_algorithm instanceof KeyEncryptionAlgorithm) { + throw new InvalidArgumentException(sprintf('The key encryption algorithm "%s" is not supported or does not implement KeyEncryptionAlgorithm interface.', $completeHeaders['alg'])); + } + + return $key_encryption_algorithm; + } + + /** + * @throws InvalidArgumentException if the content encryption algorithm is not supported or does not implement the ContentEncryption interface + */ + private function getContentEncryptionAlgorithm(array $completeHeader): ContentEncryptionAlgorithm + { + $content_encryption_algorithm = $this->contentEncryptionAlgorithmManager->get($completeHeader['enc']); + if (!$content_encryption_algorithm instanceof ContentEncryptionAlgorithm) { + throw new InvalidArgumentException(sprintf('The key encryption algorithm "%s" is not supported or does not implement the ContentEncryption interface.', $completeHeader['enc'])); + } + + return $content_encryption_algorithm; + } +} diff --git a/vendor/web-token/jwt-encryption/JWEDecrypterFactory.php b/vendor/web-token/jwt-encryption/JWEDecrypterFactory.php new file mode 100644 index 000000000..71ba0bf24 --- /dev/null +++ b/vendor/web-token/jwt-encryption/JWEDecrypterFactory.php @@ -0,0 +1,52 @@ +algorithmManagerFactory = $algorithmManagerFactory; + $this->compressionMethodManagerFactory = $compressionMethodManagerFactory; + } + + /** + * Creates a JWE Decrypter object using the given key encryption algorithms, content encryption algorithms and compression methods. + * + * @param string[] $keyEncryptionAlgorithms + * @param string[] $contentEncryptionAlgorithms + * @param string[] $compressionMethods + */ + public function create(array $keyEncryptionAlgorithms, array $contentEncryptionAlgorithms, array $compressionMethods): JWEDecrypter + { + $keyEncryptionAlgorithmManager = $this->algorithmManagerFactory->create($keyEncryptionAlgorithms); + $contentEncryptionAlgorithmManager = $this->algorithmManagerFactory->create($contentEncryptionAlgorithms); + $compressionMethodManager = $this->compressionMethodManagerFactory->create($compressionMethods); + + return new JWEDecrypter($keyEncryptionAlgorithmManager, $contentEncryptionAlgorithmManager, $compressionMethodManager); + } +} diff --git a/vendor/web-token/jwt-encryption/JWELoader.php b/vendor/web-token/jwt-encryption/JWELoader.php new file mode 100644 index 000000000..dcc7557c0 --- /dev/null +++ b/vendor/web-token/jwt-encryption/JWELoader.php @@ -0,0 +1,122 @@ +serializerManager = $serializerManager; + $this->jweDecrypter = $jweDecrypter; + $this->headerCheckerManager = $headerCheckerManager; + } + + /** + * Returns the JWE Decrypter object. + */ + public function getJweDecrypter(): JWEDecrypter + { + return $this->jweDecrypter; + } + + /** + * Returns the header checker manager if set. + */ + public function getHeaderCheckerManager(): ?HeaderCheckerManager + { + return $this->headerCheckerManager; + } + + /** + * Returns the serializer manager. + */ + public function getSerializerManager(): JWESerializerManager + { + return $this->serializerManager; + } + + /** + * This method will try to load and decrypt the given token using a JWK. + * If succeeded, the methods will populate the $recipient variable and returns the JWE. + */ + public function loadAndDecryptWithKey(string $token, JWK $key, ?int &$recipient): JWE + { + $keyset = new JWKSet([$key]); + + return $this->loadAndDecryptWithKeySet($token, $keyset, $recipient); + } + + /** + * This method will try to load and decrypt the given token using a JWKSet. + * If succeeded, the methods will populate the $recipient variable and returns the JWE. + * + * @throws RuntimeException if the data cannot be loaded or decrypted + */ + public function loadAndDecryptWithKeySet(string $token, JWKSet $keyset, ?int &$recipient): JWE + { + try { + $jwe = $this->serializerManager->unserialize($token); + $nbRecipients = $jwe->countRecipients(); + for ($i = 0; $i < $nbRecipients; ++$i) { + if ($this->processRecipient($jwe, $keyset, $i)) { + $recipient = $i; + + return $jwe; + } + } + } catch (Throwable $e) { + // Nothing to do. Exception thrown just after + } + + throw new RuntimeException('Unable to load and decrypt the token.'); + } + + private function processRecipient(JWE &$jwe, JWKSet $keyset, int $recipient): bool + { + try { + if (null !== $this->headerCheckerManager) { + $this->headerCheckerManager->check($jwe, $recipient); + } + + return $this->jweDecrypter->decryptUsingKeySet($jwe, $keyset, $recipient); + } catch (Throwable $e) { + return false; + } + } +} diff --git a/vendor/web-token/jwt-encryption/JWELoaderFactory.php b/vendor/web-token/jwt-encryption/JWELoaderFactory.php new file mode 100644 index 000000000..461cca5ae --- /dev/null +++ b/vendor/web-token/jwt-encryption/JWELoaderFactory.php @@ -0,0 +1,62 @@ +jweSerializerManagerFactory = $jweSerializerManagerFactory; + $this->jweDecrypterFactory = $jweDecrypterFactory; + $this->headerCheckerManagerFactory = $headerCheckerManagerFactory; + } + + /** + * Creates a JWELoader using the given serializer aliases, encryption algorithm aliases, compression method aliases + * and header checker aliases. + */ + public function create(array $serializers, array $keyEncryptionAlgorithms, array $contentEncryptionAlgorithms, array $compressionMethods, array $headerCheckers = []): JWELoader + { + $serializerManager = $this->jweSerializerManagerFactory->create($serializers); + $jweDecrypter = $this->jweDecrypterFactory->create($keyEncryptionAlgorithms, $contentEncryptionAlgorithms, $compressionMethods); + if (null !== $this->headerCheckerManagerFactory) { + $headerCheckerManager = $this->headerCheckerManagerFactory->create($headerCheckers); + } else { + $headerCheckerManager = null; + } + + return new JWELoader($serializerManager, $jweDecrypter, $headerCheckerManager); + } +} diff --git a/vendor/web-token/jwt-encryption/JWETokenSupport.php b/vendor/web-token/jwt-encryption/JWETokenSupport.php new file mode 100644 index 000000000..0c311ffd7 --- /dev/null +++ b/vendor/web-token/jwt-encryption/JWETokenSupport.php @@ -0,0 +1,40 @@ +getSharedProtectedHeader(); + $unprotectedHeader = $jwt->getSharedHeader(); + $recipient = $jwt->getRecipient($index)->getHeader(); + + $unprotectedHeader = array_merge( + $unprotectedHeader, + $recipient + ); + } +} diff --git a/vendor/web-token/jwt-encryption/LICENSE b/vendor/web-token/jwt-encryption/LICENSE new file mode 100644 index 000000000..37cf976b1 --- /dev/null +++ b/vendor/web-token/jwt-encryption/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014-2019 Spomky-Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/web-token/jwt-encryption/README.md b/vendor/web-token/jwt-encryption/README.md new file mode 100644 index 000000000..d3dbe017b --- /dev/null +++ b/vendor/web-token/jwt-encryption/README.md @@ -0,0 +1,15 @@ +PHP JWT Encryption Component +============================ + +This repository is a sub repository of [the JWT Framework](https://github.com/web-token/jwt-framework) project and is READ ONLY. + +**Please do not submit any Pull Request here.** +You should go to [the main repository](https://github.com/web-token/jwt-framework) instead. + +# Documentation + +The official documentation is available as https://web-token.spomky-labs.com/ + +# Licence + +This software is release under [MIT licence](LICENSE). diff --git a/vendor/web-token/jwt-encryption/Recipient.php b/vendor/web-token/jwt-encryption/Recipient.php new file mode 100644 index 000000000..98c4dfb82 --- /dev/null +++ b/vendor/web-token/jwt-encryption/Recipient.php @@ -0,0 +1,83 @@ +header = $header; + $this->encryptedKey = $encryptedKey; + } + + /** + * Returns the recipient header. + */ + public function getHeader(): array + { + return $this->header; + } + + /** + * Returns the value of the recipient header parameter with the specified key. + * + * @param string $key The key + * + * @throws InvalidArgumentException if the header parameter does not exist + * + * @return null|mixed + */ + public function getHeaderParameter(string $key) + { + if (!$this->hasHeaderParameter($key)) { + throw new InvalidArgumentException(sprintf('The header "%s" does not exist.', $key)); + } + + return $this->header[$key]; + } + + /** + * Returns true if the recipient header contains the parameter with the specified key. + * + * @param string $key The key + */ + public function hasHeaderParameter(string $key): bool + { + return array_key_exists($key, $this->header); + } + + /** + * Returns the encrypted key. + */ + public function getEncryptedKey(): ?string + { + return $this->encryptedKey; + } +} diff --git a/vendor/web-token/jwt-encryption/Serializer/CompactSerializer.php b/vendor/web-token/jwt-encryption/Serializer/CompactSerializer.php new file mode 100644 index 000000000..6d45bcdd5 --- /dev/null +++ b/vendor/web-token/jwt-encryption/Serializer/CompactSerializer.php @@ -0,0 +1,122 @@ +getRecipient($recipientIndex); + + $this->checkHasNoAAD($jwe); + $this->checkHasSharedProtectedHeader($jwe); + $this->checkRecipientHasNoHeader($jwe, $recipientIndex); + + return sprintf( + '%s.%s.%s.%s.%s', + $jwe->getEncodedSharedProtectedHeader(), + Base64Url::encode(null === $recipient->getEncryptedKey() ? '' : $recipient->getEncryptedKey()), + Base64Url::encode(null === $jwe->getIV() ? '' : $jwe->getIV()), + Base64Url::encode($jwe->getCiphertext()), + Base64Url::encode(null === $jwe->getTag() ? '' : $jwe->getTag()) + ); + } + + /** + * @throws InvalidArgumentException if the input is not supported + */ + public function unserialize(string $input): JWE + { + $parts = explode('.', $input); + if (5 !== count($parts)) { + throw new InvalidArgumentException('Unsupported input'); + } + + try { + $encodedSharedProtectedHeader = $parts[0]; + $sharedProtectedHeader = JsonConverter::decode(Base64Url::decode($encodedSharedProtectedHeader)); + $encryptedKey = '' === $parts[1] ? null : Base64Url::decode($parts[1]); + $iv = Base64Url::decode($parts[2]); + $ciphertext = Base64Url::decode($parts[3]); + $tag = Base64Url::decode($parts[4]); + + return new JWE( + $ciphertext, + $iv, + $tag, + null, + [], + $sharedProtectedHeader, + $encodedSharedProtectedHeader, + [new Recipient([], $encryptedKey)] + ); + } catch (Throwable $throwable) { + throw new InvalidArgumentException('Unsupported input', $throwable->getCode(), $throwable); + } + } + + /** + * @throws LogicException if the AAD is invalid + */ + private function checkHasNoAAD(JWE $jwe): void + { + if (null !== $jwe->getAAD()) { + throw new LogicException('This JWE has AAD and cannot be converted into Compact JSON.'); + } + } + + /** + * @throws LogicException if the JWE has a shared header or recipient header (invalid for compact JSON) + */ + private function checkRecipientHasNoHeader(JWE $jwe, int $id): void + { + if (0 !== count($jwe->getSharedHeader()) || 0 !== count($jwe->getRecipient($id)->getHeader())) { + throw new LogicException('This JWE has shared header parameters or recipient header parameters and cannot be converted into Compact JSON.'); + } + } + + /** + * @throws LogicException if the JWE has no shared protected header (invalid for compact JSON) + */ + private function checkHasSharedProtectedHeader(JWE $jwe): void + { + if (0 === count($jwe->getSharedProtectedHeader())) { + throw new LogicException('This JWE does not have shared protected header parameters and cannot be converted into Compact JSON.'); + } + } +} diff --git a/vendor/web-token/jwt-encryption/Serializer/JSONFlattenedSerializer.php b/vendor/web-token/jwt-encryption/Serializer/JSONFlattenedSerializer.php new file mode 100644 index 000000000..0afde06d8 --- /dev/null +++ b/vendor/web-token/jwt-encryption/Serializer/JSONFlattenedSerializer.php @@ -0,0 +1,111 @@ +getRecipient($recipientIndex); + $data = [ + 'ciphertext' => Base64Url::encode($jwe->getCiphertext()), + 'iv' => Base64Url::encode($jwe->getIV()), + 'tag' => Base64Url::encode($jwe->getTag()), + ]; + if (null !== $jwe->getAAD()) { + $data['aad'] = Base64Url::encode($jwe->getAAD()); + } + if (0 !== count($jwe->getSharedProtectedHeader())) { + $data['protected'] = $jwe->getEncodedSharedProtectedHeader(); + } + if (0 !== count($jwe->getSharedHeader())) { + $data['unprotected'] = $jwe->getSharedHeader(); + } + if (0 !== count($recipient->getHeader())) { + $data['header'] = $recipient->getHeader(); + } + if (null !== $recipient->getEncryptedKey()) { + $data['encrypted_key'] = Base64Url::encode($recipient->getEncryptedKey()); + } + + return JsonConverter::encode($data); + } + + public function unserialize(string $input): JWE + { + $data = JsonConverter::decode($input); + $this->checkData($data); + + $ciphertext = Base64Url::decode($data['ciphertext']); + $iv = Base64Url::decode($data['iv']); + $tag = Base64Url::decode($data['tag']); + $aad = array_key_exists('aad', $data) ? Base64Url::decode($data['aad']) : null; + list($encodedSharedProtectedHeader, $sharedProtectedHeader, $sharedHeader) = $this->processHeaders($data); + $encryptedKey = array_key_exists('encrypted_key', $data) ? Base64Url::decode($data['encrypted_key']) : null; + $header = array_key_exists('header', $data) ? $data['header'] : []; + + return new JWE( + $ciphertext, + $iv, + $tag, + $aad, + $sharedHeader, + $sharedProtectedHeader, + $encodedSharedProtectedHeader, + [new Recipient($header, $encryptedKey)] + ); + } + + /** + * @throws InvalidArgumentException if the payload cannot be encoded + */ + private function checkData(?array $data): void + { + if (null === $data || !isset($data['ciphertext']) || isset($data['recipients'])) { + throw new InvalidArgumentException('Unsupported input.'); + } + } + + private function processHeaders(array $data): array + { + $encodedSharedProtectedHeader = array_key_exists('protected', $data) ? $data['protected'] : null; + $sharedProtectedHeader = $encodedSharedProtectedHeader ? JsonConverter::decode(Base64Url::decode($encodedSharedProtectedHeader)) : []; + $sharedHeader = $data['unprotected'] ?? []; + + return [$encodedSharedProtectedHeader, $sharedProtectedHeader, $sharedHeader]; + } +} diff --git a/vendor/web-token/jwt-encryption/Serializer/JSONGeneralSerializer.php b/vendor/web-token/jwt-encryption/Serializer/JSONGeneralSerializer.php new file mode 100644 index 000000000..c318c21d5 --- /dev/null +++ b/vendor/web-token/jwt-encryption/Serializer/JSONGeneralSerializer.php @@ -0,0 +1,131 @@ +countRecipients()) { + throw new LogicException('No recipient.'); + } + + $data = [ + 'ciphertext' => Base64Url::encode($jwe->getCiphertext()), + 'iv' => Base64Url::encode($jwe->getIV()), + 'tag' => Base64Url::encode($jwe->getTag()), + ]; + if (null !== $jwe->getAAD()) { + $data['aad'] = Base64Url::encode($jwe->getAAD()); + } + if (0 !== count($jwe->getSharedProtectedHeader())) { + $data['protected'] = $jwe->getEncodedSharedProtectedHeader(); + } + if (0 !== count($jwe->getSharedHeader())) { + $data['unprotected'] = $jwe->getSharedHeader(); + } + $data['recipients'] = []; + foreach ($jwe->getRecipients() as $recipient) { + $temp = []; + if (0 !== count($recipient->getHeader())) { + $temp['header'] = $recipient->getHeader(); + } + if (null !== $recipient->getEncryptedKey()) { + $temp['encrypted_key'] = Base64Url::encode($recipient->getEncryptedKey()); + } + $data['recipients'][] = $temp; + } + + return JsonConverter::encode($data); + } + + public function unserialize(string $input): JWE + { + $data = JsonConverter::decode($input); + $this->checkData($data); + + $ciphertext = Base64Url::decode($data['ciphertext']); + $iv = Base64Url::decode($data['iv']); + $tag = Base64Url::decode($data['tag']); + $aad = array_key_exists('aad', $data) ? Base64Url::decode($data['aad']) : null; + list($encodedSharedProtectedHeader, $sharedProtectedHeader, $sharedHeader) = $this->processHeaders($data); + $recipients = []; + foreach ($data['recipients'] as $recipient) { + list($encryptedKey, $header) = $this->processRecipient($recipient); + $recipients[] = new Recipient($header, $encryptedKey); + } + + return new JWE( + $ciphertext, + $iv, + $tag, + $aad, + $sharedHeader, + $sharedProtectedHeader, + $encodedSharedProtectedHeader, + $recipients + ); + } + + /** + * @throws InvalidArgumentException if the input is not supported + */ + private function checkData(?array $data): void + { + if (null === $data || !isset($data['ciphertext']) || !isset($data['recipients'])) { + throw new InvalidArgumentException('Unsupported input.'); + } + } + + private function processRecipient(array $recipient): array + { + $encryptedKey = array_key_exists('encrypted_key', $recipient) ? Base64Url::decode($recipient['encrypted_key']) : null; + $header = array_key_exists('header', $recipient) ? $recipient['header'] : []; + + return [$encryptedKey, $header]; + } + + private function processHeaders(array $data): array + { + $encodedSharedProtectedHeader = array_key_exists('protected', $data) ? $data['protected'] : null; + $sharedProtectedHeader = $encodedSharedProtectedHeader ? JsonConverter::decode(Base64Url::decode($encodedSharedProtectedHeader)) : []; + $sharedHeader = array_key_exists('unprotected', $data) ? $data['unprotected'] : []; + + return [$encodedSharedProtectedHeader, $sharedProtectedHeader, $sharedHeader]; + } +} diff --git a/vendor/web-token/jwt-encryption/Serializer/JWESerializer.php b/vendor/web-token/jwt-encryption/Serializer/JWESerializer.php new file mode 100644 index 000000000..aa9c341ac --- /dev/null +++ b/vendor/web-token/jwt-encryption/Serializer/JWESerializer.php @@ -0,0 +1,44 @@ +add($serializer); + } + } + + /** + * Return the serializer names supported by the manager. + * + * @return string[] + */ + public function names(): array + { + return array_keys($this->serializers); + } + + /** + * Converts a JWE into a string. + * Throws an exception if none of the serializer was able to convert the input. + * + * @throws InvalidArgumentException if the serializer is not supported + */ + public function serialize(string $name, JWE $jws, ?int $recipientIndex = null): string + { + if (!isset($this->serializers[$name])) { + throw new InvalidArgumentException(sprintf('Unsupported serializer "%s".', $name)); + } + + return $this->serializers[$name]->serialize($jws, $recipientIndex); + } + + /** + * Loads data and return a JWE object. + * Throws an exception if none of the serializer was able to convert the input. + * + * @param string $input A string that represents a JWE + * @param null|string $name the name of the serializer if the input is unserialized + * + * @throws InvalidArgumentException if the input cannot be loaded + */ + public function unserialize(string $input, ?string &$name = null): JWE + { + foreach ($this->serializers as $serializer) { + try { + $jws = $serializer->unserialize($input); + $name = $serializer->name(); + + return $jws; + } catch (InvalidArgumentException $e) { + continue; + } + } + + throw new InvalidArgumentException('Unsupported input.'); + } + + /** + * Adds a serializer to the manager. + */ + private function add(JWESerializer $serializer): void + { + $this->serializers[$serializer->name()] = $serializer; + } +} diff --git a/vendor/web-token/jwt-encryption/Serializer/JWESerializerManagerFactory.php b/vendor/web-token/jwt-encryption/Serializer/JWESerializerManagerFactory.php new file mode 100644 index 000000000..63dd69df3 --- /dev/null +++ b/vendor/web-token/jwt-encryption/Serializer/JWESerializerManagerFactory.php @@ -0,0 +1,72 @@ +serializers[$name])) { + throw new InvalidArgumentException(sprintf('Unsupported serializer "%s".', $name)); + } + $serializers[] = $this->serializers[$name]; + } + + return new JWESerializerManager($serializers); + } + + /** + * Return the serializer names supported by the manager. + * + * @return string[] + */ + public function names(): array + { + return array_keys($this->serializers); + } + + /** + * Returns all serializers supported by this factory. + * + * @return JWESerializer[] + */ + public function all(): array + { + return $this->serializers; + } + + /** + * Adds a serializer to the manager. + */ + public function add(JWESerializer $serializer): void + { + $this->serializers[$serializer->name()] = $serializer; + } +} diff --git a/vendor/web-token/jwt-encryption/Tests/CompressionTest.php b/vendor/web-token/jwt-encryption/Tests/CompressionTest.php new file mode 100644 index 000000000..16876778a --- /dev/null +++ b/vendor/web-token/jwt-encryption/Tests/CompressionTest.php @@ -0,0 +1,83 @@ +list()); + $manager->get('DEF'); + } + + /** + * @covers \Jose\Component\Encryption\Compression\CompressionMethodManager + * @test + */ + public function getInvalidCompressionAlgorithm(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The compression method "FOO" is not supported.'); + + $manager = new CompressionMethodManager([]); + static::assertFalse($manager->has('FOO')); + $manager->get('FOO'); + } + + /** + * @covers \Jose\Component\Encryption\Compression\Deflate + * @test + */ + public function deflate(): void + { + $compression = new Deflate(9); + + $data = 'Live long and Prosper.'; + $compressed = $compression->compress($data); + $uncompressed = $compression->uncompress($compressed); + static::assertNotNull($compressed); + static::assertSame($data, $uncompressed); + } + + /** + * @covers \Jose\Component\Encryption\Compression\Deflate + * @test + */ + public function deflateInvalidCompressionLevel(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The compression level can be given as 0 for no compression up to 9 for maximum compression. If -1 given, the default compression level will be the default compression level of the zlib library.'); + + new Deflate(100); + } +} diff --git a/vendor/web-token/jwt-encryption/Tests/ECDHESWithX25519EncryptionTest.php b/vendor/web-token/jwt-encryption/Tests/ECDHESWithX25519EncryptionTest.php new file mode 100644 index 000000000..b9b25f673 --- /dev/null +++ b/vendor/web-token/jwt-encryption/Tests/ECDHESWithX25519EncryptionTest.php @@ -0,0 +1,64 @@ + 'OKP', + 'crv' => 'X25519', + 'x' => 'azBwhSxIIhQIri4QdT__5q7ybEhKItJlGeyuLNN5ZCQ', + 'd' => 'aCaXuAvPEuLVqQSihzryIWaQqmXZxA-3ZrF6CEm180c', + ]); + $input = "You can trust us to stick with you through thick and thin\xe2\x80\x93to the bitter end. And you can trust us to keep any secret of yours\xe2\x80\x93closer than you keep it yourself. But you cannot trust us to let you face trouble alone, and go off without a word. We are your friends, Frodo."; + + $protectedHeader = [ + 'alg' => 'ECDH-ES+A128KW', + 'enc' => 'A128GCM', + ]; + + $jweBuilder = $this->getJWEBuilderFactory()->create(['ECDH-ES+A128KW'], ['A128GCM'], ['DEF']); + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['ECDH-ES+A128KW'], ['A128GCM'], ['DEF']); + + $jwt = $jweBuilder + ->create()->withPayload($input) + ->withSharedProtectedHeader($protectedHeader) + ->addRecipient($receiverKey) + ->build() + ; + $jwt = $this->getJWESerializerManager()->serialize('jwe_compact', $jwt, 0); + + $jwe = $this->getJWESerializerManager()->unserialize($jwt); + static::assertTrue($jweDecrypter->decryptUsingKey($jwe, $receiverKey, 0)); + static::assertTrue($jwe->hasSharedProtectedHeaderParameter('epk')); + static::assertEquals($input, $jwe->getPayload()); + } +} diff --git a/vendor/web-token/jwt-encryption/Tests/EncrypterTest.php b/vendor/web-token/jwt-encryption/Tests/EncrypterTest.php new file mode 100644 index 000000000..dcc49e09b --- /dev/null +++ b/vendor/web-token/jwt-encryption/Tests/EncrypterTest.php @@ -0,0 +1,793 @@ +getJWEBuilderFactory()->create(['RSA-OAEP-256'], ['A256CBC-HS512'], ['DEF']); + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['RSA-OAEP-256'], ['A256CBC-HS512'], ['DEF']); + + $jwe = $jweBuilder + ->create()->withPayload('FOO') + ->withSharedProtectedHeader([ + 'enc' => 'A256CBC-HS512', + 'alg' => 'RSA-OAEP-256', + 'zip' => 'DEF', + ]) + ->withAAD('foo,bar,baz') + ->addRecipient($this->getRSARecipientKey()) + ->build() + ; + + $jwe = $this->getJWESerializerManager()->serialize('jwe_json_flattened', $jwe, 0); + + $loaded = $this->getJWESerializerManager()->unserialize($jwe); + + static::assertEquals('RSA-OAEP-256', $loaded->getSharedProtectedHeaderParameter('alg')); + static::assertEquals('A256CBC-HS512', $loaded->getSharedProtectedHeaderParameter('enc')); + static::assertEquals('DEF', $loaded->getSharedProtectedHeaderParameter('zip')); + static::assertNull($loaded->getPayload()); + static::assertTrue($jweDecrypter->decryptUsingKeySet($loaded, $this->getPrivateKeySet(), 0)); + + static::assertEquals('FOO', $loaded->getPayload()); + } + + /** + * @test + */ + public function duplicatedHeader(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The header contains duplicated entries: zip.'); + + $jweBuilder = $this->getJWEBuilderFactory()->create(['RSA-OAEP-256'], ['A256CBC-HS512'], ['DEF']); + + $jweBuilder + ->create()->withPayload('FOO') + ->withSharedProtectedHeader([ + 'enc' => 'A256CBC-HS512', + 'alg' => 'RSA-OAEP-256', + 'zip' => 'DEF', + ]) + ->addRecipient( + $this->getRSARecipientKey(), + ['zip' => 'DEF'] + ) + ; + } + + /** + * @test + */ + public function createCompactJWEUsingFactory(): void + { + $jweBuilder = $this->getJWEBuilderFactory()->create(['RSA-OAEP-256'], ['A256CBC-HS512'], ['DEF']); + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['RSA-OAEP-256'], ['A256CBC-HS512'], ['DEF']); + + $jwe = $jweBuilder + ->create()->withPayload('FOO') + ->withSharedProtectedHeader([ + 'enc' => 'A256CBC-HS512', + 'alg' => 'RSA-OAEP-256', + 'zip' => 'DEF', + ]) + ->addRecipient($this->getRSARecipientKey()) + ->build() + ; + $jwe = $this->getJWESerializerManager()->serialize('jwe_compact', $jwe, 0); + + $loaded = $this->getJWESerializerManager()->unserialize($jwe); + + static::assertEquals('RSA-OAEP-256', $loaded->getSharedProtectedHeaderParameter('alg')); + static::assertEquals('A256CBC-HS512', $loaded->getSharedProtectedHeaderParameter('enc')); + static::assertEquals('DEF', $loaded->getSharedProtectedHeaderParameter('zip')); + static::assertNull($loaded->getPayload()); + + static::assertTrue($jweDecrypter->decryptUsingKeySet($loaded, $this->getPrivateKeySet(), 0)); + + static::assertEquals('FOO', $loaded->getPayload()); + } + + /** + * @test + */ + public function createFlattenedJWEUsingFactory(): void + { + $jweBuilder = $this->getJWEBuilderFactory()->create(['RSA-OAEP-256'], ['A256CBC-HS512'], ['DEF']); + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['RSA-OAEP-256'], ['A256CBC-HS512'], ['DEF']); + + $jwe = $jweBuilder + ->create()->withPayload('FOO') + ->withSharedProtectedHeader([ + 'enc' => 'A256CBC-HS512', + 'alg' => 'RSA-OAEP-256', + 'zip' => 'DEF', + ]) + ->withSharedHeader([ + 'foo' => 'bar', + ]) + ->addRecipient( + $this->getRSARecipientKey(), + [ + 'plic' => 'ploc', + ] + ) + ->withAAD('A,B,C,D') + ->build() + ; + $jwe = $this->getJWESerializerManager()->serialize('jwe_json_flattened', $jwe, 0); + + $loaded = $this->getJWESerializerManager()->unserialize($jwe); + + static::assertEquals('RSA-OAEP-256', $loaded->getSharedProtectedHeaderParameter('alg')); + static::assertEquals('A256CBC-HS512', $loaded->getSharedProtectedHeaderParameter('enc')); + static::assertEquals('DEF', $loaded->getSharedProtectedHeaderParameter('zip')); + static::assertEquals('bar', $loaded->getSharedHeaderParameter('foo')); + static::assertEquals('A,B,C,D', $loaded->getAAD()); + static::assertEquals('ploc', $loaded->getRecipient(0)->getHeaderParameter('plic')); + static::assertNull($loaded->getPayload()); + + static::assertTrue($jweDecrypter->decryptUsingKeySet($loaded, $this->getPrivateKeySet(), 0)); + + static::assertEquals('FOO', $loaded->getPayload()); + } + + /** + * @test + */ + public function encryptAndLoadFlattenedWithAAD(): void + { + $jweBuilder = $this->getJWEBuilderFactory()->create(['RSA-OAEP-256'], ['A256CBC-HS512'], ['DEF']); + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['RSA-OAEP-256'], ['A256CBC-HS512'], ['DEF']); + + $jwe = $jweBuilder + ->create()->withPayload(json_encode($this->getKeyToEncrypt())) + ->withSharedProtectedHeader([ + 'enc' => 'A256CBC-HS512', + 'alg' => 'RSA-OAEP-256', + 'zip' => 'DEF', + ]) + ->addRecipient($this->getRSARecipientKey()) + ->withAAD('foo,bar,baz') + ->build() + ; + $jwe = $this->getJWESerializerManager()->serialize('jwe_json_flattened', $jwe, 0); + + $loaded = $this->getJWESerializerManager()->unserialize($jwe); + + static::assertEquals('RSA-OAEP-256', $loaded->getSharedProtectedHeaderParameter('alg')); + static::assertEquals('A256CBC-HS512', $loaded->getSharedProtectedHeaderParameter('enc')); + static::assertEquals('DEF', $loaded->getSharedProtectedHeaderParameter('zip')); + static::assertNull($loaded->getPayload()); + + static::assertTrue($jweDecrypter->decryptUsingKeySet($loaded, $this->getPrivateKeySet(), 0)); + + static::assertEquals($this->getKeyToEncrypt(), new JWK(json_decode($loaded->getPayload(), true))); + } + + /** + * @test + */ + public function compressionAlgorithmNotSupported(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The compression method "FIP" is not supported.'); + + $jweBuilder = $this->getJWEBuilderFactory()->create(['RSA-OAEP-256'], ['A256CBC-HS512'], ['DEF']); + + $jwe = $jweBuilder + ->create()->withPayload(json_encode($this->getKeyToEncrypt())) + ->withSharedProtectedHeader([ + 'enc' => 'A256CBC-HS512', + 'alg' => 'RSA-OAEP-256', + 'zip' => 'FIP', + ]) + ->addRecipient($this->getRSARecipientKey()) + ->withAAD('foo,bar,baz') + ->build() + ; + $this->getJWESerializerManager()->serialize('jwe_json_flattened', $jwe, 0); + } + + /** + * @test + */ + public function foreignKeyManagementModeForbidden(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Foreign key management mode forbidden.'); + + $jweBuilder = $this->getJWEBuilderFactory()->create(['dir', 'ECDH-ES+A256KW'], ['A256CBC-HS512'], ['DEF']); + + $jweBuilder + ->create()->withPayload('Live long and Prosper.') + ->withSharedProtectedHeader([ + 'enc' => 'A256CBC-HS512', + ]) + ->addRecipient($this->getECDHRecipientPublicKey(), ['kid' => 'e9bc097a-ce51-4036-9562-d2ade882db0d', 'alg' => 'ECDH-ES+A256KW']) + ->addRecipient($this->getDirectKey(), ['kid' => 'DIR_1', 'alg' => 'dir']) + ->build() + ; + } + + /** + * @test + */ + public function operationNotAllowedForTheKey(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Key cannot be used to encrypt'); + + $jweBuilder = $this->getJWEBuilderFactory()->create(['RSA-OAEP-256'], ['A256CBC-HS512'], ['DEF']); + + $jweBuilder + ->create()->withPayload('Live long and Prosper.') + ->withSharedProtectedHeader([ + 'enc' => 'A256CBC-HS512', + 'alg' => 'RSA-OAEP-256', + 'zip' => 'DEF', + ]) + ->addRecipient($this->getSigningKey()) + ->build() + ; + } + + /** + * @test + */ + public function algorithmNotAllowedForTheKey(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Key is only allowed for algorithm "RSA-OAEP".'); + + $jweBuilder = $this->getJWEBuilderFactory()->create(['RSA-OAEP-256'], ['A256CBC-HS512'], ['DEF']); + + $jweBuilder + ->create()->withPayload('Live long and Prosper.') + ->withSharedProtectedHeader([ + 'enc' => 'A256CBC-HS512', + 'alg' => 'RSA-OAEP-256', + 'zip' => 'DEF', + ]) + ->addRecipient($this->getRSARecipientKeyWithAlgorithm()) + ->build() + ; + } + + /** + * @test + */ + public function encryptAndLoadFlattenedWithDeflateCompression(): void + { + $jweBuilder = $this->getJWEBuilderFactory()->create(['RSA-OAEP-256'], ['A128CBC-HS256'], ['DEF']); + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['RSA-OAEP-256'], ['A128CBC-HS256'], ['DEF']); + + $jwe = $jweBuilder + ->create()->withPayload(json_encode($this->getKeySetToEncrypt())) + ->withSharedProtectedHeader([ + 'kid' => '123456789', + 'enc' => 'A128CBC-HS256', + 'alg' => 'RSA-OAEP-256', + 'zip' => 'DEF', + ]) + ->addRecipient($this->getRSARecipientKey()) + ->build() + ; + $jwe = $this->getJWESerializerManager()->serialize('jwe_compact', $jwe, 0); + + $loaded = $this->getJWESerializerManager()->unserialize($jwe); + + static::assertEquals('RSA-OAEP-256', $loaded->getSharedProtectedHeaderParameter('alg')); + static::assertEquals('A128CBC-HS256', $loaded->getSharedProtectedHeaderParameter('enc')); + static::assertEquals('DEF', $loaded->getSharedProtectedHeaderParameter('zip')); + static::assertNull($loaded->getPayload()); + + static::assertTrue($jweDecrypter->decryptUsingKeySet($loaded, $this->getPrivateKeySet(), 0)); + + static::assertEquals($this->getKeySetToEncrypt(), JWKSet::createFromKeyData(json_decode($loaded->getPayload(), true))); + } + + /** + * @test + */ + public function algParameterIsMissing(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Parameter "alg" is missing.'); + + $jweBuilder = $this->getJWEBuilderFactory()->create([], ['A256CBC-HS512'], ['DEF']); + + $jweBuilder + ->create()->withPayload(json_encode($this->getKeyToEncrypt())) + ->withSharedProtectedHeader([ + 'kid' => '123456789', + 'enc' => 'A256CBC-HS512', + 'zip' => 'DEF', + ]) + ->addRecipient($this->getRSARecipientKey()) + ->build() + ; + } + + /** + * @test + */ + public function encParameterIsMissing(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Parameter "enc" is missing.'); + + $jweBuilder = $this->getJWEBuilderFactory()->create(['RSA-OAEP-256'], [], ['DEF']); + + $jweBuilder + ->create()->withPayload(json_encode($this->getKeyToEncrypt())) + ->withSharedProtectedHeader([ + 'kid' => '123456789', + 'alg' => 'RSA-OAEP-256', + 'zip' => 'DEF', + ]) + ->addRecipient($this->getRSARecipientKey()) + ->build() + ; + } + + /** + * @test + */ + public function notAKeyEncryptionAlgorithm(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The key encryption algorithm "A256CBC-HS512" is not supported or not a key encryption algorithm instance.'); + + $jweBuilder = $this->getJWEBuilderFactory()->create(['A256CBC-HS512'], ['A256CBC-HS512'], ['DEF']); + + $jweBuilder + ->create()->withPayload(json_encode($this->getKeyToEncrypt())) + ->withSharedProtectedHeader([ + 'kid' => '123456789', + 'enc' => 'A256CBC-HS512', + 'alg' => 'A256CBC-HS512', + 'zip' => 'DEF', + ]) + ->addRecipient($this->getRSARecipientKey()) + ->build() + ; + } + + /** + * @test + */ + public function notAContentEncryptionAlgorithm(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The content encryption algorithm "RSA-OAEP-256" is not supported or not a content encryption algorithm instance.'); + + $jweBuilder = $this->getJWEBuilderFactory()->create(['RSA-OAEP-256'], ['RSA-OAEP-256'], ['DEF']); + + $jweBuilder + ->create()->withPayload(json_encode($this->getKeyToEncrypt())) + ->withSharedProtectedHeader([ + 'kid' => '123456789', + 'enc' => 'RSA-OAEP-256', + 'alg' => 'RSA-OAEP-256', + 'zip' => 'DEF', + ]) + ->addRecipient($this->getRSARecipientKey()) + ->build() + ; + } + + /** + * @test + */ + public function encryptAndLoadCompactWithDirectKeyEncryption(): void + { + $jweBuilder = $this->getJWEBuilderFactory()->create(['dir'], ['A192CBC-HS384'], ['DEF']); + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['dir'], ['A192CBC-HS384'], ['DEF']); + + $jwe = $jweBuilder + ->create()->withPayload(json_encode($this->getKeyToEncrypt())) + ->withSharedProtectedHeader([ + 'kid' => 'DIR_1', + 'enc' => 'A192CBC-HS384', + 'alg' => 'dir', + ]) + ->addRecipient($this->getDirectKey()) + ->build() + ; + $jwe = $this->getJWESerializerManager()->serialize('jwe_json_flattened', $jwe, 0); + + $loaded = $this->getJWESerializerManager()->unserialize($jwe); + + static::assertEquals('dir', $loaded->getSharedProtectedHeaderParameter('alg')); + static::assertEquals('A192CBC-HS384', $loaded->getSharedProtectedHeaderParameter('enc')); + static::assertFalse($loaded->hasSharedHeaderParameter('zip')); + static::assertNull($loaded->getPayload()); + + static::assertTrue($jweDecrypter->decryptUsingKeySet($loaded, $this->getSymmetricKeySet(), 0)); + + static::assertEquals($this->getKeyToEncrypt(), new JWK(json_decode($loaded->getPayload(), true))); + } + + /** + * @test + */ + public function encryptAndLoadCompactKeyAgreement(): void + { + $jweBuilder = $this->getJWEBuilderFactory()->create(['ECDH-ES'], ['A192CBC-HS384'], ['DEF']); + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['ECDH-ES'], ['A192CBC-HS384'], ['DEF']); + + $payload = json_encode(['user_id' => '1234', 'exp' => time() + 3600]); + $jwe = $jweBuilder + ->create()->withPayload($payload) + ->withSharedProtectedHeader([ + 'kid' => 'e9bc097a-ce51-4036-9562-d2ade882db0d', + 'enc' => 'A192CBC-HS384', + 'alg' => 'ECDH-ES', + ]) + ->addRecipient($this->getECDHRecipientPublicKey()) + ->build() + ; + $jwe = $this->getJWESerializerManager()->serialize('jwe_json_flattened', $jwe, 0); + + $loaded = $this->getJWESerializerManager()->unserialize($jwe); + + static::assertEquals('ECDH-ES', $loaded->getSharedProtectedHeaderParameter('alg')); + static::assertEquals('A192CBC-HS384', $loaded->getSharedProtectedHeaderParameter('enc')); + static::assertFalse($loaded->hasSharedProtectedHeaderParameter('zip')); + static::assertNull($loaded->getPayload()); + + static::assertTrue($jweDecrypter->decryptUsingKeySet($loaded, $this->getPrivateKeySet(), 0)); + + static::assertEquals($payload, $loaded->getPayload()); + } + + /** + * @test + */ + public function encryptAndLoadCompactKeyAgreementWithWrappingCompact(): void + { + $jweBuilder = $this->getJWEBuilderFactory()->create(['ECDH-ES+A256KW'], ['A256CBC-HS512'], ['DEF']); + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['ECDH-ES+A256KW'], ['A256CBC-HS512'], ['DEF']); + + $jwe = $jweBuilder + ->create()->withPayload('Live long and Prosper.') + ->withSharedProtectedHeader([ + 'kid' => 'e9bc097a-ce51-4036-9562-d2ade882db0d', + 'enc' => 'A256CBC-HS512', + 'alg' => 'ECDH-ES+A256KW', + ]) + ->addRecipient($this->getECDHRecipientPublicKey()) + ->build() + ; + $jwe = $this->getJWESerializerManager()->serialize('jwe_json_flattened', $jwe, 0); + + $loaded = $this->getJWESerializerManager()->unserialize($jwe); + + static::assertEquals('ECDH-ES+A256KW', $loaded->getSharedProtectedHeaderParameter('alg')); + static::assertEquals('A256CBC-HS512', $loaded->getSharedProtectedHeaderParameter('enc')); + static::assertFalse($loaded->hasSharedProtectedHeaderParameter('zip')); + static::assertFalse($loaded->hasSharedHeaderParameter('zip')); + static::assertNull($loaded->getPayload()); + + static::assertTrue($jweDecrypter->decryptUsingKeySet($loaded, $this->getPrivateKeySet(), 0)); + + static::assertTrue(is_string($loaded->getPayload())); + static::assertEquals('Live long and Prosper.', $loaded->getPayload()); + } + + /** + * @test + */ + public function encryptAndLoadWithGCMAndAAD(): void + { + $jweBuilder = $this->getJWEBuilderFactory()->create(['ECDH-ES+A256KW'], ['A256GCM'], ['DEF']); + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['ECDH-ES+A256KW'], ['A256GCM'], ['DEF']); + + $jwe = $jweBuilder + ->create()->withPayload('Live long and Prosper.') + ->withSharedProtectedHeader([ + 'kid' => 'e9bc097a-ce51-4036-9562-d2ade882db0d', + 'enc' => 'A256GCM', + 'alg' => 'ECDH-ES+A256KW', + ]) + ->withAAD('foo,bar,baz') + ->addRecipient($this->getECDHRecipientPublicKey()) + ->build() + ; + $jwe = $this->getJWESerializerManager()->serialize('jwe_json_flattened', $jwe, 0); + + $loaded = $this->getJWESerializerManager()->unserialize($jwe); + + static::assertEquals('ECDH-ES+A256KW', $loaded->getSharedProtectedHeaderParameter('alg')); + static::assertEquals('A256GCM', $loaded->getSharedProtectedHeaderParameter('enc')); + static::assertFalse($loaded->hasSharedProtectedHeaderParameter('zip')); + static::assertFalse($loaded->hasSharedHeaderParameter('zip')); + static::assertNull($loaded->getPayload()); + + static::assertTrue($jweDecrypter->decryptUsingKeySet($loaded, $this->getPrivateKeySet(), 0)); + + static::assertTrue(is_string($loaded->getPayload())); + static::assertEquals('Live long and Prosper.', $loaded->getPayload()); + } + + /** + * @test + */ + public function encryptAndLoadCompactKeyAgreementWithWrapping(): void + { + $jweBuilder = $this->getJWEBuilderFactory()->create(['RSA-OAEP-256', 'ECDH-ES+A256KW'], ['A256CBC-HS512'], ['DEF']); + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['RSA-OAEP-256', 'ECDH-ES+A256KW'], ['A256CBC-HS512'], ['DEF']); + + $jwe = $jweBuilder + ->create()->withPayload('Live long and Prosper.') + ->withSharedProtectedHeader([ + 'enc' => 'A256CBC-HS512', + ]) + ->withAAD('foo,bar,baz') + ->addRecipient($this->getECDHRecipientPublicKey(), ['kid' => 'e9bc097a-ce51-4036-9562-d2ade882db0d', 'alg' => 'ECDH-ES+A256KW']) + ->addRecipient($this->getRSARecipientKey(), ['kid' => '123456789', 'alg' => 'RSA-OAEP-256']) + ->build() + ; + $jwe = $this->getJWESerializerManager()->serialize('jwe_json_general', $jwe); + + $loaded = $this->getJWESerializerManager()->unserialize($jwe); + + static::assertEquals(2, $loaded->countRecipients()); + + static::assertEquals('A256CBC-HS512', $loaded->getSharedProtectedHeaderParameter('enc')); + static::assertEquals('ECDH-ES+A256KW', $loaded->getRecipient(0)->getHeaderParameter('alg')); + static::assertEquals('RSA-OAEP-256', $loaded->getRecipient(1)->getHeaderParameter('alg')); + static::assertFalse($loaded->hasSharedHeaderParameter('zip')); + static::assertFalse($loaded->hasSharedProtectedHeaderParameter('zip')); + static::assertNull($loaded->getPayload()); + + static::assertTrue($jweDecrypter->decryptUsingKeySet($loaded, $this->getPrivateKeySet(), 0)); + + static::assertTrue(is_string($loaded->getPayload())); + static::assertEquals('Live long and Prosper.', $loaded->getPayload()); + } + + /** + * @return JWK + */ + private function getKeyToEncrypt() + { + return new JWK([ + 'kty' => 'EC', + 'use' => 'enc', + 'crv' => 'P-256', + 'x' => 'f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU', + 'y' => 'x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0', + 'd' => 'jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI', + ]); + } + + /** + * @return JWKSet + */ + private function getKeySetToEncrypt() + { + $key = new JWK([ + 'kty' => 'EC', + 'use' => 'enc', + 'crv' => 'P-256', + 'x' => 'f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU', + 'y' => 'x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0', + 'd' => 'jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI', + ]); + + return new JWKSet([$key]); + } + + /** + * @return JWK + */ + private function getRSARecipientKey() + { + return new JWK([ + 'kty' => 'RSA', + 'use' => 'enc', + 'n' => 'tpS1ZmfVKVP5KofIhMBP0tSWc4qlh6fm2lrZSkuKxUjEaWjzZSzs72gEIGxraWusMdoRuV54xsWRyf5KeZT0S-I5Prle3Idi3gICiO4NwvMk6JwSBcJWwmSLFEKyUSnB2CtfiGc0_5rQCpcEt_Dn5iM-BNn7fqpoLIbks8rXKUIj8-qMVqkTXsEKeKinE23t1ykMldsNaaOH-hvGti5Jt2DMnH1JjoXdDXfxvSP_0gjUYb0ektudYFXoA6wekmQyJeImvgx4Myz1I4iHtkY_Cp7J4Mn1ejZ6HNmyvoTE_4OuY1uCeYv4UyXFc1s1uUyYtj4z57qsHGsS4dQ3A2MJsw', + 'e' => 'AQAB', + ]); + } + + /** + * @return JWK + */ + private function getRSARecipientKeyWithAlgorithm() + { + return new JWK([ + 'kty' => 'RSA', + 'use' => 'enc', + 'alg' => 'RSA-OAEP', + 'n' => 'tpS1ZmfVKVP5KofIhMBP0tSWc4qlh6fm2lrZSkuKxUjEaWjzZSzs72gEIGxraWusMdoRuV54xsWRyf5KeZT0S-I5Prle3Idi3gICiO4NwvMk6JwSBcJWwmSLFEKyUSnB2CtfiGc0_5rQCpcEt_Dn5iM-BNn7fqpoLIbks8rXKUIj8-qMVqkTXsEKeKinE23t1ykMldsNaaOH-hvGti5Jt2DMnH1JjoXdDXfxvSP_0gjUYb0ektudYFXoA6wekmQyJeImvgx4Myz1I4iHtkY_Cp7J4Mn1ejZ6HNmyvoTE_4OuY1uCeYv4UyXFc1s1uUyYtj4z57qsHGsS4dQ3A2MJsw', + 'e' => 'AQAB', + ]); + } + + /** + * @return JWK + */ + private function getSigningKey() + { + return new JWK([ + 'kty' => 'EC', + 'key_ops' => ['sign', 'verify'], + 'crv' => 'P-256', + 'x' => 'f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU', + 'y' => 'x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0', + 'd' => 'jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI', + ]); + } + + /** + * @return JWK + */ + private function getECDHRecipientPublicKey() + { + return new JWK([ + 'kty' => 'EC', + 'key_ops' => ['encrypt', 'decrypt'], + 'crv' => 'P-256', + 'x' => 'f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU', + 'y' => 'x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0', + ]); + } + + /** + * @return JWK + */ + private function getDirectKey() + { + return new JWK([ + 'kid' => 'DIR_1', + 'key_ops' => ['encrypt', 'decrypt'], + 'kty' => 'oct', + 'k' => Base64Url::encode(hex2bin('00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F')), + ]); + } + + private function getPrivateKeySet(): JWKSet + { + $keys = ['keys' => [ + [ + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => 'weNJy2HscCSM6AEDTDg04biOvhFhyyWvOHQfeF_PxMQ', + 'y' => 'e8lnCO-AlStT-NJVX-crhB7QRYhiix03illJOVAOyck', + 'd' => 'VEmDZpDXXK8p8N0Cndsxs924q6nS1RXFASRl6BfUqdw', + ], + [ + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => 'gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0', + 'y' => 'SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps', + 'd' => '0_NxaRPUMQoAJt50Gz8YiTr8gRTwyEaCumd-MToTmIo', + ], + [ + 'kid' => '2010-12-29', + 'kty' => 'RSA', + 'n' => 'ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ', + 'e' => 'AQAB', + 'd' => 'Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97IjlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYTCBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLhBOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ', + ], + [ + 'kid' => 'e9bc097a-ce51-4036-9562-d2ade882db0d', + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => 'f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU', + 'y' => 'x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0', + 'd' => 'jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI', + ], + [ + 'kid' => '123456789', + 'kty' => 'RSA', + 'n' => 'tpS1ZmfVKVP5KofIhMBP0tSWc4qlh6fm2lrZSkuKxUjEaWjzZSzs72gEIGxraWusMdoRuV54xsWRyf5KeZT0S-I5Prle3Idi3gICiO4NwvMk6JwSBcJWwmSLFEKyUSnB2CtfiGc0_5rQCpcEt_Dn5iM-BNn7fqpoLIbks8rXKUIj8-qMVqkTXsEKeKinE23t1ykMldsNaaOH-hvGti5Jt2DMnH1JjoXdDXfxvSP_0gjUYb0ektudYFXoA6wekmQyJeImvgx4Myz1I4iHtkY_Cp7J4Mn1ejZ6HNmyvoTE_4OuY1uCeYv4UyXFc1s1uUyYtj4z57qsHGsS4dQ3A2MJsw', + 'e' => 'AQAB', + 'p' => '5BGU1c7af_5sFyfsa-onIJgo5BZu8uHvz3Uyb8OA0a-G9UPO1ShLYjX0wUfhZcFB7fwPtgmmYAN6wKGVce9eMAbX4PliPk3r-BcpZuPKkuLk_wFvgWAQ5Hqw2iEuwXLV0_e8c2gaUt_hyMC5-nFc4v0Bmv6NT6Pfry-UrK3BKWc', + 'd' => 'Kp0KuZwCZGL1BLgsVM-N0edMNitl9wN5Hf2WOYDoIqOZNAEKzdJuenIMhITJjRFUX05GVL138uyp2js_pqDdY9ipA7rAKThwGuDdNphZHech9ih3DGEPXs-YpmHqvIbCd3GoGm38MKwxYkddEpFnjo8rKna1_BpJthrFxjDRhw9DxJBycOdH2yWTyp62ZENPvneK40H2a57W4QScTgfecZqD59m2fGUaWaX5uUmIxaEmtGoJnd9RE4oywKhgN7_TK7wXRlqA4UoRPiH2ACrdU-_cLQL9Jc0u0GqZJK31LDbOeN95QgtSCc72k3Vtzy3CrVpp5TAA67s1Gj9Skn-CAQ', + 'q' => 'zPD-B-nrngwF-O99BHvb47XGKR7ON8JCI6JxavzIkusMXCB8rMyYW8zLs68L8JLAzWZ34oMq0FPUnysBxc5nTF8Nb4BZxTZ5-9cHfoKrYTI3YWsmVW2FpCJFEjMs4NXZ28PBkS9b4zjfS2KhNdkmCeOYU0tJpNfwmOTI90qeUdU', + 'dp' => 'aJrzw_kjWK9uDlTeaES2e4muv6bWbopYfrPHVWG7NPGoGdhnBnd70-jhgMEiTZSNU8VXw2u7prAR3kZ-kAp1DdwlqedYOzFsOJcPA0UZhbORyrBy30kbll_7u6CanFm6X4VyJxCpejd7jKNw6cCTFP1sfhWg5NVJ5EUTkPwE66M', + 'dq' => 'Swz1-m_vmTFN_pu1bK7vF7S5nNVrL4A0OFiEsGliCmuJWzOKdL14DiYxctvnw3H6qT2dKZZfV2tbse5N9-JecdldUjfuqAoLIe7dD7dKi42YOlTC9QXmqvTh1ohnJu8pmRFXEZQGUm_BVhoIb2_WPkjav6YSkguCUHt4HRd2YwE', + 'qi' => 'BocuCOEOq-oyLDALwzMXU8gOf3IL1Q1_BWwsdoANoh6i179psxgE4JXToWcpXZQQqub8ngwE6uR9fpd3m6N_PL4T55vbDDyjPKmrL2ttC2gOtx9KrpPh-Z7LQRo4BE48nHJJrystKHfFlaH2G7JxHNgMBYVADyttN09qEoav8Os', + ], + [ + 'kty' => 'RSA', + 'n' => 'oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUWcJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3Spsk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2asbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMStPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2djYgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw', + 'e' => 'AQAB', + 'd' => 'kLdtIj6GbDks_ApCSTYQtelcNttlKiOyPzMrXHeI-yk1F7-kpDxY4-WY5NWV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD93Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghkqDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vlt3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSndVTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ', + 'p' => '1r52Xk46c-LsfB5P442p7atdPUrxQSy4mti_tZI3Mgf2EuFVbUoDBvaRQ-SWxkbkmoEzL7JXroSBjSrK3YIQgYdMgyAEPTPjXv_hI2_1eTSPVZfzL0lffNn03IXqWF5MDFuoUYE0hzb2vhrlN_rKrbfDIwUbTrjjgieRbwC6Cl0', + 'q' => 'wLb35x7hmQWZsWJmB_vle87ihgZ19S8lBEROLIsZG4ayZVe9Hi9gDVCOBmUDdaDYVTSNx_8Fyw1YYa9XGrGnDew00J28cRUoeBB_jKI1oma0Orv1T9aXIWxKwd4gvxFImOWr3QRL9KEBRzk2RatUBnmDZJTIAfwTs0g68UZHvtc', + 'dp' => 'ZK-YwE7diUh0qR1tR7w8WHtolDx3MZ_OTowiFvgfeQ3SiresXjm9gZ5KLhMXvo-uz-KUJWDxS5pFQ_M0evdo1dKiRTjVw_x4NyqyXPM5nULPkcpU827rnpZzAJKpdhWAgqrXGKAECQH0Xt4taznjnd_zVpAmZZq60WPMBMfKcuE', + 'dq' => 'Dq0gfgJ1DdFGXiLvQEZnuKEN0UUmsJBxkjydc3j4ZYdBiMRAy86x0vHCjywcMlYYg4yoC4YZa9hNVcsjqA3FeiL19rk8g6Qn29Tt0cj8qqyFpz9vNDBUfCAiJVeESOjJDZPYHdHY8v1b-o-Z2X5tvLx-TCekf7oxyeKDUqKWjis', + 'qi' => 'VIMpMYbPf47dT1w_zDUXfPimsSegnMOA1zTaX7aGk_8urY6R8-ZW1FxU7AlWAyLWybqq6t16VFd7hQd0y6flUK4SlOydB61gwanOsXGOAOv82cHq0E3eL4HrtZkUuKvnPrMnsUUFlfUdybVzxyjz9JF_XyaY14ardLSjf4L_FNY', + ], + [ + 'kty' => 'RSA', + 'n' => 'sXchDaQebHnPiGvyDOAT4saGEUetSyo9MKLOoWFsueri23bOdgWp4Dy1WlUzewbgBHod5pcM9H95GQRV3JDXboIRROSBigeC5yjU1hGzHHyXss8UDprecbAYxknTcQkhslANGRUZmdTOQ5qTRsLAt6BTYuyvVRdhS8exSZEy_c4gs_7svlJJQ4H9_NxsiIoLwAEk7-Q3UXERGYw_75IDrGA84-lA_-Ct4eTlXHBIY2EaV7t7LjJaynVJCpkv4LKjTTAumiGUIuQhrNhZLuF_RJLqHpM2kgWFLU7-VTdL1VbC2tejvcI2BlMkEpk1BzBZI0KQB0GaDWFLN-aEAw3vRw', + 'e' => 'AQAB', + 'd' => 'VFCWOqXr8nvZNyaaJLXdnNPXZKRaWCjkU5Q2egQQpTBMwhprMzWzpR8Sxq1OPThh_J6MUD8Z35wky9b8eEO0pwNS8xlh1lOFRRBoNqDIKVOku0aZb-rynq8cxjDTLZQ6Fz7jSjR1Klop-YKaUHc9GsEofQqYruPhzSA-QgajZGPbE_0ZaVDJHfyd7UUBUKunFMScbflYAAOYJqVIVwaYR5zWEEceUjNnTNo_CVSj-VvXLO5VZfCUAVLgW4dpf1SrtZjSt34YLsRarSb127reG_DUwg9Ch-KyvjT1SkHgUWRVGcyly7uvVGRSDwsXypdrNinPA4jlhoNdizK2zF2CWQ', + 'p' => '9gY2w6I6S6L0juEKsbeDAwpd9WMfgqFoeA9vEyEUuk4kLwBKcoe1x4HG68ik918hdDSE9vDQSccA3xXHOAFOPJ8R9EeIAbTi1VwBYnbTp87X-xcPWlEPkrdoUKW60tgs1aNd_Nnc9LEVVPMS390zbFxt8TN_biaBgelNgbC95sM', + 'q' => 'uKlCKvKv_ZJMVcdIs5vVSU_6cPtYI1ljWytExV_skstvRSNi9r66jdd9-yBhVfuG4shsp2j7rGnIio901RBeHo6TPKWVVykPu1iYhQXw1jIABfw-MVsN-3bQ76WLdt2SDxsHs7q7zPyUyHXmps7ycZ5c72wGkUwNOjYelmkiNS0', + 'dp' => 'w0kZbV63cVRvVX6yk3C8cMxo2qCM4Y8nsq1lmMSYhG4EcL6FWbX5h9yuvngs4iLEFk6eALoUS4vIWEwcL4txw9LsWH_zKI-hwoReoP77cOdSL4AVcraHawlkpyd2TWjE5evgbhWtOxnZee3cXJBkAi64Ik6jZxbvk-RR3pEhnCs', + 'dq' => 'o_8V14SezckO6CNLKs_btPdFiO9_kC1DsuUTd2LAfIIVeMZ7jn1Gus_Ff7B7IVx3p5KuBGOVF8L-qifLb6nQnLysgHDh132NDioZkhH7mI7hPG-PYE_odApKdnqECHWw0J-F0JWnUd6D2B_1TvF9mXA2Qx-iGYn8OVV1Bsmp6qU', + 'qi' => 'eNho5yRBEBxhGBtQRww9QirZsB66TrfFReG_CcteI1aCneT0ELGhYlRlCtUkTRclIfuEPmNsNDPbLoLqqCVznFbvdB7x-Tl-m0l_eFTj2KiqwGqE9PZB9nNTwMVvH3VRRSLWACvPnSiwP8N5Usy-WRXS-V7TbpxIhvepTfE0NNo', + ], + [ + 'kty' => 'RSA', + 'n' => 'ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ', + 'e' => 'AQAB', + 'd' => 'Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97IjlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYTCBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLhBOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ', + 'p' => '4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPGBY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc', + 'q' => 'uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc', + 'dp' => 'BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3QCLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0', + 'dq' => 'h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-kyNlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU', + 'qi' => 'IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2oy26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLUW0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U', + ], + [ + 'kty' => 'EC', + 'crv' => 'P-521', + 'x' => 'AekpBQ8ST8a8VcfVOTNl353vSrDCLLJXmPk06wTjxrrjcBpXp5EOnYG_NjFZ6OvLFV1jSfS9tsz4qUxcWceqwQGk', + 'y' => 'ADSmRA43Z1DSNx_RvcLI87cdL07l6jQyyBXMoxVg_l2Th-x3S1WDhjDly79ajL4Kkd0AZMaZmh9ubmf63e3kyMj2', + 'd' => 'AY5pb7A0UFiB3RELSD64fTLOSV_jazdF7fLYyuTw8lOfRhWg6Y6rUrPAxerEzgdRhajnu0ferB0d53vM9mE15j2C', + ], + ]]; + + return JWKSet::createFromKeyData($keys); + } + + private function getSymmetricKeySet(): JWKSet + { + $keys = ['keys' => [ + [ + 'kid' => 'DIR_1', + 'kty' => 'oct', + 'k' => Base64Url::encode(hex2bin('00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F')), + ], + [ + 'kty' => 'oct', + 'k' => 'f5aN5V6iihwQVqP-tPNNtkIJNCwUb9-JukCIKkF0rNfxqxA771RJynYAT2xtzAP0MYaR7U5fMP_wvbRQq5l38Q', + ], + [ + 'kty' => 'oct', + 'k' => 'GawgguFyGrWKav7AX4VKUg', + ], + [ + 'kty' => 'oct', + 'k' => 'AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow', + ], + ]]; + + return JWKSet::createFromKeyData($keys); + } +} diff --git a/vendor/web-token/jwt-encryption/Tests/EncryptionTest.php b/vendor/web-token/jwt-encryption/Tests/EncryptionTest.php new file mode 100644 index 000000000..aeb9f2962 --- /dev/null +++ b/vendor/web-token/jwt-encryption/Tests/EncryptionTest.php @@ -0,0 +1,167 @@ +algorithmManagerFactory) { + $this->algorithmManagerFactory = new AlgorithmManagerFactory(); + $this->algorithmManagerFactory->add('A128GCM', new ContentEncryption\A128GCM()); + $this->algorithmManagerFactory->add('A192GCM', new ContentEncryption\A192GCM()); + $this->algorithmManagerFactory->add('A256GCM', new ContentEncryption\A256GCM()); + $this->algorithmManagerFactory->add('A128CBC-HS256', new ContentEncryption\A128CBCHS256()); + $this->algorithmManagerFactory->add('A192CBC-HS384', new ContentEncryption\A192CBCHS384()); + $this->algorithmManagerFactory->add('A256CBC-HS512', new ContentEncryption\A256CBCHS512()); + $this->algorithmManagerFactory->add('A128GCMKW', new KeyEncryption\A128GCMKW()); + $this->algorithmManagerFactory->add('A192GCMKW', new KeyEncryption\A192GCMKW()); + $this->algorithmManagerFactory->add('A256GCMKW', new KeyEncryption\A256GCMKW()); + $this->algorithmManagerFactory->add('A128KW', new KeyEncryption\A128KW()); + $this->algorithmManagerFactory->add('A192KW', new KeyEncryption\A192KW()); + $this->algorithmManagerFactory->add('A256KW', new KeyEncryption\A256KW()); + $this->algorithmManagerFactory->add('dir', new KeyEncryption\Dir()); + $this->algorithmManagerFactory->add('ECDH-ES', new KeyEncryption\ECDHES()); + $this->algorithmManagerFactory->add('ECDH-ES+A128KW', new KeyEncryption\ECDHESA128KW()); + $this->algorithmManagerFactory->add('ECDH-ES+A192KW', new KeyEncryption\ECDHESA192KW()); + $this->algorithmManagerFactory->add('ECDH-ES+A256KW', new KeyEncryption\ECDHESA256KW()); + $this->algorithmManagerFactory->add('PBES2-HS256+A128KW', new KeyEncryption\PBES2HS256A128KW()); + $this->algorithmManagerFactory->add('PBES2-HS384+A192KW', new KeyEncryption\PBES2HS384A192KW()); + $this->algorithmManagerFactory->add('PBES2-HS512+A256KW', new KeyEncryption\PBES2HS512A256KW()); + $this->algorithmManagerFactory->add('RSA1_5', new KeyEncryption\RSA15()); + $this->algorithmManagerFactory->add('RSA-OAEP', new KeyEncryption\RSAOAEP()); + $this->algorithmManagerFactory->add('RSA-OAEP-256', new KeyEncryption\RSAOAEP256()); + } + + return $this->algorithmManagerFactory; + } + + protected function getCompressionMethodManagerFactory(): CompressionMethodManagerFactory + { + if (null === $this->compressionMethodManagerFactory) { + $this->compressionMethodManagerFactory = new CompressionMethodManagerFactory(); + $this->compressionMethodManagerFactory->add('DEF', new Compression\Deflate()); + } + + return $this->compressionMethodManagerFactory; + } + + protected function getJWEBuilderFactory(): JWEBuilderFactory + { + if (null === $this->jweBuilderFactory) { + $this->jweBuilderFactory = new JWEBuilderFactory( + $this->getAlgorithmManagerFactory(), + $this->getCompressionMethodManagerFactory() + ); + } + + return $this->jweBuilderFactory; + } + + protected function getJWEDecrypterFactory(): JWEDecrypterFactory + { + if (null === $this->jweDecrypterFactory) { + $this->jweDecrypterFactory = new JWEDecrypterFactory( + $this->getAlgorithmManagerFactory(), + $this->getCompressionMethodManagerFactory() + ); + } + + return $this->jweDecrypterFactory; + } + + protected function getJWELoaderFactory(): JWELoaderFactory + { + if (null === $this->jweLoaderFactory) { + $this->jweLoaderFactory = new JWELoaderFactory( + $this->getJWESerializerManagerFactory(), + $this->getJWEDecrypterFactory(), + null + ); + } + + return $this->jweLoaderFactory; + } + + protected function getJWESerializerManagerFactory(): Serializer\JWESerializerManagerFactory + { + if (null === $this->jwsSerializerManagerFactory) { + $this->jwsSerializerManagerFactory = new Serializer\JWESerializerManagerFactory(); + $this->jwsSerializerManagerFactory->add(new Serializer\CompactSerializer()); + $this->jwsSerializerManagerFactory->add(new Serializer\JSONFlattenedSerializer()); + $this->jwsSerializerManagerFactory->add(new Serializer\JSONGeneralSerializer()); + } + + return $this->jwsSerializerManagerFactory; + } + + protected function getJWESerializerManager(): Serializer\JWESerializerManager + { + if (null === $this->jwsSerializerManager) { + $this->jwsSerializerManager = new Serializer\JWESerializerManager([ + new Serializer\CompactSerializer(), + new Serializer\JSONFlattenedSerializer(), + new Serializer\JSONGeneralSerializer(), + ]); + } + + return $this->jwsSerializerManager; + } +} diff --git a/vendor/web-token/jwt-encryption/Tests/InvalidCurveAttackTest.php b/vendor/web-token/jwt-encryption/Tests/InvalidCurveAttackTest.php new file mode 100644 index 000000000..98ae7823e --- /dev/null +++ b/vendor/web-token/jwt-encryption/Tests/InvalidCurveAttackTest.php @@ -0,0 +1,65 @@ +getJWEDecrypterFactory()->create(['ECDH-ES+A128KW'], ['A128CBC-HS256'], ['DEF']); + + $loaded_compact_json = $this->getJWESerializerManager()->unserialize($maliciousJWE); + $privateKey = new JWK([ + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => 'weNJy2HscCSM6AEDTDg04biOvhFhyyWvOHQfeF_PxMQ', + 'y' => 'e8lnCO-AlStT-NJVX-crhB7QRYhiix03illJOVAOyck', + 'd' => 'VEmDZpDXXK8p8N0Cndsxs924q6nS1RXFASRl6BfUqdw', + ]); + static::assertFalse($jweDecrypter->decryptUsingKey($loaded_compact_json, $privateKey, 0)); + } + + /** + * @test + */ + public function curveCheckNegativeP256AttackPt2(): void + { + // The malicious JWE contains a public key with order 2447 + $maliciousJWE = 'eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImVuYyI6IkExMjhDQkMtSFMyNTYiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiWE9YR1E5XzZRQ3ZCZzN1OHZDSS1VZEJ2SUNBRWNOTkJyZnFkN3RHN29RNCIsInkiOiJoUW9XTm90bk56S2x3aUNuZUprTElxRG5UTnc3SXNkQkM1M1ZVcVZqVkpjIiwiY3J2IjoiUC0yNTYifX0.UGb3hX3ePAvtFB9TCdWsNkFTv9QWxSr3MpYNiSBdW630uRXRBT3sxw.6VpU84oMob16DxOR98YTRw.y1UslvtkoWdl9HpugfP0rSAkTw1xhm_LbK1iRXzGdpYqNwIG5VU33UBpKAtKFBoA1Kk_sYtfnHYAvn-aes4FTg.UZPN8h7FcvA5MIOq-Pkj8A'; + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['ECDH-ES+A128KW'], ['A128CBC-HS256'], ['DEF']); + + $loaded_compact_json = $this->getJWESerializerManager()->unserialize($maliciousJWE); + $privateKey = new JWK([ + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => 'weNJy2HscCSM6AEDTDg04biOvhFhyyWvOHQfeF_PxMQ', + 'y' => 'e8lnCO-AlStT-NJVX-crhB7QRYhiix03illJOVAOyck', + 'd' => 'VEmDZpDXXK8p8N0Cndsxs924q6nS1RXFASRl6BfUqdw', + ]); + static::assertFalse($jweDecrypter->decryptUsingKey($loaded_compact_json, $privateKey, 0)); + } +} diff --git a/vendor/web-token/jwt-encryption/Tests/JWEFlattenedTest.php b/vendor/web-token/jwt-encryption/Tests/JWEFlattenedTest.php new file mode 100644 index 000000000..b6c4ae659 --- /dev/null +++ b/vendor/web-token/jwt-encryption/Tests/JWEFlattenedTest.php @@ -0,0 +1,68 @@ +getJWEDecrypterFactory()->create(['A128KW'], ['A128CBC-HS256'], ['DEF']); + + $loaded = $this->getJWESerializerManager()->unserialize('{"protected":"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0","unprotected":{"jku":"https://server.example.com/keys.jwks"},"header":{"alg":"A128KW","kid":"7"},"encrypted_key":"6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ","iv":"AxY8DCtDaGlsbGljb3RoZQ","ciphertext":"KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY","tag":"Mz-VPPyU4RlcuYv1IwIvzw"}'); + + static::assertEquals('A128KW', $loaded->getRecipient(0)->getHeaderParameter('alg')); + static::assertEquals('A128CBC-HS256', $loaded->getSharedProtectedHeaderParameter('enc')); + static::assertNull($loaded->getPayload()); + static::assertTrue($jweDecrypter->decryptUsingKeySet($loaded, $this->getSymmetricKeySet(), 0)); + static::assertEquals('Live long and prosper.', $loaded->getPayload()); + } + + private function getSymmetricKeySet(): JWKSet + { + $keys = ['keys' => [ + [ + 'kid' => 'DIR_1', + 'kty' => 'oct', + 'k' => Base64Url::encode(hex2bin('00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F')), + ], + [ + 'kty' => 'oct', + 'k' => 'f5aN5V6iihwQVqP-tPNNtkIJNCwUb9-JukCIKkF0rNfxqxA771RJynYAT2xtzAP0MYaR7U5fMP_wvbRQq5l38Q', + ], + [ + 'kty' => 'oct', + 'k' => 'GawgguFyGrWKav7AX4VKUg', + ], + [ + 'kty' => 'oct', + 'k' => 'AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow', + ], + ]]; + + return JWKSet::createFromKeyData($keys); + } +} diff --git a/vendor/web-token/jwt-encryption/Tests/JWELoaderTest.php b/vendor/web-token/jwt-encryption/Tests/JWELoaderTest.php new file mode 100644 index 000000000..7b52811ec --- /dev/null +++ b/vendor/web-token/jwt-encryption/Tests/JWELoaderTest.php @@ -0,0 +1,126 @@ +expectException(Exception::class); + $this->expectExceptionMessage('Unable to load and decrypt the token.'); + + $token = '{"protected":"eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIn0","encrypted_key":"CBI6oDw8MydIx1IBntf_lQcw2MmJKIQx","iv":"Qx0pmsDa8KnJc9Jo","ciphertext":"AwliP-KmWgsZ37BvzCefNen6VTbRK3QMA4TkvRkH0tP1bTdhtFJgJxeVmJkLD61A1hnWGetdg11c9ADsnWgL56NyxwSYjU1ZEHcGkd3EkU0vjHi9gTlb90qSYFfeF0LwkcTtjbYKCsiNJQkcIp1yeM03OmuiYSoYJVSpf7ej6zaYcMv3WwdxDFl8REwOhNImk2Xld2JXq6BR53TSFkyT7PwVLuq-1GwtGHlQeg7gDT6xW0JqHDPn_H-puQsmthc9Zg0ojmJfqqFvETUxLAF-KjcBTS5dNy6egwkYtOt8EIHK-oEsKYtZRaa8Z7MOZ7UGxGIMvEmxrGCPeJa14slv2-gaqK0kEThkaSqdYw0FkQZF","tag":"ER7MWJZ1FBI_NKvn7Zb1Lw"}'; + $key = new JWK([ + 'kty' => 'EC', + 'kid' => 'peregrin.took@tuckborough.example', + 'use' => 'enc', + 'crv' => 'P-384', + 'x' => 'YU4rRUzdmVqmRtWOs2OpDE_T5fsNIodcG8G5FWPrTPMyxpzsSOGaQLpe2FpxBmu2', + 'y' => 'A8-yxCHxkfBz3hKZfI1jUYMjUhsEveZ9THuwFjH2sCNdtksRJU7D5-SkgaFL1ETP', + ]); + + $this->getJWELoader()->loadAndDecryptWithKey($token, $key, $recipient); + } + + /** + * @test + */ + public function theTokenCannotBeVerifiedBecauseOfAnUnsupportedAlgorithm(): void + { + $this->expectException(Exception::class); + $this->expectExceptionMessage('Unable to load and decrypt the token.'); + + $token = 'eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImtpZCI6InBlcmVncmluLnRvb2tAdHVja2Jvcm91Z2guZXhhbXBsZSIsImVwayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMzg0IiwieCI6InVCbzRrSFB3Nmtiang1bDB4b3dyZF9vWXpCbWF6LUdLRlp1NHhBRkZrYllpV2d1dEVLNml1RURzUTZ3TmROZzMiLCJ5Ijoic3AzcDVTR2haVkMyZmFYdW1JLWU5SlUyTW84S3BvWXJGRHI1eVBOVnRXNFBnRXdaT3lRVEEtSmRhWTh0YjdFMCJ9LCJlbmMiOiJBMTI4R0NNIn0.0DJjBXri_kBcC46IkU5_Jk9BqaQeHdv2.mH-G2zVqgztUtnW_.tkZuOO9h95OgHJmkkrfLBisku8rGf6nzVxhRM3sVOhXgz5NJ76oID7lpnAi_cPWJRCjSpAaUZ5dOR3Spy7QuEkmKx8-3RCMhSYMzsXaEwDdXta9Mn5B7cCBoJKB0IgEnj_qfo1hIi-uEkUpOZ8aLTZGHfpl05jMwbKkTe2yK3mjF6SBAsgicQDVCkcY9BLluzx1RmC3ORXaM0JaHPB93YcdSDGgpgBWMVrNU1ErkjcMqMoT_wtCex3w03XdLkjXIuEr2hWgeP-nkUZTPU9EoGSPj6fAS-bSz87RCPrxZdj_iVyC6QWcqAu07WNhjzJEPc4jVntRJ6K53NgPQ5p99l3Z408OUqj4ioYezbS6vTPlQ.WuGzxmcreYjpHGJoa17EBg'; + $key = new JWK([ + 'kty' => 'EC', + 'kid' => 'peregrin.took@tuckborough.example', + 'use' => 'enc', + 'crv' => 'P-384', + 'x' => 'YU4rRUzdmVqmRtWOs2OpDE_T5fsNIodcG8G5FWPrTPMyxpzsSOGaQLpe2FpxBmu2', + 'y' => 'A8-yxCHxkfBz3hKZfI1jUYMjUhsEveZ9THuwFjH2sCNdtksRJU7D5-SkgaFL1ETP', + ]); + + $this->getJWELoader()->loadAndDecryptWithKey($token, $key, $recipient); + } + + /** + * @test + */ + public function theTokenCannotBeVerifiedBecauseOfABadKey(): void + { + $this->expectException(Exception::class); + $this->expectExceptionMessage('Unable to load and decrypt the token.'); + + $token = 'eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIn0.CBI6oDw8MydIx1IBntf_lQcw2MmJKIQx.Qx0pmsDa8KnJc9Jo.AwliP-KmWgsZ37BvzCefNen6VTbRK3QMA4TkvRkH0tP1bTdhtFJgJxeVmJkLD61A1hnWGetdg11c9ADsnWgL56NyxwSYjU1ZEHcGkd3EkU0vjHi9gTlb90qSYFfeF0LwkcTtjbYKCsiNJQkcIp1yeM03OmuiYSoYJVSpf7ej6zaYcMv3WwdxDFl8REwOhNImk2Xld2JXq6BR53TSFkyT7PwVLuq-1GwtGHlQeg7gDT6xW0JqHDPn_H-puQsmthc9Zg0ojmJfqqFvETUxLAF-KjcBTS5dNy6egwkYtOt8EIHK-oEsKYtZRaa8Z7MOZ7UGxGIMvEmxrGCPeJa14slv2-gaqK0kEThkaSqdYw0FkQZF.ER7MWJZ1FBI_NKvn7Zb1Lw'; + $key = new JWK([ + 'kty' => 'EC', + 'kid' => 'peregrin.took@tuckborough.example', + 'use' => 'enc', + 'crv' => 'P-384', + 'x' => 'YU4rRUzdmVqmRtWOs2OpDE_T5fsNIodcG8G5FWPrTPMyxpzsSOGaQLpe2FpxBmu2', + 'y' => 'A8-yxCHxkfBz3hKZfI1jUYMjUhsEveZ9THuwFjH2sCNdtksRJU7D5-SkgaFL1ETP', + ]); + + $this->getJWELoader()->loadAndDecryptWithKey($token, $key, $recipient); + } + + /** + * @test + */ + public function theJweLoaderCanLoadAndDecryptAToken(): void + { + $token = 'eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIn0.CBI6oDw8MydIx1IBntf_lQcw2MmJKIQx.Qx0pmsDa8KnJc9Jo.AwliP-KmWgsZ37BvzCefNen6VTbRK3QMA4TkvRkH0tP1bTdhtFJgJxeVmJkLD61A1hnWGetdg11c9ADsnWgL56NyxwSYjU1ZEHcGkd3EkU0vjHi9gTlb90qSYFfeF0LwkcTtjbYKCsiNJQkcIp1yeM03OmuiYSoYJVSpf7ej6zaYcMv3WwdxDFl8REwOhNImk2Xld2JXq6BR53TSFkyT7PwVLuq-1GwtGHlQeg7gDT6xW0JqHDPn_H-puQsmthc9Zg0ojmJfqqFvETUxLAF-KjcBTS5dNy6egwkYtOt8EIHK-oEsKYtZRaa8Z7MOZ7UGxGIMvEmxrGCPeJa14slv2-gaqK0kEThkaSqdYw0FkQZF.ER7MWJZ1FBI_NKvn7Zb1Lw'; + $key = new JWK([ + 'kty' => 'oct', + 'kid' => '81b20965-8332-43d9-a468-82160ad91ac8', + 'use' => 'enc', + 'alg' => 'A128KW', + 'k' => 'GZy6sIZ6wl9NJOKB-jnmVQ', + ]); + $recipient = 0; + $jwe = $this->getJWELoader()->loadAndDecryptWithKey($token, $key, $recipient); + + static::assertEquals('You can trust us to stick with you through thick and thin–to the bitter end. And you can trust us to keep any secret of yours–closer than you keep it yourself. But you cannot trust us to let you face trouble alone, and go off without a word. We are your friends, Frodo.', $jwe->getPayload()); + static::assertEquals(0, $recipient); + } + + private function getJWELoader(): JWELoader + { + if (null === $this->jweLoader) { + $this->jweLoader = $this->getJWELoaderFactory()->create(['jwe_compact'], ['A128KW'], ['A128GCM'], ['DEF']); + } + + return $this->jweLoader; + } +} diff --git a/vendor/web-token/jwt-encryption/Tests/JWESplitTest.php b/vendor/web-token/jwt-encryption/Tests/JWESplitTest.php new file mode 100644 index 000000000..fdb6ae234 --- /dev/null +++ b/vendor/web-token/jwt-encryption/Tests/JWESplitTest.php @@ -0,0 +1,57 @@ +unserialize($input); + $split = $jwe->split(); + + static::assertEquals(3, $jwe->countRecipients()); + static::assertEquals(3, count($split)); + + for ($i = 0; $i < $jwe->countRecipients(); ++$i) { + $recipient1 = $jwe->getRecipient($i); + $tempJwe = $split[$i]; + static::assertEquals(1, $tempJwe->countRecipients()); + static::assertEquals($jwe->getAAD(), $tempJwe->getAAD()); + static::assertEquals($jwe->getCiphertext(), $tempJwe->getCiphertext()); + static::assertEquals($jwe->getEncodedSharedProtectedHeader(), $tempJwe->getEncodedSharedProtectedHeader()); + static::assertEquals($jwe->getSharedProtectedHeader(), $tempJwe->getSharedProtectedHeader()); + static::assertEquals($jwe->getSharedHeader(), $tempJwe->getSharedHeader()); + static::assertEquals($jwe->getIV(), $tempJwe->getIV()); + static::assertEquals($jwe->getTag(), $tempJwe->getTag()); + static::assertEquals($jwe->isEncrypted(), $tempJwe->isEncrypted()); + + $recipient2 = $tempJwe->getRecipient(0); + static::assertEquals($recipient1->getHeader(), $recipient2->getHeader()); + static::assertEquals($recipient1->getEncryptedKey(), $recipient2->getEncryptedKey()); + } + } +} diff --git a/vendor/web-token/jwt-encryption/Tests/RFC7520/A128KWAndA128GCMEncryptionProtectedContentOnlyTest.php b/vendor/web-token/jwt-encryption/Tests/RFC7520/A128KWAndA128GCMEncryptionProtectedContentOnlyTest.php new file mode 100644 index 000000000..5b89f3e7d --- /dev/null +++ b/vendor/web-token/jwt-encryption/Tests/RFC7520/A128KWAndA128GCMEncryptionProtectedContentOnlyTest.php @@ -0,0 +1,141 @@ + 'oct', + 'kid' => '81b20965-8332-43d9-a468-82160ad91ac8', + 'use' => 'enc', + 'alg' => 'A128KW', + 'k' => 'GZy6sIZ6wl9NJOKB-jnmVQ', + ]); + + $protectedHeader = [ + ]; + + $header = [ + 'enc' => 'A128GCM', + 'alg' => 'A128KW', + 'kid' => '81b20965-8332-43d9-a468-82160ad91ac8', + ]; + + $expected_flattened_json = '{"unprotected":{"alg":"A128KW","kid":"81b20965-8332-43d9-a468-82160ad91ac8","enc":"A128GCM"},"encrypted_key":"244YHfO_W7RMpQW81UjQrZcq5LSyqiPv","iv":"YihBoVOGsR1l7jCD","ciphertext":"qtPIMMaOBRgASL10dNQhOa7Gqrk7Eal1vwht7R4TT1uq-arsVCPaIeFwQfzrSS6oEUWbBtxEasE0vC6r7sphyVziMCVJEuRJyoAHFSP3eqQPb4Ic1SDSqyXjw_L3svybhHYUGyQuTmUQEDjgjJfBOifwHIsDsRPeBz1NomqeifVPq5GTCWFo5k_MNIQURR2Wj0AHC2k7JZfu2iWjUHLF8ExFZLZ4nlmsvJu_mvifMYiikfNfsZAudISOa6O73yPZtL04k_1FI7WDfrb2w7OqKLWDXzlpcxohPVOLQwpA3mFNRKdY-bQz4Z4KX9lfz1cne31N4-8BKmojpw-OdQjKdLOGkC445Fb_K1tlDQXw2sBF","tag":"e2m0Vm7JvjK2VpCKXS-kyg"}'; + $expected_json = '{"recipients":[{"encrypted_key":"244YHfO_W7RMpQW81UjQrZcq5LSyqiPv"}],"unprotected":{"alg":"A128KW","kid":"81b20965-8332-43d9-a468-82160ad91ac8","enc":"A128GCM"},"iv":"YihBoVOGsR1l7jCD","ciphertext":"qtPIMMaOBRgASL10dNQhOa7Gqrk7Eal1vwht7R4TT1uq-arsVCPaIeFwQfzrSS6oEUWbBtxEasE0vC6r7sphyVziMCVJEuRJyoAHFSP3eqQPb4Ic1SDSqyXjw_L3svybhHYUGyQuTmUQEDjgjJfBOifwHIsDsRPeBz1NomqeifVPq5GTCWFo5k_MNIQURR2Wj0AHC2k7JZfu2iWjUHLF8ExFZLZ4nlmsvJu_mvifMYiikfNfsZAudISOa6O73yPZtL04k_1FI7WDfrb2w7OqKLWDXzlpcxohPVOLQwpA3mFNRKdY-bQz4Z4KX9lfz1cne31N4-8BKmojpw-OdQjKdLOGkC445Fb_K1tlDQXw2sBF","tag":"e2m0Vm7JvjK2VpCKXS-kyg"}'; + $expected_iv = 'YihBoVOGsR1l7jCD'; + $expected_encrypted_key = '244YHfO_W7RMpQW81UjQrZcq5LSyqiPv'; + $expected_ciphertext = 'qtPIMMaOBRgASL10dNQhOa7Gqrk7Eal1vwht7R4TT1uq-arsVCPaIeFwQfzrSS6oEUWbBtxEasE0vC6r7sphyVziMCVJEuRJyoAHFSP3eqQPb4Ic1SDSqyXjw_L3svybhHYUGyQuTmUQEDjgjJfBOifwHIsDsRPeBz1NomqeifVPq5GTCWFo5k_MNIQURR2Wj0AHC2k7JZfu2iWjUHLF8ExFZLZ4nlmsvJu_mvifMYiikfNfsZAudISOa6O73yPZtL04k_1FI7WDfrb2w7OqKLWDXzlpcxohPVOLQwpA3mFNRKdY-bQz4Z4KX9lfz1cne31N4-8BKmojpw-OdQjKdLOGkC445Fb_K1tlDQXw2sBF'; + $expected_tag = 'e2m0Vm7JvjK2VpCKXS-kyg'; + + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['A128KW'], ['A128GCM'], ['DEF']); + + $loaded_flattened_json = $this->getJWESerializerManager()->unserialize($expected_flattened_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_flattened_json, $private_key, 0)); + + $loaded_json = $this->getJWESerializerManager()->unserialize($expected_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $private_key, 0)); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_flattened_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_flattened_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_flattened_json->getIV())); + static::assertEquals($expected_encrypted_key, Base64Url::encode($loaded_flattened_json->getRecipient(0)->getEncryptedKey())); + static::assertEquals($header, $loaded_flattened_json->getSharedHeader()); + static::assertEquals($expected_tag, Base64Url::encode($loaded_flattened_json->getTag())); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_json->getIV())); + static::assertEquals($expected_encrypted_key, Base64Url::encode($loaded_json->getRecipient(0)->getEncryptedKey())); + static::assertEquals($header, $loaded_json->getSharedHeader()); + static::assertEquals($expected_tag, Base64Url::encode($loaded_json->getTag())); + + static::assertEquals($expected_payload, $loaded_flattened_json->getPayload()); + static::assertEquals($expected_payload, $loaded_json->getPayload()); + } + + /** + * Same input as before, but we perform the encryption first. + * + * @test + */ + public function a128KWAndA128GCMEncryptionProtectedContentOnlyBis(): void + { + $expected_payload = "You can trust us to stick with you through thick and thin\xe2\x80\x93to the bitter end. And you can trust us to keep any secret of yours\xe2\x80\x93closer than you keep it yourself. But you cannot trust us to let you face trouble alone, and go off without a word. We are your friends, Frodo."; + + $private_key = new JWK([ + 'kty' => 'oct', + 'kid' => '81b20965-8332-43d9-a468-82160ad91ac8', + 'use' => 'enc', + 'alg' => 'A128KW', + 'k' => 'GZy6sIZ6wl9NJOKB-jnmVQ', + ]); + + $protectedHeader = [ + ]; + + $header = [ + 'enc' => 'A128GCM', + 'alg' => 'A128KW', + 'kid' => '81b20965-8332-43d9-a468-82160ad91ac8', + ]; + + $jweBuilder = $this->getJWEBuilderFactory()->create(['A128KW'], ['A128GCM'], ['DEF']); + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['A128KW'], ['A128GCM'], ['DEF']); + + $jwe = $jweBuilder + ->create()->withPayload($expected_payload) + ->withSharedProtectedHeader($protectedHeader) + ->withSharedHeader($header) + ->addRecipient($private_key) + ->build() + ; + + $loaded_flattened_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_json_flattened', $jwe, 0)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_flattened_json, $private_key, 0)); + + $loaded_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_json_general', $jwe)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $private_key, 0)); + + static::assertEquals($protectedHeader, $loaded_flattened_json->getSharedProtectedHeader()); + static::assertEquals($header, $loaded_flattened_json->getSharedHeader()); + + static::assertEquals($protectedHeader, $loaded_json->getSharedProtectedHeader()); + static::assertEquals($header, $loaded_json->getSharedHeader()); + + static::assertEquals($expected_payload, $loaded_flattened_json->getPayload()); + static::assertEquals($expected_payload, $loaded_json->getPayload()); + } +} diff --git a/vendor/web-token/jwt-encryption/Tests/RFC7520/A128KWAndA128GCMEncryptionTest.php b/vendor/web-token/jwt-encryption/Tests/RFC7520/A128KWAndA128GCMEncryptionTest.php new file mode 100644 index 000000000..d8f1237ec --- /dev/null +++ b/vendor/web-token/jwt-encryption/Tests/RFC7520/A128KWAndA128GCMEncryptionTest.php @@ -0,0 +1,147 @@ + 'oct', + 'kid' => '81b20965-8332-43d9-a468-82160ad91ac8', + 'use' => 'enc', + 'alg' => 'A128KW', + 'k' => 'GZy6sIZ6wl9NJOKB-jnmVQ', + ]); + + $protectedHeader = [ + 'alg' => 'A128KW', + 'kid' => '81b20965-8332-43d9-a468-82160ad91ac8', + 'enc' => 'A128GCM', + ]; + + $expected_compact_json = 'eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIn0.CBI6oDw8MydIx1IBntf_lQcw2MmJKIQx.Qx0pmsDa8KnJc9Jo.AwliP-KmWgsZ37BvzCefNen6VTbRK3QMA4TkvRkH0tP1bTdhtFJgJxeVmJkLD61A1hnWGetdg11c9ADsnWgL56NyxwSYjU1ZEHcGkd3EkU0vjHi9gTlb90qSYFfeF0LwkcTtjbYKCsiNJQkcIp1yeM03OmuiYSoYJVSpf7ej6zaYcMv3WwdxDFl8REwOhNImk2Xld2JXq6BR53TSFkyT7PwVLuq-1GwtGHlQeg7gDT6xW0JqHDPn_H-puQsmthc9Zg0ojmJfqqFvETUxLAF-KjcBTS5dNy6egwkYtOt8EIHK-oEsKYtZRaa8Z7MOZ7UGxGIMvEmxrGCPeJa14slv2-gaqK0kEThkaSqdYw0FkQZF.ER7MWJZ1FBI_NKvn7Zb1Lw'; + $expected_flattened_json = '{"protected":"eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIn0","encrypted_key":"CBI6oDw8MydIx1IBntf_lQcw2MmJKIQx","iv":"Qx0pmsDa8KnJc9Jo","ciphertext":"AwliP-KmWgsZ37BvzCefNen6VTbRK3QMA4TkvRkH0tP1bTdhtFJgJxeVmJkLD61A1hnWGetdg11c9ADsnWgL56NyxwSYjU1ZEHcGkd3EkU0vjHi9gTlb90qSYFfeF0LwkcTtjbYKCsiNJQkcIp1yeM03OmuiYSoYJVSpf7ej6zaYcMv3WwdxDFl8REwOhNImk2Xld2JXq6BR53TSFkyT7PwVLuq-1GwtGHlQeg7gDT6xW0JqHDPn_H-puQsmthc9Zg0ojmJfqqFvETUxLAF-KjcBTS5dNy6egwkYtOt8EIHK-oEsKYtZRaa8Z7MOZ7UGxGIMvEmxrGCPeJa14slv2-gaqK0kEThkaSqdYw0FkQZF","tag":"ER7MWJZ1FBI_NKvn7Zb1Lw"}'; + $expected_json = '{"recipients":[{"encrypted_key":"CBI6oDw8MydIx1IBntf_lQcw2MmJKIQx"}],"protected":"eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIn0","iv":"Qx0pmsDa8KnJc9Jo","ciphertext":"AwliP-KmWgsZ37BvzCefNen6VTbRK3QMA4TkvRkH0tP1bTdhtFJgJxeVmJkLD61A1hnWGetdg11c9ADsnWgL56NyxwSYjU1ZEHcGkd3EkU0vjHi9gTlb90qSYFfeF0LwkcTtjbYKCsiNJQkcIp1yeM03OmuiYSoYJVSpf7ej6zaYcMv3WwdxDFl8REwOhNImk2Xld2JXq6BR53TSFkyT7PwVLuq-1GwtGHlQeg7gDT6xW0JqHDPn_H-puQsmthc9Zg0ojmJfqqFvETUxLAF-KjcBTS5dNy6egwkYtOt8EIHK-oEsKYtZRaa8Z7MOZ7UGxGIMvEmxrGCPeJa14slv2-gaqK0kEThkaSqdYw0FkQZF","tag":"ER7MWJZ1FBI_NKvn7Zb1Lw"}'; + $expected_iv = 'Qx0pmsDa8KnJc9Jo'; + $expected_encrypted_key = 'CBI6oDw8MydIx1IBntf_lQcw2MmJKIQx'; + $expected_ciphertext = 'AwliP-KmWgsZ37BvzCefNen6VTbRK3QMA4TkvRkH0tP1bTdhtFJgJxeVmJkLD61A1hnWGetdg11c9ADsnWgL56NyxwSYjU1ZEHcGkd3EkU0vjHi9gTlb90qSYFfeF0LwkcTtjbYKCsiNJQkcIp1yeM03OmuiYSoYJVSpf7ej6zaYcMv3WwdxDFl8REwOhNImk2Xld2JXq6BR53TSFkyT7PwVLuq-1GwtGHlQeg7gDT6xW0JqHDPn_H-puQsmthc9Zg0ojmJfqqFvETUxLAF-KjcBTS5dNy6egwkYtOt8EIHK-oEsKYtZRaa8Z7MOZ7UGxGIMvEmxrGCPeJa14slv2-gaqK0kEThkaSqdYw0FkQZF'; + $expected_tag = 'ER7MWJZ1FBI_NKvn7Zb1Lw'; + + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['A128KW'], ['A128GCM'], ['DEF']); + + $loaded_compact_json = $this->getJWESerializerManager()->unserialize($expected_compact_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_compact_json, $private_key, 0)); + + $loaded_flattened_json = $this->getJWESerializerManager()->unserialize($expected_flattened_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_flattened_json, $private_key, 0)); + + $loaded_json = $this->getJWESerializerManager()->unserialize($expected_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $private_key, 0)); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_compact_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_compact_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_compact_json->getIV())); + static::assertEquals($expected_encrypted_key, Base64Url::encode($loaded_compact_json->getRecipient(0)->getEncryptedKey())); + static::assertEquals($expected_tag, Base64Url::encode($loaded_compact_json->getTag())); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_flattened_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_flattened_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_flattened_json->getIV())); + static::assertEquals($expected_encrypted_key, Base64Url::encode($loaded_flattened_json->getRecipient(0)->getEncryptedKey())); + static::assertEquals($expected_tag, Base64Url::encode($loaded_flattened_json->getTag())); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_json->getIV())); + static::assertEquals($expected_encrypted_key, Base64Url::encode($loaded_json->getRecipient(0)->getEncryptedKey())); + static::assertEquals($expected_tag, Base64Url::encode($loaded_json->getTag())); + + static::assertEquals($expected_payload, $loaded_compact_json->getPayload()); + static::assertEquals($expected_payload, $loaded_flattened_json->getPayload()); + static::assertEquals($expected_payload, $loaded_json->getPayload()); + } + + /** + * Same input as before, but we perform the encryption first. + * + * @test + */ + public function a128KWAndA128GCMEncryptionBis(): void + { + $expected_payload = "You can trust us to stick with you through thick and thin\xe2\x80\x93to the bitter end. And you can trust us to keep any secret of yours\xe2\x80\x93closer than you keep it yourself. But you cannot trust us to let you face trouble alone, and go off without a word. We are your friends, Frodo."; + + $private_key = new JWK([ + 'kty' => 'oct', + 'kid' => '81b20965-8332-43d9-a468-82160ad91ac8', + 'use' => 'enc', + 'alg' => 'A128KW', + 'k' => 'GZy6sIZ6wl9NJOKB-jnmVQ', + ]); + + $protectedHeader = [ + 'alg' => 'A128KW', + 'kid' => '81b20965-8332-43d9-a468-82160ad91ac8', + 'enc' => 'A128GCM', + ]; + + $jweBuilder = $this->getJWEBuilderFactory()->create(['A128KW'], ['A128GCM'], ['DEF']); + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['A128KW'], ['A128GCM'], ['DEF']); + + $jwe = $jweBuilder + ->create()->withPayload($expected_payload) + ->withSharedProtectedHeader($protectedHeader) + ->addRecipient($private_key) + ->build() + ; + + $loaded_compact_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_compact', $jwe, 0)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_compact_json, $private_key, 0)); + + $loaded_flattened_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_json_flattened', $jwe, 0)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_flattened_json, $private_key, 0)); + + $loaded_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_json_general', $jwe)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $private_key, 0)); + + static::assertEquals($protectedHeader, $loaded_compact_json->getSharedProtectedHeader()); + + static::assertEquals($protectedHeader, $loaded_flattened_json->getSharedProtectedHeader()); + + static::assertEquals($protectedHeader, $loaded_json->getSharedProtectedHeader()); + + static::assertEquals($expected_payload, $loaded_compact_json->getPayload()); + static::assertEquals($expected_payload, $loaded_flattened_json->getPayload()); + static::assertEquals($expected_payload, $loaded_json->getPayload()); + } +} diff --git a/vendor/web-token/jwt-encryption/Tests/RFC7520/A128KWAndA128GCMEncryptionWithAdditionalAuthenticatedDataTest.php b/vendor/web-token/jwt-encryption/Tests/RFC7520/A128KWAndA128GCMEncryptionWithAdditionalAuthenticatedDataTest.php new file mode 100644 index 000000000..5cdf075d7 --- /dev/null +++ b/vendor/web-token/jwt-encryption/Tests/RFC7520/A128KWAndA128GCMEncryptionWithAdditionalAuthenticatedDataTest.php @@ -0,0 +1,133 @@ + 'oct', + 'kid' => '81b20965-8332-43d9-a468-82160ad91ac8', + 'use' => 'enc', + 'alg' => 'A128KW', + 'k' => 'GZy6sIZ6wl9NJOKB-jnmVQ', + ]); + + $protectedHeader = [ + 'alg' => 'A128KW', + 'kid' => '81b20965-8332-43d9-a468-82160ad91ac8', + 'enc' => 'A128GCM', + ]; + + $expected_flattened_json = '{"protected":"eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIn0","encrypted_key":"4YiiQ_ZzH76TaIkJmYfRFgOV9MIpnx4X","aad":"WyJ2Y2FyZCIsW1sidmVyc2lvbiIse30sInRleHQiLCI0LjAiXSxbImZuIix7fSwidGV4dCIsIk1lcmlhZG9jIEJyYW5keWJ1Y2siXSxbIm4iLHt9LCJ0ZXh0IixbIkJyYW5keWJ1Y2siLCJNZXJpYWRvYyIsIk1yLiIsIiJdXSxbImJkYXkiLHt9LCJ0ZXh0IiwiVEEgMjk4MiJdLFsiZ2VuZGVyIix7fSwidGV4dCIsIk0iXV1d","iv":"veCx9ece2orS7c_N","ciphertext":"Z_3cbr0k3bVM6N3oSNmHz7Lyf3iPppGf3Pj17wNZqteJ0Ui8p74SchQP8xygM1oFRWCNzeIa6s6BcEtp8qEFiqTUEyiNkOWDNoF14T_4NFqF-p2Mx8zkbKxI7oPK8KNarFbyxIDvICNqBLba-v3uzXBdB89fzOI-Lv4PjOFAQGHrgv1rjXAmKbgkft9cB4WeyZw8MldbBhc-V_KWZslrsLNygon_JJWd_ek6LQn5NRehvApqf9ZrxB4aq3FXBxOxCys35PhCdaggy2kfUfl2OkwKnWUbgXVD1C6HxLIlqHhCwXDG59weHrRDQeHyMRoBljoV3X_bUTJDnKBFOod7nLz-cj48JMx3SnCZTpbQAkFV","tag":"vOaH_Rajnpy_3hOtqvZHRA"}'; + $expected_json = '{"recipients":[{"encrypted_key":"4YiiQ_ZzH76TaIkJmYfRFgOV9MIpnx4X"}],"protected":"eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIn0","iv":"veCx9ece2orS7c_N","aad":"WyJ2Y2FyZCIsW1sidmVyc2lvbiIse30sInRleHQiLCI0LjAiXSxbImZuIix7fSwidGV4dCIsIk1lcmlhZG9jIEJyYW5keWJ1Y2siXSxbIm4iLHt9LCJ0ZXh0IixbIkJyYW5keWJ1Y2siLCJNZXJpYWRvYyIsIk1yLiIsIiJdXSxbImJkYXkiLHt9LCJ0ZXh0IiwiVEEgMjk4MiJdLFsiZ2VuZGVyIix7fSwidGV4dCIsIk0iXV1d","ciphertext":"Z_3cbr0k3bVM6N3oSNmHz7Lyf3iPppGf3Pj17wNZqteJ0Ui8p74SchQP8xygM1oFRWCNzeIa6s6BcEtp8qEFiqTUEyiNkOWDNoF14T_4NFqF-p2Mx8zkbKxI7oPK8KNarFbyxIDvICNqBLba-v3uzXBdB89fzOI-Lv4PjOFAQGHrgv1rjXAmKbgkft9cB4WeyZw8MldbBhc-V_KWZslrsLNygon_JJWd_ek6LQn5NRehvApqf9ZrxB4aq3FXBxOxCys35PhCdaggy2kfUfl2OkwKnWUbgXVD1C6HxLIlqHhCwXDG59weHrRDQeHyMRoBljoV3X_bUTJDnKBFOod7nLz-cj48JMx3SnCZTpbQAkFV","tag":"vOaH_Rajnpy_3hOtqvZHRA"}'; + $expected_iv = 'veCx9ece2orS7c_N'; + $expected_aad = '["vcard",[["version",{},"text","4.0"],["fn",{},"text","Meriadoc Brandybuck"],["n",{},"text",["Brandybuck","Meriadoc","Mr.",""]],["bday",{},"text","TA 2982"],["gender",{},"text","M"]]]'; + $expected_encrypted_key = '4YiiQ_ZzH76TaIkJmYfRFgOV9MIpnx4X'; + $expected_ciphertext = 'Z_3cbr0k3bVM6N3oSNmHz7Lyf3iPppGf3Pj17wNZqteJ0Ui8p74SchQP8xygM1oFRWCNzeIa6s6BcEtp8qEFiqTUEyiNkOWDNoF14T_4NFqF-p2Mx8zkbKxI7oPK8KNarFbyxIDvICNqBLba-v3uzXBdB89fzOI-Lv4PjOFAQGHrgv1rjXAmKbgkft9cB4WeyZw8MldbBhc-V_KWZslrsLNygon_JJWd_ek6LQn5NRehvApqf9ZrxB4aq3FXBxOxCys35PhCdaggy2kfUfl2OkwKnWUbgXVD1C6HxLIlqHhCwXDG59weHrRDQeHyMRoBljoV3X_bUTJDnKBFOod7nLz-cj48JMx3SnCZTpbQAkFV'; + $expected_tag = 'vOaH_Rajnpy_3hOtqvZHRA'; + + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['A128KW'], ['A128GCM'], ['DEF']); + + $loaded_flattened_json = $this->getJWESerializerManager()->unserialize($expected_flattened_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_flattened_json, $private_key, 0)); + + $loaded_json = $this->getJWESerializerManager()->unserialize($expected_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $private_key, 0)); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_flattened_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_flattened_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_flattened_json->getIV())); + static::assertEquals($expected_encrypted_key, Base64Url::encode($loaded_flattened_json->getRecipient(0)->getEncryptedKey())); + static::assertEquals($expected_tag, Base64Url::encode($loaded_flattened_json->getTag())); + static::assertEquals($expected_aad, $loaded_flattened_json->getAAD()); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_json->getIV())); + static::assertEquals($expected_encrypted_key, Base64Url::encode($loaded_json->getRecipient(0)->getEncryptedKey())); + static::assertEquals($expected_tag, Base64Url::encode($loaded_json->getTag())); + static::assertEquals($expected_aad, $loaded_json->getAAD()); + + static::assertEquals($expected_payload, $loaded_flattened_json->getPayload()); + static::assertEquals($expected_payload, $loaded_json->getPayload()); + } + + /** + * Same input as before, but we perform the encryption first. + * + * @test + */ + public function a128KWAndA128GCMEncryptionWithAdditionalAuthenticatedDataBis(): void + { + $expected_payload = "You can trust us to stick with you through thick and thin\xe2\x80\x93to the bitter end. And you can trust us to keep any secret of yours\xe2\x80\x93closer than you keep it yourself. But you cannot trust us to let you face trouble alone, and go off without a word. We are your friends, Frodo."; + + $private_key = new JWK([ + 'kty' => 'oct', + 'kid' => '81b20965-8332-43d9-a468-82160ad91ac8', + 'use' => 'enc', + 'alg' => 'A128KW', + 'k' => 'GZy6sIZ6wl9NJOKB-jnmVQ', + ]); + + $protectedHeader = [ + 'alg' => 'A128KW', + 'kid' => '81b20965-8332-43d9-a468-82160ad91ac8', + 'enc' => 'A128GCM', + ]; + + $jweBuilder = $this->getJWEBuilderFactory()->create(['A128KW'], ['A128GCM'], ['DEF']); + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['A128KW'], ['A128GCM'], ['DEF']); + + $jwe = $jweBuilder + ->create()->withPayload($expected_payload) + ->withSharedProtectedHeader($protectedHeader) + ->addRecipient($private_key) + ->build() + ; + + $loaded_flattened_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_json_flattened', $jwe, 0)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_flattened_json, $private_key, 0)); + + $loaded_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_json_general', $jwe)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $private_key, 0)); + + static::assertEquals($protectedHeader, $loaded_flattened_json->getSharedProtectedHeader()); + + static::assertEquals($protectedHeader, $loaded_json->getSharedProtectedHeader()); + + static::assertEquals($expected_payload, $loaded_flattened_json->getPayload()); + static::assertEquals($expected_payload, $loaded_json->getPayload()); + } +} diff --git a/vendor/web-token/jwt-encryption/Tests/RFC7520/A128KWAndA128GCMEncryptionWithCompressionTest.php b/vendor/web-token/jwt-encryption/Tests/RFC7520/A128KWAndA128GCMEncryptionWithCompressionTest.php new file mode 100644 index 000000000..48acf160a --- /dev/null +++ b/vendor/web-token/jwt-encryption/Tests/RFC7520/A128KWAndA128GCMEncryptionWithCompressionTest.php @@ -0,0 +1,149 @@ + 'oct', + 'kid' => '81b20965-8332-43d9-a468-82160ad91ac8', + 'use' => 'enc', + 'alg' => 'A128KW', + 'k' => 'GZy6sIZ6wl9NJOKB-jnmVQ', + ]); + + $protectedHeader = [ + 'alg' => 'A128KW', + 'kid' => '81b20965-8332-43d9-a468-82160ad91ac8', + 'enc' => 'A128GCM', + 'zip' => 'DEF', + ]; + + $expected_compact_json = 'eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIiwiemlwIjoiREVGIn0.5vUT2WOtQxKWcekM_IzVQwkGgzlFDwPi.p9pUq6XHY0jfEZIl.HbDtOsdai1oYziSx25KEeTxmwnh8L8jKMFNc1k3zmMI6VB8hry57tDZ61jXyezSPt0fdLVfe6Jf5y5-JaCap_JQBcb5opbmT60uWGml8blyiMQmOn9J--XhhlYg0m-BHaqfDO5iTOWxPxFMUedx7WCy8mxgDHj0aBMG6152PsM-w5E_o2B3jDbrYBKhpYA7qi3AyijnCJ7BP9rr3U8kxExCpG3mK420TjOw.VILuUwuIxaLVmh5X-T7kmA'; + $expected_flattened_json = '{"protected":"eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIiwiemlwIjoiREVGIn0","encrypted_key":"5vUT2WOtQxKWcekM_IzVQwkGgzlFDwPi","iv":"p9pUq6XHY0jfEZIl","ciphertext":"HbDtOsdai1oYziSx25KEeTxmwnh8L8jKMFNc1k3zmMI6VB8hry57tDZ61jXyezSPt0fdLVfe6Jf5y5-JaCap_JQBcb5opbmT60uWGml8blyiMQmOn9J--XhhlYg0m-BHaqfDO5iTOWxPxFMUedx7WCy8mxgDHj0aBMG6152PsM-w5E_o2B3jDbrYBKhpYA7qi3AyijnCJ7BP9rr3U8kxExCpG3mK420TjOw","tag":"VILuUwuIxaLVmh5X-T7kmA"}'; + $expected_json = '{"recipients":[{"encrypted_key":"5vUT2WOtQxKWcekM_IzVQwkGgzlFDwPi"}],"protected":"eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIiwiemlwIjoiREVGIn0","iv":"p9pUq6XHY0jfEZIl","ciphertext":"HbDtOsdai1oYziSx25KEeTxmwnh8L8jKMFNc1k3zmMI6VB8hry57tDZ61jXyezSPt0fdLVfe6Jf5y5-JaCap_JQBcb5opbmT60uWGml8blyiMQmOn9J--XhhlYg0m-BHaqfDO5iTOWxPxFMUedx7WCy8mxgDHj0aBMG6152PsM-w5E_o2B3jDbrYBKhpYA7qi3AyijnCJ7BP9rr3U8kxExCpG3mK420TjOw","tag":"VILuUwuIxaLVmh5X-T7kmA"}'; + $expected_iv = 'p9pUq6XHY0jfEZIl'; + $expected_encrypted_key = '5vUT2WOtQxKWcekM_IzVQwkGgzlFDwPi'; + $expected_ciphertext = 'HbDtOsdai1oYziSx25KEeTxmwnh8L8jKMFNc1k3zmMI6VB8hry57tDZ61jXyezSPt0fdLVfe6Jf5y5-JaCap_JQBcb5opbmT60uWGml8blyiMQmOn9J--XhhlYg0m-BHaqfDO5iTOWxPxFMUedx7WCy8mxgDHj0aBMG6152PsM-w5E_o2B3jDbrYBKhpYA7qi3AyijnCJ7BP9rr3U8kxExCpG3mK420TjOw'; + $expected_tag = 'VILuUwuIxaLVmh5X-T7kmA'; + + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['A128KW'], ['A128GCM'], ['DEF']); + + $loaded_compact_json = $this->getJWESerializerManager()->unserialize($expected_compact_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_compact_json, $private_key, 0)); + + $loaded_flattened_json = $this->getJWESerializerManager()->unserialize($expected_flattened_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_flattened_json, $private_key, 0)); + + $loaded_json = $this->getJWESerializerManager()->unserialize($expected_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $private_key, 0)); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_compact_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_compact_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_compact_json->getIV())); + static::assertEquals($expected_encrypted_key, Base64Url::encode($loaded_compact_json->getRecipient(0)->getEncryptedKey())); + static::assertEquals($expected_tag, Base64Url::encode($loaded_compact_json->getTag())); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_flattened_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_flattened_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_flattened_json->getIV())); + static::assertEquals($expected_encrypted_key, Base64Url::encode($loaded_flattened_json->getRecipient(0)->getEncryptedKey())); + static::assertEquals($expected_tag, Base64Url::encode($loaded_flattened_json->getTag())); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_json->getIV())); + static::assertEquals($expected_encrypted_key, Base64Url::encode($loaded_json->getRecipient(0)->getEncryptedKey())); + static::assertEquals($expected_tag, Base64Url::encode($loaded_json->getTag())); + + static::assertEquals($expected_payload, $loaded_compact_json->getPayload()); + static::assertEquals($expected_payload, $loaded_flattened_json->getPayload()); + static::assertEquals($expected_payload, $loaded_json->getPayload()); + } + + /** + * Same input as before, but we perform the encryption first. + * + * @test + */ + public function a128KWAndA128GCMEncryptionWithCompressionBis(): void + { + $expected_payload = "You can trust us to stick with you through thick and thin\xe2\x80\x93to the bitter end. And you can trust us to keep any secret of yours\xe2\x80\x93closer than you keep it yourself. But you cannot trust us to let you face trouble alone, and go off without a word. We are your friends, Frodo."; + + $private_key = new JWK([ + 'kty' => 'oct', + 'kid' => '81b20965-8332-43d9-a468-82160ad91ac8', + 'use' => 'enc', + 'alg' => 'A128KW', + 'k' => 'GZy6sIZ6wl9NJOKB-jnmVQ', + ]); + + $protectedHeader = [ + 'alg' => 'A128KW', + 'kid' => '81b20965-8332-43d9-a468-82160ad91ac8', + 'enc' => 'A128GCM', + 'zip' => 'DEF', + ]; + + $jweBuilder = $this->getJWEBuilderFactory()->create(['A128KW'], ['A128GCM'], ['DEF']); + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['A128KW'], ['A128GCM'], ['DEF']); + + $jwe = $jweBuilder + ->create()->withPayload($expected_payload) + ->withSharedProtectedHeader($protectedHeader) + ->addRecipient($private_key) + ->build() + ; + + $loaded_compact_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_compact', $jwe, 0)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_compact_json, $private_key, 0)); + + $loaded_flattened_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_json_flattened', $jwe, 0)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_flattened_json, $private_key, 0)); + + $loaded_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_json_general', $jwe)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $private_key, 0)); + + static::assertEquals($protectedHeader, $loaded_compact_json->getSharedProtectedHeader()); + + static::assertEquals($protectedHeader, $loaded_flattened_json->getSharedProtectedHeader()); + + static::assertEquals($protectedHeader, $loaded_json->getSharedProtectedHeader()); + + static::assertEquals($expected_payload, $loaded_compact_json->getPayload()); + static::assertEquals($expected_payload, $loaded_flattened_json->getPayload()); + static::assertEquals($expected_payload, $loaded_json->getPayload()); + } +} diff --git a/vendor/web-token/jwt-encryption/Tests/RFC7520/A128KWAndA128GCMEncryptionWithSpecificProtectedHeaderValuesTest.php b/vendor/web-token/jwt-encryption/Tests/RFC7520/A128KWAndA128GCMEncryptionWithSpecificProtectedHeaderValuesTest.php new file mode 100644 index 000000000..7ef999b3e --- /dev/null +++ b/vendor/web-token/jwt-encryption/Tests/RFC7520/A128KWAndA128GCMEncryptionWithSpecificProtectedHeaderValuesTest.php @@ -0,0 +1,141 @@ + 'oct', + 'kid' => '81b20965-8332-43d9-a468-82160ad91ac8', + 'use' => 'enc', + 'alg' => 'A128KW', + 'k' => 'GZy6sIZ6wl9NJOKB-jnmVQ', + ]); + + $protectedHeader = [ + 'enc' => 'A128GCM', + ]; + + $header = [ + 'alg' => 'A128KW', + 'kid' => '81b20965-8332-43d9-a468-82160ad91ac8', + ]; + + $expected_flattened_json = '{"protected":"eyJlbmMiOiJBMTI4R0NNIn0","unprotected":{"alg":"A128KW","kid":"81b20965-8332-43d9-a468-82160ad91ac8"},"encrypted_key":"jJIcM9J-hbx3wnqhf5FlkEYos0sHsF0H","iv":"WgEJsDS9bkoXQ3nR","ciphertext":"lIbCyRmRJxnB2yLQOTqjCDKV3H30ossOw3uD9DPsqLL2DM3swKkjOwQyZtWsFLYMj5YeLht_StAn21tHmQJuuNt64T8D4t6C7kC9OCCJ1IHAolUv4MyOt80MoPb8fZYbNKqplzYJgIL58g8N2v46OgyG637d6uuKPwhAnTGm_zWhqc_srOvgiLkzyFXPq1hBAURbc3-8BqeRb48iR1-_5g5UjWVD3lgiLCN_P7AW8mIiFvUNXBPJK3nOWL4teUPS8yHLbWeL83olU4UAgL48x-8dDkH23JykibVSQju-f7e-1xreHWXzWLHs1NqBbre0dEwK3HX_xM0LjUz77Krppgegoutpf5qaKg3l-_xMINmf","tag":"fNYLqpUe84KD45lvDiaBAQ"}'; + $expected_json = '{"recipients":[{"encrypted_key":"jJIcM9J-hbx3wnqhf5FlkEYos0sHsF0H"}],"unprotected":{"alg":"A128KW","kid":"81b20965-8332-43d9-a468-82160ad91ac8"},"protected":"eyJlbmMiOiJBMTI4R0NNIn0","iv":"WgEJsDS9bkoXQ3nR","ciphertext":"lIbCyRmRJxnB2yLQOTqjCDKV3H30ossOw3uD9DPsqLL2DM3swKkjOwQyZtWsFLYMj5YeLht_StAn21tHmQJuuNt64T8D4t6C7kC9OCCJ1IHAolUv4MyOt80MoPb8fZYbNKqplzYJgIL58g8N2v46OgyG637d6uuKPwhAnTGm_zWhqc_srOvgiLkzyFXPq1hBAURbc3-8BqeRb48iR1-_5g5UjWVD3lgiLCN_P7AW8mIiFvUNXBPJK3nOWL4teUPS8yHLbWeL83olU4UAgL48x-8dDkH23JykibVSQju-f7e-1xreHWXzWLHs1NqBbre0dEwK3HX_xM0LjUz77Krppgegoutpf5qaKg3l-_xMINmf","tag":"fNYLqpUe84KD45lvDiaBAQ"}'; + $expected_iv = 'WgEJsDS9bkoXQ3nR'; + $expected_encrypted_key = 'jJIcM9J-hbx3wnqhf5FlkEYos0sHsF0H'; + $expected_ciphertext = 'lIbCyRmRJxnB2yLQOTqjCDKV3H30ossOw3uD9DPsqLL2DM3swKkjOwQyZtWsFLYMj5YeLht_StAn21tHmQJuuNt64T8D4t6C7kC9OCCJ1IHAolUv4MyOt80MoPb8fZYbNKqplzYJgIL58g8N2v46OgyG637d6uuKPwhAnTGm_zWhqc_srOvgiLkzyFXPq1hBAURbc3-8BqeRb48iR1-_5g5UjWVD3lgiLCN_P7AW8mIiFvUNXBPJK3nOWL4teUPS8yHLbWeL83olU4UAgL48x-8dDkH23JykibVSQju-f7e-1xreHWXzWLHs1NqBbre0dEwK3HX_xM0LjUz77Krppgegoutpf5qaKg3l-_xMINmf'; + $expected_tag = 'fNYLqpUe84KD45lvDiaBAQ'; + + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['A128KW'], ['A128GCM'], ['DEF']); + + $loaded_flattened_json = $this->getJWESerializerManager()->unserialize($expected_flattened_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_flattened_json, $private_key, 0)); + + $loaded_json = $this->getJWESerializerManager()->unserialize($expected_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $private_key, 0)); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_flattened_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_flattened_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_flattened_json->getIV())); + static::assertEquals($expected_encrypted_key, Base64Url::encode($loaded_flattened_json->getRecipient(0)->getEncryptedKey())); + static::assertEquals($header, $loaded_flattened_json->getSharedHeader()); + static::assertEquals($expected_tag, Base64Url::encode($loaded_flattened_json->getTag())); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_json->getIV())); + static::assertEquals($expected_encrypted_key, Base64Url::encode($loaded_json->getRecipient(0)->getEncryptedKey())); + static::assertEquals($header, $loaded_json->getSharedHeader()); + static::assertEquals($expected_tag, Base64Url::encode($loaded_json->getTag())); + + static::assertEquals($expected_payload, $loaded_flattened_json->getPayload()); + static::assertEquals($expected_payload, $loaded_json->getPayload()); + } + + /** + * Same input as before, but we perform the encryption first. + * + * @test + */ + public function a128KWAndA128GCMEncryptionWithSpecificProtectedHeaderValuesBis(): void + { + $expected_payload = "You can trust us to stick with you through thick and thin\xe2\x80\x93to the bitter end. And you can trust us to keep any secret of yours\xe2\x80\x93closer than you keep it yourself. But you cannot trust us to let you face trouble alone, and go off without a word. We are your friends, Frodo."; + + $private_key = new JWK([ + 'kty' => 'oct', + 'kid' => '81b20965-8332-43d9-a468-82160ad91ac8', + 'use' => 'enc', + 'alg' => 'A128KW', + 'k' => 'GZy6sIZ6wl9NJOKB-jnmVQ', + ]); + + $protectedHeader = [ + 'enc' => 'A128GCM', + ]; + + $header = [ + 'alg' => 'A128KW', + 'kid' => '81b20965-8332-43d9-a468-82160ad91ac8', + ]; + + $jweBuilder = $this->getJWEBuilderFactory()->create(['A128KW'], ['A128GCM'], ['DEF']); + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['A128KW'], ['A128GCM'], ['DEF']); + + $jwe = $jweBuilder + ->create()->withPayload($expected_payload) + ->withSharedProtectedHeader($protectedHeader) + ->withSharedHeader($header) + ->addRecipient($private_key) + ->build() + ; + + $loaded_flattened_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_json_flattened', $jwe, 0)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_flattened_json, $private_key, 0)); + + $loaded_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_json_general', $jwe)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $private_key, 0)); + + static::assertEquals($protectedHeader, $loaded_flattened_json->getSharedProtectedHeader()); + static::assertEquals($header, $loaded_flattened_json->getSharedHeader()); + + static::assertEquals($protectedHeader, $loaded_json->getSharedProtectedHeader()); + static::assertEquals($header, $loaded_json->getSharedHeader()); + + static::assertEquals($expected_payload, $loaded_flattened_json->getPayload()); + static::assertEquals($expected_payload, $loaded_json->getPayload()); + } +} diff --git a/vendor/web-token/jwt-encryption/Tests/RFC7520/A256GCMKWAndA128CBC_HS256EncryptionTest.php b/vendor/web-token/jwt-encryption/Tests/RFC7520/A256GCMKWAndA128CBC_HS256EncryptionTest.php new file mode 100644 index 000000000..974741107 --- /dev/null +++ b/vendor/web-token/jwt-encryption/Tests/RFC7520/A256GCMKWAndA128CBC_HS256EncryptionTest.php @@ -0,0 +1,159 @@ + 'oct', + 'kid' => '18ec08e1-bfa9-4d95-b205-2b4dd1d4321d', + 'use' => 'enc', + 'alg' => 'A256GCMKW', + 'k' => 'qC57l_uxcm7Nm3K-ct4GFjx8tM1U8CZ0NLBvdQstiS8', + ]); + + $protectedHeader = [ + 'alg' => 'A256GCMKW', + 'kid' => '18ec08e1-bfa9-4d95-b205-2b4dd1d4321d', + 'tag' => 'kfPduVQ3T3H6vnewt--ksw', + 'iv' => 'KkYT0GX_2jHlfqN_', + 'enc' => 'A128CBC-HS256', + ]; + + $expected_compact_json = 'eyJhbGciOiJBMjU2R0NNS1ciLCJraWQiOiIxOGVjMDhlMS1iZmE5LTRkOTUtYjIwNS0yYjRkZDFkNDMyMWQiLCJ0YWciOiJrZlBkdVZRM1QzSDZ2bmV3dC0ta3N3IiwiaXYiOiJLa1lUMEdYXzJqSGxmcU5fIiwiZW5jIjoiQTEyOENCQy1IUzI1NiJ9.lJf3HbOApxMEBkCMOoTnnABxs_CvTWUmZQ2ElLvYNok.gz6NjyEFNm_vm8Gj6FwoFQ.Jf5p9-ZhJlJy_IQ_byKFmI0Ro7w7G1QiaZpI8OaiVgD8EqoDZHyFKFBupS8iaEeVIgMqWmsuJKuoVgzR3YfzoMd3GxEm3VxNhzWyWtZKX0gxKdy6HgLvqoGNbZCzLjqcpDiF8q2_62EVAbr2uSc2oaxFmFuIQHLcqAHxy51449xkjZ7ewzZaGV3eFqhpco8o4DijXaG5_7kp3h2cajRfDgymuxUbWgLqaeNQaJtvJmSMFuEOSAzw9Hdeb6yhdTynCRmu-kqtO5Dec4lT2OMZKpnxc_F1_4yDJFcqb5CiDSmA-psB2k0JtjxAj4UPI61oONK7zzFIu4gBfjJCndsZfdvG7h8wGjV98QhrKEnR7xKZ3KCr0_qR1B-gxpNk3xWU.DKW7jrb4WaRSNfbXVPlT5g'; + + /* + * There is an error in this vector + * In the RFC7520, the tag is 'DKW7jrb4WaRSNfbXVPlT5g' (see figure 147), but the tag from the flattened representation is 'NvBveHr_vonkvflfnUrmBQ' + * Same goes for the protected header. The values are good, but as the order is different, the protected header value is different and the tag is not validated. + */ + $expected_flattened_json = '{"protected":"eyJhbGciOiJBMjU2R0NNS1ciLCJraWQiOiIxOGVjMDhlMS1iZmE5LTRkOTUtYjIwNS0yYjRkZDFkNDMyMWQiLCJ0YWciOiJrZlBkdVZRM1QzSDZ2bmV3dC0ta3N3IiwiaXYiOiJLa1lUMEdYXzJqSGxmcU5fIiwiZW5jIjoiQTEyOENCQy1IUzI1NiJ9","encrypted_key":"lJf3HbOApxMEBkCMOoTnnABxs_CvTWUmZQ2ElLvYNok","iv":"gz6NjyEFNm_vm8Gj6FwoFQ","ciphertext":"Jf5p9-ZhJlJy_IQ_byKFmI0Ro7w7G1QiaZpI8OaiVgD8EqoDZHyFKFBupS8iaEeVIgMqWmsuJKuoVgzR3YfzoMd3GxEm3VxNhzWyWtZKX0gxKdy6HgLvqoGNbZCzLjqcpDiF8q2_62EVAbr2uSc2oaxFmFuIQHLcqAHxy51449xkjZ7ewzZaGV3eFqhpco8o4DijXaG5_7kp3h2cajRfDgymuxUbWgLqaeNQaJtvJmSMFuEOSAzw9Hdeb6yhdTynCRmu-kqtO5Dec4lT2OMZKpnxc_F1_4yDJFcqb5CiDSmA-psB2k0JtjxAj4UPI61oONK7zzFIu4gBfjJCndsZfdvG7h8wGjV98QhrKEnR7xKZ3KCr0_qR1B-gxpNk3xWU","tag":"DKW7jrb4WaRSNfbXVPlT5g"}'; + $expected_json = '{"recipients":[{"encrypted_key":"lJf3HbOApxMEBkCMOoTnnABxs_CvTWUmZQ2ElLvYNok"}],"protected":"eyJhbGciOiJBMjU2R0NNS1ciLCJraWQiOiIxOGVjMDhlMS1iZmE5LTRkOTUtYjIwNS0yYjRkZDFkNDMyMWQiLCJ0YWciOiJrZlBkdVZRM1QzSDZ2bmV3dC0ta3N3IiwiaXYiOiJLa1lUMEdYXzJqSGxmcU5fIiwiZW5jIjoiQTEyOENCQy1IUzI1NiJ9","iv":"gz6NjyEFNm_vm8Gj6FwoFQ","ciphertext":"Jf5p9-ZhJlJy_IQ_byKFmI0Ro7w7G1QiaZpI8OaiVgD8EqoDZHyFKFBupS8iaEeVIgMqWmsuJKuoVgzR3YfzoMd3GxEm3VxNhzWyWtZKX0gxKdy6HgLvqoGNbZCzLjqcpDiF8q2_62EVAbr2uSc2oaxFmFuIQHLcqAHxy51449xkjZ7ewzZaGV3eFqhpco8o4DijXaG5_7kp3h2cajRfDgymuxUbWgLqaeNQaJtvJmSMFuEOSAzw9Hdeb6yhdTynCRmu-kqtO5Dec4lT2OMZKpnxc_F1_4yDJFcqb5CiDSmA-psB2k0JtjxAj4UPI61oONK7zzFIu4gBfjJCndsZfdvG7h8wGjV98QhrKEnR7xKZ3KCr0_qR1B-gxpNk3xWU","tag":"DKW7jrb4WaRSNfbXVPlT5g"}'; + $expected_iv = 'gz6NjyEFNm_vm8Gj6FwoFQ'; + $expected_encrypted_key = 'lJf3HbOApxMEBkCMOoTnnABxs_CvTWUmZQ2ElLvYNok'; + $expected_ciphertext = 'Jf5p9-ZhJlJy_IQ_byKFmI0Ro7w7G1QiaZpI8OaiVgD8EqoDZHyFKFBupS8iaEeVIgMqWmsuJKuoVgzR3YfzoMd3GxEm3VxNhzWyWtZKX0gxKdy6HgLvqoGNbZCzLjqcpDiF8q2_62EVAbr2uSc2oaxFmFuIQHLcqAHxy51449xkjZ7ewzZaGV3eFqhpco8o4DijXaG5_7kp3h2cajRfDgymuxUbWgLqaeNQaJtvJmSMFuEOSAzw9Hdeb6yhdTynCRmu-kqtO5Dec4lT2OMZKpnxc_F1_4yDJFcqb5CiDSmA-psB2k0JtjxAj4UPI61oONK7zzFIu4gBfjJCndsZfdvG7h8wGjV98QhrKEnR7xKZ3KCr0_qR1B-gxpNk3xWU'; + $expected_tag = 'DKW7jrb4WaRSNfbXVPlT5g'; + + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['A256GCMKW'], ['A128CBC-HS256'], ['DEF']); + + $loaded_compact_json = $this->getJWESerializerManager()->unserialize($expected_compact_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_compact_json, $private_key, 0)); + + $loaded_flattened_json = $this->getJWESerializerManager()->unserialize($expected_flattened_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_flattened_json, $private_key, 0)); + + $loaded_json = $this->getJWESerializerManager()->unserialize($expected_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $private_key, 0)); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_compact_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_compact_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_compact_json->getIV())); + static::assertEquals($expected_encrypted_key, Base64Url::encode($loaded_compact_json->getRecipient(0)->getEncryptedKey())); + static::assertEquals($expected_tag, Base64Url::encode($loaded_compact_json->getTag())); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_flattened_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_flattened_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_flattened_json->getIV())); + static::assertEquals($expected_encrypted_key, Base64Url::encode($loaded_flattened_json->getRecipient(0)->getEncryptedKey())); + static::assertEquals($expected_tag, Base64Url::encode($loaded_flattened_json->getTag())); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_json->getIV())); + static::assertEquals($expected_encrypted_key, Base64Url::encode($loaded_json->getRecipient(0)->getEncryptedKey())); + static::assertEquals($expected_tag, Base64Url::encode($loaded_json->getTag())); + + static::assertEquals($expected_payload, $loaded_compact_json->getPayload()); + static::assertEquals($expected_payload, $loaded_flattened_json->getPayload()); + static::assertEquals($expected_payload, $loaded_json->getPayload()); + } + + /** + * Same input as before, but we perform the encryption first. + * + * @test + */ + public function a256GCMKWAndA128CBCHS256EncryptionBis(): void + { + $expected_payload = "You can trust us to stick with you through thick and thin\xe2\x80\x93to the bitter end. And you can trust us to keep any secret of yours\xe2\x80\x93closer than you keep it yourself. But you cannot trust us to let you face trouble alone, and go off without a word. We are your friends, Frodo."; + + $private_key = new JWK([ + 'kty' => 'oct', + 'kid' => '18ec08e1-bfa9-4d95-b205-2b4dd1d4321d', + 'use' => 'enc', + 'alg' => 'A256GCMKW', + 'k' => 'qC57l_uxcm7Nm3K-ct4GFjx8tM1U8CZ0NLBvdQstiS8', + ]); + + $protectedHeader = [ + 'alg' => 'A256GCMKW', + 'kid' => '18ec08e1-bfa9-4d95-b205-2b4dd1d4321d', + 'enc' => 'A128CBC-HS256', + ]; + + $jweBuilder = $this->getJWEBuilderFactory()->create(['A256GCMKW'], ['A128CBC-HS256'], ['DEF']); + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['A256GCMKW'], ['A128CBC-HS256'], ['DEF']); + + $jwe = $jweBuilder + ->create()->withPayload($expected_payload) + ->withSharedProtectedHeader($protectedHeader) + ->addRecipient($private_key) + ->build() + ; + + $loaded_compact_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_compact', $jwe, 0)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_compact_json, $private_key, 0)); + + $loaded_flattened_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_json_flattened', $jwe, 0)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_flattened_json, $private_key, 0)); + + $loaded_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_json_general', $jwe)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $private_key, 0)); + + static::assertTrue(array_key_exists('iv', $loaded_compact_json->getSharedProtectedHeader())); + static::assertTrue(array_key_exists('tag', $loaded_compact_json->getSharedProtectedHeader())); + + static::assertTrue(array_key_exists('iv', $loaded_flattened_json->getSharedProtectedHeader())); + static::assertTrue(array_key_exists('tag', $loaded_flattened_json->getSharedProtectedHeader())); + + static::assertTrue(array_key_exists('iv', $loaded_json->getSharedProtectedHeader())); + static::assertTrue(array_key_exists('tag', $loaded_json->getSharedProtectedHeader())); + + static::assertEquals($expected_payload, $loaded_compact_json->getPayload()); + static::assertEquals($expected_payload, $loaded_flattened_json->getPayload()); + static::assertEquals($expected_payload, $loaded_json->getPayload()); + } +} diff --git a/vendor/web-token/jwt-encryption/Tests/RFC7520/DirAndA128GCMEncryptionTest.php b/vendor/web-token/jwt-encryption/Tests/RFC7520/DirAndA128GCMEncryptionTest.php new file mode 100644 index 000000000..b3b26a44d --- /dev/null +++ b/vendor/web-token/jwt-encryption/Tests/RFC7520/DirAndA128GCMEncryptionTest.php @@ -0,0 +1,127 @@ + 'oct', + 'kid' => '77c7e2b8-6e13-45cf-8672-617b5b45243a', + 'use' => 'enc', + 'alg' => 'A128GCM', + 'k' => 'XctOhJAkA-pD9Lh7ZgW_2A', + ]); + + $protectedHeader = [ + 'alg' => 'dir', + 'kid' => '77c7e2b8-6e13-45cf-8672-617b5b45243a', + 'enc' => 'A128GCM', + ]; + + $expected_compact_json = 'eyJhbGciOiJkaXIiLCJraWQiOiI3N2M3ZTJiOC02ZTEzLTQ1Y2YtODY3Mi02MTdiNWI0NTI0M2EiLCJlbmMiOiJBMTI4R0NNIn0..refa467QzzKx6QAB.JW_i_f52hww_ELQPGaYyeAB6HYGcR559l9TYnSovc23XJoBcW29rHP8yZOZG7YhLpT1bjFuvZPjQS-m0IFtVcXkZXdH_lr_FrdYt9HRUYkshtrMmIUAyGmUnd9zMDB2n0cRDIHAzFVeJUDxkUwVAE7_YGRPdcqMyiBoCO-FBdE-Nceb4h3-FtBP-c_BIwCPTjb9o0SbdcdREEMJMyZBH8ySWMVi1gPD9yxi-aQpGbSv_F9N4IZAxscj5g-NJsUPbjk29-s7LJAGb15wEBtXphVCgyy53CoIKLHHeJHXex45Uz9aKZSRSInZI-wjsY0yu3cT4_aQ3i1o-tiE-F8Ios61EKgyIQ4CWao8PFMj8TTnp.vbb32Xvllea2OtmHAdccRQ'; + $expected_json = '{"protected":"eyJhbGciOiJkaXIiLCJraWQiOiI3N2M3ZTJiOC02ZTEzLTQ1Y2YtODY3Mi02MTdiNWI0NTI0M2EiLCJlbmMiOiJBMTI4R0NNIn0","iv":"refa467QzzKx6QAB","ciphertext":"JW_i_f52hww_ELQPGaYyeAB6HYGcR559l9TYnSovc23XJoBcW29rHP8yZOZG7YhLpT1bjFuvZPjQS-m0IFtVcXkZXdH_lr_FrdYt9HRUYkshtrMmIUAyGmUnd9zMDB2n0cRDIHAzFVeJUDxkUwVAE7_YGRPdcqMyiBoCO-FBdE-Nceb4h3-FtBP-c_BIwCPTjb9o0SbdcdREEMJMyZBH8ySWMVi1gPD9yxi-aQpGbSv_F9N4IZAxscj5g-NJsUPbjk29-s7LJAGb15wEBtXphVCgyy53CoIKLHHeJHXex45Uz9aKZSRSInZI-wjsY0yu3cT4_aQ3i1o-tiE-F8Ios61EKgyIQ4CWao8PFMj8TTnp","tag":"vbb32Xvllea2OtmHAdccRQ"}'; + $expected_iv = 'refa467QzzKx6QAB'; + $expected_ciphertext = 'JW_i_f52hww_ELQPGaYyeAB6HYGcR559l9TYnSovc23XJoBcW29rHP8yZOZG7YhLpT1bjFuvZPjQS-m0IFtVcXkZXdH_lr_FrdYt9HRUYkshtrMmIUAyGmUnd9zMDB2n0cRDIHAzFVeJUDxkUwVAE7_YGRPdcqMyiBoCO-FBdE-Nceb4h3-FtBP-c_BIwCPTjb9o0SbdcdREEMJMyZBH8ySWMVi1gPD9yxi-aQpGbSv_F9N4IZAxscj5g-NJsUPbjk29-s7LJAGb15wEBtXphVCgyy53CoIKLHHeJHXex45Uz9aKZSRSInZI-wjsY0yu3cT4_aQ3i1o-tiE-F8Ios61EKgyIQ4CWao8PFMj8TTnp'; + $expected_tag = 'vbb32Xvllea2OtmHAdccRQ'; + + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['dir'], ['A128GCM'], ['DEF']); + + $loaded_compact_json = $this->getJWESerializerManager()->unserialize($expected_compact_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_compact_json, $private_key, 0)); + + $loaded_json = $this->getJWESerializerManager()->unserialize($expected_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $private_key, 0)); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_compact_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_compact_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_compact_json->getIV())); + static::assertEquals($expected_tag, Base64Url::encode($loaded_compact_json->getTag())); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_json->getIV())); + static::assertEquals($expected_tag, Base64Url::encode($loaded_json->getTag())); + + static::assertEquals($expected_payload, $loaded_compact_json->getPayload()); + static::assertEquals($expected_payload, $loaded_json->getPayload()); + } + + /** + * Same input as before, but we perform the encryption first. + * + * @test + */ + public function dirAndA128GCMEncryptionBis(): void + { + $expected_payload = "You can trust us to stick with you through thick and thin\xe2\x80\x93to the bitter end. And you can trust us to keep any secret of yours\xe2\x80\x93closer than you keep it yourself. But you cannot trust us to let you face trouble alone, and go off without a word. We are your friends, Frodo."; + + $private_key = new JWK([ + 'kty' => 'oct', + 'kid' => '77c7e2b8-6e13-45cf-8672-617b5b45243a', + 'use' => 'enc', + 'alg' => 'A128GCM', + 'k' => 'XctOhJAkA-pD9Lh7ZgW_2A', + ]); + + $protectedHeader = [ + 'alg' => 'dir', + 'kid' => '77c7e2b8-6e13-45cf-8672-617b5b45243a', + 'enc' => 'A128GCM', + ]; + + $jweBuilder = $this->getJWEBuilderFactory()->create(['dir'], ['A128GCM'], ['DEF']); + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['dir'], ['A128GCM'], ['DEF']); + + $jwe = $jweBuilder + ->create()->withPayload($expected_payload) + ->withSharedProtectedHeader($protectedHeader) + ->addRecipient($private_key) + ->build() + ; + + $loaded_compact_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_compact', $jwe, 0)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_compact_json, $private_key, 0)); + + $loaded_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_json_general', $jwe)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $private_key, 0)); + + static::assertEquals($protectedHeader, $loaded_compact_json->getSharedProtectedHeader()); + + static::assertEquals($protectedHeader, $loaded_json->getSharedProtectedHeader()); + + static::assertEquals($expected_payload, $loaded_compact_json->getPayload()); + static::assertEquals($expected_payload, $loaded_json->getPayload()); + } +} diff --git a/vendor/web-token/jwt-encryption/Tests/RFC7520/ECDH_ES_A128KWAndA128GCMEncryptionTest.php b/vendor/web-token/jwt-encryption/Tests/RFC7520/ECDH_ES_A128KWAndA128GCMEncryptionTest.php new file mode 100644 index 000000000..c39ce8cec --- /dev/null +++ b/vendor/web-token/jwt-encryption/Tests/RFC7520/ECDH_ES_A128KWAndA128GCMEncryptionTest.php @@ -0,0 +1,161 @@ + 'EC', + 'kid' => 'peregrin.took@tuckborough.example', + 'use' => 'enc', + 'crv' => 'P-384', + 'x' => 'YU4rRUzdmVqmRtWOs2OpDE_T5fsNIodcG8G5FWPrTPMyxpzsSOGaQLpe2FpxBmu2', + 'y' => 'A8-yxCHxkfBz3hKZfI1jUYMjUhsEveZ9THuwFjH2sCNdtksRJU7D5-SkgaFL1ETP', + 'd' => 'iTx2pk7wW-GqJkHcEkFQb2EFyYcO7RugmaW3mRrQVAOUiPommT0IdnYK2xDlZh-j', + ]); + + $protectedHeader = [ + 'alg' => 'ECDH-ES+A128KW', + 'kid' => 'peregrin.took@tuckborough.example', + 'epk' => [ + 'kty' => 'EC', + 'crv' => 'P-384', + 'x' => 'uBo4kHPw6kbjx5l0xowrd_oYzBmaz-GKFZu4xAFFkbYiWgutEK6iuEDsQ6wNdNg3', + 'y' => 'sp3p5SGhZVC2faXumI-e9JU2Mo8KpoYrFDr5yPNVtW4PgEwZOyQTA-JdaY8tb7E0', + ], + 'enc' => 'A128GCM', + ]; + + $expected_compact_json = 'eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImtpZCI6InBlcmVncmluLnRvb2tAdHVja2Jvcm91Z2guZXhhbXBsZSIsImVwayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMzg0IiwieCI6InVCbzRrSFB3Nmtiang1bDB4b3dyZF9vWXpCbWF6LUdLRlp1NHhBRkZrYllpV2d1dEVLNml1RURzUTZ3TmROZzMiLCJ5Ijoic3AzcDVTR2haVkMyZmFYdW1JLWU5SlUyTW84S3BvWXJGRHI1eVBOVnRXNFBnRXdaT3lRVEEtSmRhWTh0YjdFMCJ9LCJlbmMiOiJBMTI4R0NNIn0.0DJjBXri_kBcC46IkU5_Jk9BqaQeHdv2.mH-G2zVqgztUtnW_.tkZuOO9h95OgHJmkkrfLBisku8rGf6nzVxhRM3sVOhXgz5NJ76oID7lpnAi_cPWJRCjSpAaUZ5dOR3Spy7QuEkmKx8-3RCMhSYMzsXaEwDdXta9Mn5B7cCBoJKB0IgEnj_qfo1hIi-uEkUpOZ8aLTZGHfpl05jMwbKkTe2yK3mjF6SBAsgicQDVCkcY9BLluzx1RmC3ORXaM0JaHPB93YcdSDGgpgBWMVrNU1ErkjcMqMoT_wtCex3w03XdLkjXIuEr2hWgeP-nkUZTPU9EoGSPj6fAS-bSz87RCPrxZdj_iVyC6QWcqAu07WNhjzJEPc4jVntRJ6K53NgPQ5p99l3Z408OUqj4ioYezbS6vTPlQ.WuGzxmcreYjpHGJoa17EBg'; + $expected_flattened_json = '{"protected":"eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImtpZCI6InBlcmVncmluLnRvb2tAdHVja2Jvcm91Z2guZXhhbXBsZSIsImVwayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMzg0IiwieCI6InVCbzRrSFB3Nmtiang1bDB4b3dyZF9vWXpCbWF6LUdLRlp1NHhBRkZrYllpV2d1dEVLNml1RURzUTZ3TmROZzMiLCJ5Ijoic3AzcDVTR2haVkMyZmFYdW1JLWU5SlUyTW84S3BvWXJGRHI1eVBOVnRXNFBnRXdaT3lRVEEtSmRhWTh0YjdFMCJ9LCJlbmMiOiJBMTI4R0NNIn0","encrypted_key":"0DJjBXri_kBcC46IkU5_Jk9BqaQeHdv2","iv":"mH-G2zVqgztUtnW_","ciphertext":"tkZuOO9h95OgHJmkkrfLBisku8rGf6nzVxhRM3sVOhXgz5NJ76oID7lpnAi_cPWJRCjSpAaUZ5dOR3Spy7QuEkmKx8-3RCMhSYMzsXaEwDdXta9Mn5B7cCBoJKB0IgEnj_qfo1hIi-uEkUpOZ8aLTZGHfpl05jMwbKkTe2yK3mjF6SBAsgicQDVCkcY9BLluzx1RmC3ORXaM0JaHPB93YcdSDGgpgBWMVrNU1ErkjcMqMoT_wtCex3w03XdLkjXIuEr2hWgeP-nkUZTPU9EoGSPj6fAS-bSz87RCPrxZdj_iVyC6QWcqAu07WNhjzJEPc4jVntRJ6K53NgPQ5p99l3Z408OUqj4ioYezbS6vTPlQ","tag":"WuGzxmcreYjpHGJoa17EBg"}'; + $expected_json = '{"recipients":[{"encrypted_key":"0DJjBXri_kBcC46IkU5_Jk9BqaQeHdv2"}],"protected":"eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImtpZCI6InBlcmVncmluLnRvb2tAdHVja2Jvcm91Z2guZXhhbXBsZSIsImVwayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMzg0IiwieCI6InVCbzRrSFB3Nmtiang1bDB4b3dyZF9vWXpCbWF6LUdLRlp1NHhBRkZrYllpV2d1dEVLNml1RURzUTZ3TmROZzMiLCJ5Ijoic3AzcDVTR2haVkMyZmFYdW1JLWU5SlUyTW84S3BvWXJGRHI1eVBOVnRXNFBnRXdaT3lRVEEtSmRhWTh0YjdFMCJ9LCJlbmMiOiJBMTI4R0NNIn0","iv":"mH-G2zVqgztUtnW_","ciphertext":"tkZuOO9h95OgHJmkkrfLBisku8rGf6nzVxhRM3sVOhXgz5NJ76oID7lpnAi_cPWJRCjSpAaUZ5dOR3Spy7QuEkmKx8-3RCMhSYMzsXaEwDdXta9Mn5B7cCBoJKB0IgEnj_qfo1hIi-uEkUpOZ8aLTZGHfpl05jMwbKkTe2yK3mjF6SBAsgicQDVCkcY9BLluzx1RmC3ORXaM0JaHPB93YcdSDGgpgBWMVrNU1ErkjcMqMoT_wtCex3w03XdLkjXIuEr2hWgeP-nkUZTPU9EoGSPj6fAS-bSz87RCPrxZdj_iVyC6QWcqAu07WNhjzJEPc4jVntRJ6K53NgPQ5p99l3Z408OUqj4ioYezbS6vTPlQ","tag":"WuGzxmcreYjpHGJoa17EBg"}'; + $expected_iv = 'mH-G2zVqgztUtnW_'; + $expected_encrypted_key = '0DJjBXri_kBcC46IkU5_Jk9BqaQeHdv2'; + $expected_ciphertext = 'tkZuOO9h95OgHJmkkrfLBisku8rGf6nzVxhRM3sVOhXgz5NJ76oID7lpnAi_cPWJRCjSpAaUZ5dOR3Spy7QuEkmKx8-3RCMhSYMzsXaEwDdXta9Mn5B7cCBoJKB0IgEnj_qfo1hIi-uEkUpOZ8aLTZGHfpl05jMwbKkTe2yK3mjF6SBAsgicQDVCkcY9BLluzx1RmC3ORXaM0JaHPB93YcdSDGgpgBWMVrNU1ErkjcMqMoT_wtCex3w03XdLkjXIuEr2hWgeP-nkUZTPU9EoGSPj6fAS-bSz87RCPrxZdj_iVyC6QWcqAu07WNhjzJEPc4jVntRJ6K53NgPQ5p99l3Z408OUqj4ioYezbS6vTPlQ'; + $expected_tag = 'WuGzxmcreYjpHGJoa17EBg'; + + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['ECDH-ES+A128KW'], ['A128GCM'], ['DEF']); + + $loaded_compact_json = $this->getJWESerializerManager()->unserialize($expected_compact_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_compact_json, $private_key, 0)); + + $loaded_flattened_json = $this->getJWESerializerManager()->unserialize($expected_flattened_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_flattened_json, $private_key, 0)); + + $loaded_json = $this->getJWESerializerManager()->unserialize($expected_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $private_key, 0)); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_compact_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_compact_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_compact_json->getIV())); + static::assertEquals($expected_encrypted_key, Base64Url::encode($loaded_compact_json->getRecipient(0)->getEncryptedKey())); + static::assertEquals($expected_tag, Base64Url::encode($loaded_compact_json->getTag())); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_flattened_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_flattened_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_flattened_json->getIV())); + static::assertEquals($expected_encrypted_key, Base64Url::encode($loaded_flattened_json->getRecipient(0)->getEncryptedKey())); + static::assertEquals($expected_tag, Base64Url::encode($loaded_flattened_json->getTag())); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_json->getIV())); + static::assertEquals($expected_encrypted_key, Base64Url::encode($loaded_json->getRecipient(0)->getEncryptedKey())); + static::assertEquals($expected_tag, Base64Url::encode($loaded_json->getTag())); + + static::assertEquals($expected_payload, $loaded_compact_json->getPayload()); + static::assertEquals($expected_payload, $loaded_flattened_json->getPayload()); + static::assertEquals($expected_payload, $loaded_json->getPayload()); + } + + /** + * Same input as before, but we perform the encryption first. + * + * @test + */ + public function eCDHESA128KWAndA128GCMEncryptionBis(): void + { + $expected_payload = "You can trust us to stick with you through thick and thin\xe2\x80\x93to the bitter end. And you can trust us to keep any secret of yours\xe2\x80\x93closer than you keep it yourself. But you cannot trust us to let you face trouble alone, and go off without a word. We are your friends, Frodo."; + + $public_key = new JWK([ + 'kty' => 'EC', + 'kid' => 'peregrin.took@tuckborough.example', + 'use' => 'enc', + 'crv' => 'P-384', + 'x' => 'YU4rRUzdmVqmRtWOs2OpDE_T5fsNIodcG8G5FWPrTPMyxpzsSOGaQLpe2FpxBmu2', + 'y' => 'A8-yxCHxkfBz3hKZfI1jUYMjUhsEveZ9THuwFjH2sCNdtksRJU7D5-SkgaFL1ETP', + ]); + + $private_key = new JWK([ + 'kty' => 'EC', + 'kid' => 'peregrin.took@tuckborough.example', + 'use' => 'enc', + 'crv' => 'P-384', + 'x' => 'YU4rRUzdmVqmRtWOs2OpDE_T5fsNIodcG8G5FWPrTPMyxpzsSOGaQLpe2FpxBmu2', + 'y' => 'A8-yxCHxkfBz3hKZfI1jUYMjUhsEveZ9THuwFjH2sCNdtksRJU7D5-SkgaFL1ETP', + 'd' => 'iTx2pk7wW-GqJkHcEkFQb2EFyYcO7RugmaW3mRrQVAOUiPommT0IdnYK2xDlZh-j', + ]); + + $protectedHeader = [ + 'alg' => 'ECDH-ES+A128KW', + 'kid' => 'peregrin.took@tuckborough.example', + 'enc' => 'A128GCM', + ]; + + $jweBuilder = $this->getJWEBuilderFactory()->create(['ECDH-ES+A128KW'], ['A128GCM'], ['DEF']); + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['ECDH-ES+A128KW'], ['A128GCM'], ['DEF']); + + $jwe = $jweBuilder + ->create()->withPayload($expected_payload) + ->withSharedProtectedHeader($protectedHeader) + ->addRecipient($public_key) + ->build() + ; + + $loaded_flattened_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_json_flattened', $jwe, 0)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_flattened_json, $private_key, 0)); + + $loaded_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_json_general', $jwe)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $private_key, 0)); + + static::assertTrue(array_key_exists('epk', $loaded_flattened_json->getSharedProtectedHeader())); + + static::assertTrue(array_key_exists('epk', $loaded_json->getSharedProtectedHeader())); + + static::assertEquals($expected_payload, $loaded_flattened_json->getPayload()); + static::assertEquals($expected_payload, $loaded_json->getPayload()); + } +} diff --git a/vendor/web-token/jwt-encryption/Tests/RFC7520/ECDH_ES_AndA128CBC_HS256EncryptionTest.php b/vendor/web-token/jwt-encryption/Tests/RFC7520/ECDH_ES_AndA128CBC_HS256EncryptionTest.php new file mode 100644 index 000000000..9d2eeecee --- /dev/null +++ b/vendor/web-token/jwt-encryption/Tests/RFC7520/ECDH_ES_AndA128CBC_HS256EncryptionTest.php @@ -0,0 +1,142 @@ + 'EC', + 'kid' => 'meriadoc.brandybuck@buckland.example', + 'use' => 'enc', + 'crv' => 'P-256', + 'x' => 'Ze2loSV3wrroKUN_4zhwGhCqo3Xhu1td4QjeQ5wIVR0', + 'y' => 'HlLtdXARY_f55A3fnzQbPcm6hgr34Mp8p-nuzQCE0Zw', + 'd' => 'r_kHyZ-a06rmxM3yESK84r1otSg-aQcVStkRhA-iCM8', + ]); + + $protectedHeader = [ + 'alg' => 'ECDH-ES', + 'kid' => 'meriadoc.brandybuck@buckland.example', + 'epk' => [ + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => 'mPUKT_bAWGHIhg0TpjjqVsP1rXWQu_vwVOHHtNkdYoA', + 'y' => '8BQAsImGeAS46fyWw5MhYfGTT0IjBpFw2SS34Dv4Irs', + ], + 'enc' => 'A128CBC-HS256', + ]; + + $expected_compact_json = 'eyJhbGciOiJFQ0RILUVTIiwia2lkIjoibWVyaWFkb2MuYnJhbmR5YnVja0BidWNrbGFuZC5leGFtcGxlIiwiZXBrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoibVBVS1RfYkFXR0hJaGcwVHBqanFWc1AxclhXUXVfdndWT0hIdE5rZFlvQSIsInkiOiI4QlFBc0ltR2VBUzQ2ZnlXdzVNaFlmR1RUMElqQnBGdzJTUzM0RHY0SXJzIn0sImVuYyI6IkExMjhDQkMtSFMyNTYifQ..yc9N8v5sYyv3iGQT926IUg.BoDlwPnTypYq-ivjmQvAYJLb5Q6l-F3LIgQomlz87yW4OPKbWE1zSTEFjDfhU9IPIOSA9Bml4m7iDFwA-1ZXvHteLDtw4R1XRGMEsDIqAYtskTTmzmzNa-_q4F_evAPUmwlO-ZG45Mnq4uhM1fm_D9rBtWolqZSF3xGNNkpOMQKF1Cl8i8wjzRli7-IXgyirlKQsbhhqRzkv8IcY6aHl24j03C-AR2le1r7URUhArM79BY8soZU0lzwI-sD5PZ3l4NDCCei9XkoIAfsXJWmySPoeRb2Ni5UZL4mYpvKDiwmyzGd65KqVw7MsFfI_K767G9C9Azp73gKZD0DyUn1mn0WW5LmyX_yJ-3AROq8p1WZBfG-ZyJ6195_JGG2m9Csg.WCCkNa-x4BeB9hIDIfFuhg'; + $expected_json = '{"protected":"eyJhbGciOiJFQ0RILUVTIiwia2lkIjoibWVyaWFkb2MuYnJhbmR5YnVja0BidWNrbGFuZC5leGFtcGxlIiwiZXBrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoibVBVS1RfYkFXR0hJaGcwVHBqanFWc1AxclhXUXVfdndWT0hIdE5rZFlvQSIsInkiOiI4QlFBc0ltR2VBUzQ2ZnlXdzVNaFlmR1RUMElqQnBGdzJTUzM0RHY0SXJzIn0sImVuYyI6IkExMjhDQkMtSFMyNTYifQ","iv":"yc9N8v5sYyv3iGQT926IUg","ciphertext":"BoDlwPnTypYq-ivjmQvAYJLb5Q6l-F3LIgQomlz87yW4OPKbWE1zSTEFjDfhU9IPIOSA9Bml4m7iDFwA-1ZXvHteLDtw4R1XRGMEsDIqAYtskTTmzmzNa-_q4F_evAPUmwlO-ZG45Mnq4uhM1fm_D9rBtWolqZSF3xGNNkpOMQKF1Cl8i8wjzRli7-IXgyirlKQsbhhqRzkv8IcY6aHl24j03C-AR2le1r7URUhArM79BY8soZU0lzwI-sD5PZ3l4NDCCei9XkoIAfsXJWmySPoeRb2Ni5UZL4mYpvKDiwmyzGd65KqVw7MsFfI_K767G9C9Azp73gKZD0DyUn1mn0WW5LmyX_yJ-3AROq8p1WZBfG-ZyJ6195_JGG2m9Csg","tag":"WCCkNa-x4BeB9hIDIfFuhg"}'; + $expected_iv = 'yc9N8v5sYyv3iGQT926IUg'; + $expected_ciphertext = 'BoDlwPnTypYq-ivjmQvAYJLb5Q6l-F3LIgQomlz87yW4OPKbWE1zSTEFjDfhU9IPIOSA9Bml4m7iDFwA-1ZXvHteLDtw4R1XRGMEsDIqAYtskTTmzmzNa-_q4F_evAPUmwlO-ZG45Mnq4uhM1fm_D9rBtWolqZSF3xGNNkpOMQKF1Cl8i8wjzRli7-IXgyirlKQsbhhqRzkv8IcY6aHl24j03C-AR2le1r7URUhArM79BY8soZU0lzwI-sD5PZ3l4NDCCei9XkoIAfsXJWmySPoeRb2Ni5UZL4mYpvKDiwmyzGd65KqVw7MsFfI_K767G9C9Azp73gKZD0DyUn1mn0WW5LmyX_yJ-3AROq8p1WZBfG-ZyJ6195_JGG2m9Csg'; + $expected_tag = 'WCCkNa-x4BeB9hIDIfFuhg'; + + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['ECDH-ES'], ['A128CBC-HS256'], ['DEF']); + + $loaded_compact_json = $this->getJWESerializerManager()->unserialize($expected_compact_json); + + $loaded_json = $this->getJWESerializerManager()->unserialize($expected_json); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_compact_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_compact_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_compact_json->getIV())); + static::assertEquals($expected_tag, Base64Url::encode($loaded_compact_json->getTag())); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_json->getIV())); + static::assertEquals($expected_tag, Base64Url::encode($loaded_json->getTag())); + + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_compact_json, $private_key, 0)); + static::assertEquals($expected_payload, $loaded_compact_json->getPayload()); + + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $private_key, 0)); + static::assertEquals($expected_payload, $loaded_json->getPayload()); + } + + /** + * Same input as before, but we perform the encryption first. + * + * @test + */ + public function eCDHESAndA128CBCHS256EncryptionBis(): void + { + $expected_payload = "You can trust us to stick with you through thick and thin\xe2\x80\x93to the bitter end. And you can trust us to keep any secret of yours\xe2\x80\x93closer than you keep it yourself. But you cannot trust us to let you face trouble alone, and go off without a word. We are your friends, Frodo."; + + $public_key = new JWK([ + 'kty' => 'EC', + 'kid' => 'meriadoc.brandybuck@buckland.example', + 'use' => 'enc', + 'crv' => 'P-256', + 'x' => 'Ze2loSV3wrroKUN_4zhwGhCqo3Xhu1td4QjeQ5wIVR0', + 'y' => 'HlLtdXARY_f55A3fnzQbPcm6hgr34Mp8p-nuzQCE0Zw', + ]); + + $private_key = new JWK([ + 'kty' => 'EC', + 'kid' => 'meriadoc.brandybuck@buckland.example', + 'use' => 'enc', + 'crv' => 'P-256', + 'x' => 'Ze2loSV3wrroKUN_4zhwGhCqo3Xhu1td4QjeQ5wIVR0', + 'y' => 'HlLtdXARY_f55A3fnzQbPcm6hgr34Mp8p-nuzQCE0Zw', + 'd' => 'r_kHyZ-a06rmxM3yESK84r1otSg-aQcVStkRhA-iCM8', + ]); + + $protectedHeader = [ + 'alg' => 'ECDH-ES', + 'kid' => 'meriadoc.brandybuck@buckland.example', + 'enc' => 'A128CBC-HS256', + ]; + + $jweBuilder = $this->getJWEBuilderFactory()->create(['ECDH-ES'], ['A128CBC-HS256'], ['DEF']); + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['ECDH-ES'], ['A128CBC-HS256'], ['DEF']); + + $jwe = $jweBuilder + ->create()->withPayload($expected_payload) + ->withSharedProtectedHeader($protectedHeader) + ->addRecipient($public_key) + ->build() + ; + + $loaded_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_json_general', $jwe)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $private_key, 0)); + + static::assertTrue(array_key_exists('epk', $loaded_json->getSharedProtectedHeader())); + + static::assertEquals($expected_payload, $loaded_json->getPayload()); + } +} diff --git a/vendor/web-token/jwt-encryption/Tests/RFC7520/MultipleRecipientEncryptionTest.php b/vendor/web-token/jwt-encryption/Tests/RFC7520/MultipleRecipientEncryptionTest.php new file mode 100644 index 000000000..dc661a424 --- /dev/null +++ b/vendor/web-token/jwt-encryption/Tests/RFC7520/MultipleRecipientEncryptionTest.php @@ -0,0 +1,304 @@ + 'RSA', + 'kid' => 'frodo.baggins@hobbiton.example', + 'use' => 'enc', + 'n' => 'maxhbsmBtdQ3CNrKvprUE6n9lYcregDMLYNeTAWcLj8NnPU9XIYegTHVHQjxKDSHP2l-F5jS7sppG1wgdAqZyhnWvXhYNvcM7RfgKxqNx_xAHx6f3yy7s-M9PSNCwPC2lh6UAkR4I00EhV9lrypM9Pi4lBUop9t5fS9W5UNwaAllhrd-osQGPjIeI1deHTwx-ZTHu3C60Pu_LJIl6hKn9wbwaUmA4cR5Bd2pgbaY7ASgsjCUbtYJaNIHSoHXprUdJZKUMAzV0WOKPfA6OPI4oypBadjvMZ4ZAj3BnXaSYsEZhaueTXvZB4eZOAjIyh2e_VOIKVMsnDrJYAVotGlvMQ', + 'e' => 'AQAB', + 'd' => 'Kn9tgoHfiTVi8uPu5b9TnwyHwG5dK6RE0uFdlpCGnJN7ZEi963R7wybQ1PLAHmpIbNTztfrheoAniRV1NCIqXaW_qS461xiDTp4ntEPnqcKsyO5jMAji7-CL8vhpYYowNFvIesgMoVaPRYMYT9TW63hNM0aWs7USZ_hLg6Oe1mY0vHTI3FucjSM86Nff4oIENt43r2fspgEPGRrdE6fpLc9Oaq-qeP1GFULimrRdndm-P8q8kvN3KHlNAtEgrQAgTTgz80S-3VD0FgWfgnb1PNmiuPUxO8OpI9KDIfu_acc6fg14nsNaJqXe6RESvhGPH2afjHqSy_Fd2vpzj85bQQ', + 'p' => '2DwQmZ43FoTnQ8IkUj3BmKRf5Eh2mizZA5xEJ2MinUE3sdTYKSLtaEoekX9vbBZuWxHdVhM6UnKCJ_2iNk8Z0ayLYHL0_G21aXf9-unynEpUsH7HHTklLpYAzOOx1ZgVljoxAdWNn3hiEFrjZLZGS7lOH-a3QQlDDQoJOJ2VFmU', + 'q' => 'te8LY4-W7IyaqH1ExujjMqkTAlTeRbv0VLQnfLY2xINnrWdwiQ93_VF099aP1ESeLja2nw-6iKIe-qT7mtCPozKfVtUYfz5HrJ_XY2kfexJINb9lhZHMv5p1skZpeIS-GPHCC6gRlKo1q-idn_qxyusfWv7WAxlSVfQfk8d6Et0', + 'dp' => 'UfYKcL_or492vVc0PzwLSplbg4L3-Z5wL48mwiswbpzOyIgd2xHTHQmjJpFAIZ8q-zf9RmgJXkDrFs9rkdxPtAsL1WYdeCT5c125Fkdg317JVRDo1inX7x2Kdh8ERCreW8_4zXItuTl_KiXZNU5lvMQjWbIw2eTx1lpsflo0rYU', + 'dq' => 'iEgcO-QfpepdH8FWd7mUFyrXdnOkXJBCogChY6YKuIHGc_p8Le9MbpFKESzEaLlN1Ehf3B6oGBl5Iz_ayUlZj2IoQZ82znoUrpa9fVYNot87ACfzIG7q9Mv7RiPAderZi03tkVXAdaBau_9vs5rS-7HMtxkVrxSUvJY14TkXlHE', + 'qi' => 'kC-lzZOqoFaZCr5l0tOVtREKoVqaAYhQiqIRGL-MzS4sCmRkxm5vZlXYx6RtE1n_AagjqajlkjieGlxTTThHD8Iga6foGBMaAr5uR1hGQpSc7Gl7CF1DZkBJMTQN6EshYzZfxW08mIO8M6Rzuh0beL6fG9mkDcIyPrBXx2bQ_mM', + ]); + + $recipient_2_private_key = new JWK([ + 'kty' => 'EC', + 'kid' => 'peregrin.took@tuckborough.example', + 'use' => 'enc', + 'crv' => 'P-384', + 'x' => 'YU4rRUzdmVqmRtWOs2OpDE_T5fsNIodcG8G5FWPrTPMyxpzsSOGaQLpe2FpxBmu2', + 'y' => 'A8-yxCHxkfBz3hKZfI1jUYMjUhsEveZ9THuwFjH2sCNdtksRJU7D5-SkgaFL1ETP', + 'd' => 'iTx2pk7wW-GqJkHcEkFQb2EFyYcO7RugmaW3mRrQVAOUiPommT0IdnYK2xDlZh-j', + ]); + + $recipient_3_private_key = new JWK([ + 'kty' => 'oct', + 'kid' => '18ec08e1-bfa9-4d95-b205-2b4dd1d4321d', + 'use' => 'enc', + 'alg' => 'A256GCMKW', + 'k' => 'qC57l_uxcm7Nm3K-ct4GFjx8tM1U8CZ0NLBvdQstiS8', + ]); + + $protectedHeader = [ + 'enc' => 'A128CBC-HS256', + ]; + + $header = [ + 'cty' => 'text/plain', + ]; + + $recipient_1Header = [ + 'alg' => 'RSA1_5', + 'kid' => 'frodo.baggins@hobbiton.example', + ]; + + $recipient_2Header = [ + 'alg' => 'ECDH-ES+A256KW', + 'kid' => 'peregrin.took@tuckborough.example', + 'epk' => [ + 'kty' => 'EC', + 'crv' => 'P-384', + 'x' => 'Uzdvk3pi5wKCRc1izp5_r0OjeqT-I68i8g2b8mva8diRhsE2xAn2DtMRb25Ma2CX', + 'y' => 'VDrRyFJh-Kwd1EjAgmj5Eo-CTHAZ53MC7PjjpLioy3ylEjI1pOMbw91fzZ84pbfm', + ], ]; + + $recipient_3Header = [ + 'alg' => 'A256GCMKW', + 'kid' => '18ec08e1-bfa9-4d95-b205-2b4dd1d4321d', + 'tag' => '59Nqh1LlYtVIhfD3pgRGvw', + 'iv' => 'AvpeoPZ9Ncn9mkBn', + ]; + + $expected_json = '{"recipients":[{"encrypted_key":"dYOD28kab0Vvf4ODgxVAJXgHcSZICSOp8M51zjwj4w6Y5G4XJQsNNIBiqyvUUAOcpL7S7-cFe7Pio7gV_Q06WmCSa-vhW6me4bWrBf7cHwEQJdXihidAYWVajJIaKMXMvFRMV6iDlRr076DFthg2_AV0_tSiV6xSEIFqt1xnYPpmP91tc5WJDOGb-wqjw0-b-S1laS11QVbuP78dQ7Fa0zAVzzjHX-xvyM2wxj_otxr9clN1LnZMbeYSrRicJK5xodvWgkpIdkMHo4LvdhRRvzoKzlic89jFWPlnBq_V4n5trGuExtp_-dbHcGlihqc_wGgho9fLMK8JOArYLcMDNQ","header":{"alg":"RSA1_5","kid":"frodo.baggins@hobbiton.example"}},{"encrypted_key":"ExInT0io9BqBMYF6-maw5tZlgoZXThD1zWKsHixJuw_elY4gSSId_w","header":{"alg":"ECDH-ES+A256KW","kid":"peregrin.took@tuckborough.example","epk":{"kty":"EC","crv":"P-384","x":"Uzdvk3pi5wKCRc1izp5_r0OjeqT-I68i8g2b8mva8diRhsE2xAn2DtMRb25Ma2CX","y":"VDrRyFJh-Kwd1EjAgmj5Eo-CTHAZ53MC7PjjpLioy3ylEjI1pOMbw91fzZ84pbfm"}}},{"encrypted_key":"a7CclAejo_7JSuPB8zeagxXRam8dwCfmkt9-WyTpS1E","header":{"alg":"A256GCMKW","kid":"18ec08e1-bfa9-4d95-b205-2b4dd1d4321d","tag":"59Nqh1LlYtVIhfD3pgRGvw","iv":"AvpeoPZ9Ncn9mkBn"}}],"unprotected":{"cty":"text/plain"},"protected":"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0","iv":"VgEIHY20EnzUtZFl2RpB1g","ciphertext":"ajm2Q-OpPXCr7-MHXicknb1lsxLdXxK_yLds0KuhJzfWK04SjdxQeSw2L9mu3a_k1C55kCQ_3xlkcVKC5yr__Is48VOoK0k63_QRM9tBURMFqLByJ8vOYQX0oJW4VUHJLmGhF-tVQWB7Kz8mr8zeE7txF0MSaP6ga7-siYxStR7_G07Thd1jh-zGT0wxM5g-VRORtq0K6AXpLlwEqRp7pkt2zRM0ZAXqSpe1O6FJ7FHLDyEFnD-zDIZukLpCbzhzMDLLw2-8I14FQrgi-iEuzHgIJFIJn2wh9Tj0cg_kOZy9BqMRZbmYXMY9YQjorZ_P_JYG3ARAIF3OjDNqpdYe-K_5Q5crGJSDNyij_ygEiItR5jssQVH2ofDQdLChtazE","tag":"BESYyFN7T09KY7i8zKs5_g"}'; + $expected_iv = 'VgEIHY20EnzUtZFl2RpB1g'; + $expected_recipient_1_encrypted_key = 'dYOD28kab0Vvf4ODgxVAJXgHcSZICSOp8M51zjwj4w6Y5G4XJQsNNIBiqyvUUAOcpL7S7-cFe7Pio7gV_Q06WmCSa-vhW6me4bWrBf7cHwEQJdXihidAYWVajJIaKMXMvFRMV6iDlRr076DFthg2_AV0_tSiV6xSEIFqt1xnYPpmP91tc5WJDOGb-wqjw0-b-S1laS11QVbuP78dQ7Fa0zAVzzjHX-xvyM2wxj_otxr9clN1LnZMbeYSrRicJK5xodvWgkpIdkMHo4LvdhRRvzoKzlic89jFWPlnBq_V4n5trGuExtp_-dbHcGlihqc_wGgho9fLMK8JOArYLcMDNQ'; + $expected_recipient_2_encrypted_key = 'ExInT0io9BqBMYF6-maw5tZlgoZXThD1zWKsHixJuw_elY4gSSId_w'; + $expected_recipient_3_encrypted_key = 'a7CclAejo_7JSuPB8zeagxXRam8dwCfmkt9-WyTpS1E'; + $expected_ciphertext = 'ajm2Q-OpPXCr7-MHXicknb1lsxLdXxK_yLds0KuhJzfWK04SjdxQeSw2L9mu3a_k1C55kCQ_3xlkcVKC5yr__Is48VOoK0k63_QRM9tBURMFqLByJ8vOYQX0oJW4VUHJLmGhF-tVQWB7Kz8mr8zeE7txF0MSaP6ga7-siYxStR7_G07Thd1jh-zGT0wxM5g-VRORtq0K6AXpLlwEqRp7pkt2zRM0ZAXqSpe1O6FJ7FHLDyEFnD-zDIZukLpCbzhzMDLLw2-8I14FQrgi-iEuzHgIJFIJn2wh9Tj0cg_kOZy9BqMRZbmYXMY9YQjorZ_P_JYG3ARAIF3OjDNqpdYe-K_5Q5crGJSDNyij_ygEiItR5jssQVH2ofDQdLChtazE'; + $expected_tag = 'BESYyFN7T09KY7i8zKs5_g'; + + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['RSA1_5', 'ECDH-ES+A256KW', 'A256GCMKW'], ['A128CBC-HS256'], ['DEF']); + + $loaded_json = $this->getJWESerializerManager()->unserialize($expected_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $recipient_1_private_key, 0)); + + $loaded_json = $this->getJWESerializerManager()->unserialize($expected_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $recipient_2_private_key, 1)); + + $loaded_json = $this->getJWESerializerManager()->unserialize($expected_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $recipient_3_private_key, 2)); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_json->getIV())); + static::assertEquals($expected_recipient_1_encrypted_key, Base64Url::encode($loaded_json->getRecipient(0)->getEncryptedKey())); + static::assertEquals($expected_recipient_2_encrypted_key, Base64Url::encode($loaded_json->getRecipient(1)->getEncryptedKey())); + static::assertEquals($expected_recipient_3_encrypted_key, Base64Url::encode($loaded_json->getRecipient(2)->getEncryptedKey())); + static::assertEquals($recipient_1Header, $loaded_json->getRecipient(0)->getHeader()); + static::assertEquals($recipient_2Header, $loaded_json->getRecipient(1)->getHeader()); + static::assertEquals($recipient_3Header, $loaded_json->getRecipient(2)->getHeader()); + static::assertEquals($header, $loaded_json->getSharedHeader()); + static::assertEquals($expected_tag, Base64Url::encode($loaded_json->getTag())); + + static::assertEquals($expected_payload, $loaded_json->getPayload()); + } + + /** + * Same input as before, but we perform the encryption first. + * + * @test + */ + public function multipleRecipientEncryptionBis(): void + { + $expected_payload = "You can trust us to stick with you through thick and thin\xe2\x80\x93to the bitter end. And you can trust us to keep any secret of yours\xe2\x80\x93closer than you keep it yourself. But you cannot trust us to let you face trouble alone, and go off without a word. We are your friends, Frodo."; + + $recipient_1_private_key = new JWK([ + 'kty' => 'RSA', + 'kid' => 'frodo.baggins@hobbiton.example', + 'use' => 'enc', + 'n' => 'maxhbsmBtdQ3CNrKvprUE6n9lYcregDMLYNeTAWcLj8NnPU9XIYegTHVHQjxKDSHP2l-F5jS7sppG1wgdAqZyhnWvXhYNvcM7RfgKxqNx_xAHx6f3yy7s-M9PSNCwPC2lh6UAkR4I00EhV9lrypM9Pi4lBUop9t5fS9W5UNwaAllhrd-osQGPjIeI1deHTwx-ZTHu3C60Pu_LJIl6hKn9wbwaUmA4cR5Bd2pgbaY7ASgsjCUbtYJaNIHSoHXprUdJZKUMAzV0WOKPfA6OPI4oypBadjvMZ4ZAj3BnXaSYsEZhaueTXvZB4eZOAjIyh2e_VOIKVMsnDrJYAVotGlvMQ', + 'e' => 'AQAB', + 'd' => 'Kn9tgoHfiTVi8uPu5b9TnwyHwG5dK6RE0uFdlpCGnJN7ZEi963R7wybQ1PLAHmpIbNTztfrheoAniRV1NCIqXaW_qS461xiDTp4ntEPnqcKsyO5jMAji7-CL8vhpYYowNFvIesgMoVaPRYMYT9TW63hNM0aWs7USZ_hLg6Oe1mY0vHTI3FucjSM86Nff4oIENt43r2fspgEPGRrdE6fpLc9Oaq-qeP1GFULimrRdndm-P8q8kvN3KHlNAtEgrQAgTTgz80S-3VD0FgWfgnb1PNmiuPUxO8OpI9KDIfu_acc6fg14nsNaJqXe6RESvhGPH2afjHqSy_Fd2vpzj85bQQ', + 'p' => '2DwQmZ43FoTnQ8IkUj3BmKRf5Eh2mizZA5xEJ2MinUE3sdTYKSLtaEoekX9vbBZuWxHdVhM6UnKCJ_2iNk8Z0ayLYHL0_G21aXf9-unynEpUsH7HHTklLpYAzOOx1ZgVljoxAdWNn3hiEFrjZLZGS7lOH-a3QQlDDQoJOJ2VFmU', + 'q' => 'te8LY4-W7IyaqH1ExujjMqkTAlTeRbv0VLQnfLY2xINnrWdwiQ93_VF099aP1ESeLja2nw-6iKIe-qT7mtCPozKfVtUYfz5HrJ_XY2kfexJINb9lhZHMv5p1skZpeIS-GPHCC6gRlKo1q-idn_qxyusfWv7WAxlSVfQfk8d6Et0', + 'dp' => 'UfYKcL_or492vVc0PzwLSplbg4L3-Z5wL48mwiswbpzOyIgd2xHTHQmjJpFAIZ8q-zf9RmgJXkDrFs9rkdxPtAsL1WYdeCT5c125Fkdg317JVRDo1inX7x2Kdh8ERCreW8_4zXItuTl_KiXZNU5lvMQjWbIw2eTx1lpsflo0rYU', + 'dq' => 'iEgcO-QfpepdH8FWd7mUFyrXdnOkXJBCogChY6YKuIHGc_p8Le9MbpFKESzEaLlN1Ehf3B6oGBl5Iz_ayUlZj2IoQZ82znoUrpa9fVYNot87ACfzIG7q9Mv7RiPAderZi03tkVXAdaBau_9vs5rS-7HMtxkVrxSUvJY14TkXlHE', + 'qi' => 'kC-lzZOqoFaZCr5l0tOVtREKoVqaAYhQiqIRGL-MzS4sCmRkxm5vZlXYx6RtE1n_AagjqajlkjieGlxTTThHD8Iga6foGBMaAr5uR1hGQpSc7Gl7CF1DZkBJMTQN6EshYzZfxW08mIO8M6Rzuh0beL6fG9mkDcIyPrBXx2bQ_mM', + ]); + + $recipient_2_public_key = new JWK([ + 'kty' => 'EC', + 'kid' => 'peregrin.took@tuckborough.example', + 'use' => 'enc', + 'crv' => 'P-384', + 'x' => 'YU4rRUzdmVqmRtWOs2OpDE_T5fsNIodcG8G5FWPrTPMyxpzsSOGaQLpe2FpxBmu2', + 'y' => 'A8-yxCHxkfBz3hKZfI1jUYMjUhsEveZ9THuwFjH2sCNdtksRJU7D5-SkgaFL1ETP', + ]); + + $recipient_2_private_key = new JWK([ + 'kty' => 'EC', + 'kid' => 'peregrin.took@tuckborough.example', + 'use' => 'enc', + 'crv' => 'P-384', + 'x' => 'YU4rRUzdmVqmRtWOs2OpDE_T5fsNIodcG8G5FWPrTPMyxpzsSOGaQLpe2FpxBmu2', + 'y' => 'A8-yxCHxkfBz3hKZfI1jUYMjUhsEveZ9THuwFjH2sCNdtksRJU7D5-SkgaFL1ETP', + 'd' => 'iTx2pk7wW-GqJkHcEkFQb2EFyYcO7RugmaW3mRrQVAOUiPommT0IdnYK2xDlZh-j', + ]); + + $recipient_3_private_key = new JWK([ + 'kty' => 'oct', + 'kid' => '18ec08e1-bfa9-4d95-b205-2b4dd1d4321d', + 'use' => 'enc', + 'alg' => 'A256GCMKW', + 'k' => 'qC57l_uxcm7Nm3K-ct4GFjx8tM1U8CZ0NLBvdQstiS8', + ]); + + $protectedHeader = [ + 'enc' => 'A128CBC-HS256', + ]; + + $header = [ + 'cty' => 'text/plain', + ]; + + $recipient_1Header = [ + 'alg' => 'RSA1_5', + 'kid' => 'frodo.baggins@hobbiton.example', + ]; + + $recipient_2Header = [ + 'alg' => 'ECDH-ES+A256KW', + 'kid' => 'peregrin.took@tuckborough.example', + ]; + + $recipient_3Header = [ + 'alg' => 'A256GCMKW', + 'kid' => '18ec08e1-bfa9-4d95-b205-2b4dd1d4321d', + ]; + + $jweBuilder = $this->getJWEBuilderFactory()->create(['RSA1_5', 'ECDH-ES+A256KW', 'A256GCMKW'], ['A128CBC-HS256'], ['DEF']); + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['RSA1_5', 'ECDH-ES+A256KW', 'A256GCMKW'], ['A128CBC-HS256'], ['DEF']); + + $jwe = $jweBuilder + ->create()->withPayload($expected_payload) + ->withSharedProtectedHeader($protectedHeader) + ->withSharedHeader($header) + ->addRecipient($recipient_1_private_key, $recipient_1Header) + ->addRecipient($recipient_2_public_key, $recipient_2Header) + ->addRecipient($recipient_3_private_key, $recipient_3Header) + ->build() + ; + + $loaded_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_json_general', $jwe)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $recipient_1_private_key, 0)); + + $loaded_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_json_general', $jwe)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $recipient_2_private_key, 1)); + + $loaded_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_json_general', $jwe)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $recipient_3_private_key, 2)); + + static::assertEquals($protectedHeader, $loaded_json->getSharedProtectedHeader()); + static::assertEquals($recipient_1Header, $loaded_json->getRecipient(0)->getHeader()); + static::assertTrue(array_key_exists('epk', $loaded_json->getRecipient(1)->getHeader())); + static::assertTrue(array_key_exists('iv', $loaded_json->getRecipient(2)->getHeader())); + static::assertTrue(array_key_exists('tag', $loaded_json->getRecipient(2)->getHeader())); + static::assertEquals($header, $loaded_json->getSharedHeader()); + + static::assertEquals($expected_payload, $loaded_json->getPayload()); + } + + /** + * @test + */ + public function multipleRecipientEncryptionWithDifferentContentEncryptionAlgorithm(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Inconsistent content encryption algorithm'); + + $expected_payload = "You can trust us to stick with you through thick and thin\xe2\x80\x93to the bitter end. And you can trust us to keep any secret of yours\xe2\x80\x93closer than you keep it yourself. But you cannot trust us to let you face trouble alone, and go off without a word. We are your friends, Frodo."; + + $recipient_1_private_key = new JWK([ + 'kty' => 'RSA', + 'kid' => 'frodo.baggins@hobbiton.example', + 'use' => 'enc', + 'n' => 'maxhbsmBtdQ3CNrKvprUE6n9lYcregDMLYNeTAWcLj8NnPU9XIYegTHVHQjxKDSHP2l-F5jS7sppG1wgdAqZyhnWvXhYNvcM7RfgKxqNx_xAHx6f3yy7s-M9PSNCwPC2lh6UAkR4I00EhV9lrypM9Pi4lBUop9t5fS9W5UNwaAllhrd-osQGPjIeI1deHTwx-ZTHu3C60Pu_LJIl6hKn9wbwaUmA4cR5Bd2pgbaY7ASgsjCUbtYJaNIHSoHXprUdJZKUMAzV0WOKPfA6OPI4oypBadjvMZ4ZAj3BnXaSYsEZhaueTXvZB4eZOAjIyh2e_VOIKVMsnDrJYAVotGlvMQ', + 'e' => 'AQAB', + 'd' => 'Kn9tgoHfiTVi8uPu5b9TnwyHwG5dK6RE0uFdlpCGnJN7ZEi963R7wybQ1PLAHmpIbNTztfrheoAniRV1NCIqXaW_qS461xiDTp4ntEPnqcKsyO5jMAji7-CL8vhpYYowNFvIesgMoVaPRYMYT9TW63hNM0aWs7USZ_hLg6Oe1mY0vHTI3FucjSM86Nff4oIENt43r2fspgEPGRrdE6fpLc9Oaq-qeP1GFULimrRdndm-P8q8kvN3KHlNAtEgrQAgTTgz80S-3VD0FgWfgnb1PNmiuPUxO8OpI9KDIfu_acc6fg14nsNaJqXe6RESvhGPH2afjHqSy_Fd2vpzj85bQQ', + 'p' => '2DwQmZ43FoTnQ8IkUj3BmKRf5Eh2mizZA5xEJ2MinUE3sdTYKSLtaEoekX9vbBZuWxHdVhM6UnKCJ_2iNk8Z0ayLYHL0_G21aXf9-unynEpUsH7HHTklLpYAzOOx1ZgVljoxAdWNn3hiEFrjZLZGS7lOH-a3QQlDDQoJOJ2VFmU', + 'q' => 'te8LY4-W7IyaqH1ExujjMqkTAlTeRbv0VLQnfLY2xINnrWdwiQ93_VF099aP1ESeLja2nw-6iKIe-qT7mtCPozKfVtUYfz5HrJ_XY2kfexJINb9lhZHMv5p1skZpeIS-GPHCC6gRlKo1q-idn_qxyusfWv7WAxlSVfQfk8d6Et0', + 'dp' => 'UfYKcL_or492vVc0PzwLSplbg4L3-Z5wL48mwiswbpzOyIgd2xHTHQmjJpFAIZ8q-zf9RmgJXkDrFs9rkdxPtAsL1WYdeCT5c125Fkdg317JVRDo1inX7x2Kdh8ERCreW8_4zXItuTl_KiXZNU5lvMQjWbIw2eTx1lpsflo0rYU', + 'dq' => 'iEgcO-QfpepdH8FWd7mUFyrXdnOkXJBCogChY6YKuIHGc_p8Le9MbpFKESzEaLlN1Ehf3B6oGBl5Iz_ayUlZj2IoQZ82znoUrpa9fVYNot87ACfzIG7q9Mv7RiPAderZi03tkVXAdaBau_9vs5rS-7HMtxkVrxSUvJY14TkXlHE', + 'qi' => 'kC-lzZOqoFaZCr5l0tOVtREKoVqaAYhQiqIRGL-MzS4sCmRkxm5vZlXYx6RtE1n_AagjqajlkjieGlxTTThHD8Iga6foGBMaAr5uR1hGQpSc7Gl7CF1DZkBJMTQN6EshYzZfxW08mIO8M6Rzuh0beL6fG9mkDcIyPrBXx2bQ_mM', + ]); + + $recipient_2_public_key = new JWK([ + 'kty' => 'EC', + 'kid' => 'peregrin.took@tuckborough.example', + 'use' => 'enc', + 'crv' => 'P-384', + 'x' => 'YU4rRUzdmVqmRtWOs2OpDE_T5fsNIodcG8G5FWPrTPMyxpzsSOGaQLpe2FpxBmu2', + 'y' => 'A8-yxCHxkfBz3hKZfI1jUYMjUhsEveZ9THuwFjH2sCNdtksRJU7D5-SkgaFL1ETP', + ]); + + $protectedHeader = []; + + $header = [ + 'cty' => 'text/plain', + ]; + + $recipient_1Header = [ + 'alg' => 'RSA1_5', + 'enc' => 'A128GCM', + 'kid' => 'frodo.baggins@hobbiton.example', + ]; + + $recipient_2Header = [ + 'alg' => 'ECDH-ES+A256KW', + 'enc' => 'A128CBC-HS256', + 'kid' => 'peregrin.took@tuckborough.example', + ]; + + $jweBuilder = $this->getJWEBuilderFactory()->create(['RSA1_5', 'A256GCMKW'], ['A128CBC-HS256', 'A128GCM'], ['DEF']); + $jweBuilder + ->create() + ->withPayload($expected_payload) + ->withSharedProtectedHeader($protectedHeader) + ->withSharedHeader($header) + ->addRecipient($recipient_1_private_key, $recipient_1Header) + ->addRecipient($recipient_2_public_key, $recipient_2Header) + ->build() + ; + } +} diff --git a/vendor/web-token/jwt-encryption/Tests/RFC7520/PBES2_HS512_A256KWAndA128CBC_HS256EncryptionTest.php b/vendor/web-token/jwt-encryption/Tests/RFC7520/PBES2_HS512_A256KWAndA128CBC_HS256EncryptionTest.php new file mode 100644 index 000000000..51fb07b27 --- /dev/null +++ b/vendor/web-token/jwt-encryption/Tests/RFC7520/PBES2_HS512_A256KWAndA128CBC_HS256EncryptionTest.php @@ -0,0 +1,182 @@ + [ + [ + 'kty' => 'oct', + 'kid' => '77c7e2b8-6e13-45cf-8672-617b5b45243a', + 'use' => 'enc', + 'alg' => 'A128GCM', + 'k' => 'XctOhJAkA-pD9Lh7ZgW_2A', + ], [ + 'kty' => 'oct', + 'kid' => '81b20965-8332-43d9-a468-82160ad91ac8', + 'use' => 'enc', + 'alg' => 'A128KW', + 'k' => 'GZy6sIZ6wl9NJOKB-jnmVQ', + ], [ + 'kty' => 'oct', + 'kid' => '18ec08e1-bfa9-4d95-b205-2b4dd1d4321d', + 'use' => 'enc', + 'alg' => 'A256GCMKW', + 'k' => 'qC57l_uxcm7Nm3K-ct4GFjx8tM1U8CZ0NLBvdQstiS8', + ], + ]]; + + $private_key = new JWK([ + 'kty' => 'oct', + 'use' => 'enc', + 'k' => Base64Url::encode("entrap_o\xe2\x80\x93peter_long\xe2\x80\x93credit_tun"), + ]); + + $protectedHeader = [ + 'alg' => 'PBES2-HS512+A256KW', + 'p2s' => '8Q1SzinasR3xchYz6ZZcHA', + 'p2c' => 8192, + 'cty' => 'jwk-set+json', + 'enc' => 'A128CBC-HS256', + ]; + + $expected_compact_json = 'eyJhbGciOiJQQkVTMi1IUzUxMitBMjU2S1ciLCJwMnMiOiI4UTFTemluYXNSM3hjaFl6NlpaY0hBIiwicDJjIjo4MTkyLCJjdHkiOiJqd2stc2V0K2pzb24iLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.d3qNhUWfqheyPp4H8sjOWsDYajoej4c5Je6rlUtFPWdgtURtmeDV1g.VBiCzVHNoLiR3F4V82uoTQ.23i-Tb1AV4n0WKVSSgcQrdg6GRqsUKxjruHXYsTHAJLZ2nsnGIX86vMXqIi6IRsfywCRFzLxEcZBRnTvG3nhzPk0GDD7FMyXhUHpDjEYCNA_XOmzg8yZR9oyjo6lTF6si4q9FZ2EhzgFQCLO_6h5EVg3vR75_hkBsnuoqoM3dwejXBtIodN84PeqMb6asmas_dpSsz7H10fC5ni9xIz424givB1YLldF6exVmL93R3fOoOJbmk2GBQZL_SEGllv2cQsBgeprARsaQ7Bq99tT80coH8ItBjgV08AtzXFFsx9qKvC982KLKdPQMTlVJKkqtV4Ru5LEVpBZXBnZrtViSOgyg6AiuwaS-rCrcD_ePOGSuxvgtrokAKYPqmXUeRdjFJwafkYEkiuDCV9vWGAi1DH2xTafhJwcmywIyzi4BqRpmdn_N-zl5tuJYyuvKhjKv6ihbsV_k1hJGPGAxJ6wUpmwC4PTQ2izEm0TuSE8oMKdTw8V3kobXZ77ulMwDs4p.0HlwodAhOCILG5SQ2LQ9dg'; + $expected_flattened_json = '{"protected":"eyJhbGciOiJQQkVTMi1IUzUxMitBMjU2S1ciLCJwMnMiOiI4UTFTemluYXNSM3hjaFl6NlpaY0hBIiwicDJjIjo4MTkyLCJjdHkiOiJqd2stc2V0K2pzb24iLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0","encrypted_key":"d3qNhUWfqheyPp4H8sjOWsDYajoej4c5Je6rlUtFPWdgtURtmeDV1g","iv":"VBiCzVHNoLiR3F4V82uoTQ","ciphertext":"23i-Tb1AV4n0WKVSSgcQrdg6GRqsUKxjruHXYsTHAJLZ2nsnGIX86vMXqIi6IRsfywCRFzLxEcZBRnTvG3nhzPk0GDD7FMyXhUHpDjEYCNA_XOmzg8yZR9oyjo6lTF6si4q9FZ2EhzgFQCLO_6h5EVg3vR75_hkBsnuoqoM3dwejXBtIodN84PeqMb6asmas_dpSsz7H10fC5ni9xIz424givB1YLldF6exVmL93R3fOoOJbmk2GBQZL_SEGllv2cQsBgeprARsaQ7Bq99tT80coH8ItBjgV08AtzXFFsx9qKvC982KLKdPQMTlVJKkqtV4Ru5LEVpBZXBnZrtViSOgyg6AiuwaS-rCrcD_ePOGSuxvgtrokAKYPqmXUeRdjFJwafkYEkiuDCV9vWGAi1DH2xTafhJwcmywIyzi4BqRpmdn_N-zl5tuJYyuvKhjKv6ihbsV_k1hJGPGAxJ6wUpmwC4PTQ2izEm0TuSE8oMKdTw8V3kobXZ77ulMwDs4p","tag":"0HlwodAhOCILG5SQ2LQ9dg"}'; + $expected_json = '{"recipients":[{"encrypted_key":"d3qNhUWfqheyPp4H8sjOWsDYajoej4c5Je6rlUtFPWdgtURtmeDV1g"}],"protected":"eyJhbGciOiJQQkVTMi1IUzUxMitBMjU2S1ciLCJwMnMiOiI4UTFTemluYXNSM3hjaFl6NlpaY0hBIiwicDJjIjo4MTkyLCJjdHkiOiJqd2stc2V0K2pzb24iLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0","iv":"VBiCzVHNoLiR3F4V82uoTQ","ciphertext":"23i-Tb1AV4n0WKVSSgcQrdg6GRqsUKxjruHXYsTHAJLZ2nsnGIX86vMXqIi6IRsfywCRFzLxEcZBRnTvG3nhzPk0GDD7FMyXhUHpDjEYCNA_XOmzg8yZR9oyjo6lTF6si4q9FZ2EhzgFQCLO_6h5EVg3vR75_hkBsnuoqoM3dwejXBtIodN84PeqMb6asmas_dpSsz7H10fC5ni9xIz424givB1YLldF6exVmL93R3fOoOJbmk2GBQZL_SEGllv2cQsBgeprARsaQ7Bq99tT80coH8ItBjgV08AtzXFFsx9qKvC982KLKdPQMTlVJKkqtV4Ru5LEVpBZXBnZrtViSOgyg6AiuwaS-rCrcD_ePOGSuxvgtrokAKYPqmXUeRdjFJwafkYEkiuDCV9vWGAi1DH2xTafhJwcmywIyzi4BqRpmdn_N-zl5tuJYyuvKhjKv6ihbsV_k1hJGPGAxJ6wUpmwC4PTQ2izEm0TuSE8oMKdTw8V3kobXZ77ulMwDs4p","tag":"0HlwodAhOCILG5SQ2LQ9dg"}'; + $expected_iv = 'VBiCzVHNoLiR3F4V82uoTQ'; + $expected_encrypted_key = 'd3qNhUWfqheyPp4H8sjOWsDYajoej4c5Je6rlUtFPWdgtURtmeDV1g'; + $expected_ciphertext = '23i-Tb1AV4n0WKVSSgcQrdg6GRqsUKxjruHXYsTHAJLZ2nsnGIX86vMXqIi6IRsfywCRFzLxEcZBRnTvG3nhzPk0GDD7FMyXhUHpDjEYCNA_XOmzg8yZR9oyjo6lTF6si4q9FZ2EhzgFQCLO_6h5EVg3vR75_hkBsnuoqoM3dwejXBtIodN84PeqMb6asmas_dpSsz7H10fC5ni9xIz424givB1YLldF6exVmL93R3fOoOJbmk2GBQZL_SEGllv2cQsBgeprARsaQ7Bq99tT80coH8ItBjgV08AtzXFFsx9qKvC982KLKdPQMTlVJKkqtV4Ru5LEVpBZXBnZrtViSOgyg6AiuwaS-rCrcD_ePOGSuxvgtrokAKYPqmXUeRdjFJwafkYEkiuDCV9vWGAi1DH2xTafhJwcmywIyzi4BqRpmdn_N-zl5tuJYyuvKhjKv6ihbsV_k1hJGPGAxJ6wUpmwC4PTQ2izEm0TuSE8oMKdTw8V3kobXZ77ulMwDs4p'; + $expected_tag = '0HlwodAhOCILG5SQ2LQ9dg'; + + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['PBES2-HS512+A256KW'], ['A128CBC-HS256'], ['DEF']); + + $loaded_compact_json = $this->getJWESerializerManager()->unserialize($expected_compact_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_compact_json, $private_key, 0)); + + $loaded_flattened_json = $this->getJWESerializerManager()->unserialize($expected_flattened_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_flattened_json, $private_key, 0)); + + $loaded_json = $this->getJWESerializerManager()->unserialize($expected_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $private_key, 0)); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_compact_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_compact_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_compact_json->getIV())); + static::assertEquals($expected_encrypted_key, Base64Url::encode($loaded_compact_json->getRecipient(0)->getEncryptedKey())); + static::assertEquals($expected_tag, Base64Url::encode($loaded_compact_json->getTag())); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_flattened_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_flattened_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_flattened_json->getIV())); + static::assertEquals($expected_encrypted_key, Base64Url::encode($loaded_flattened_json->getRecipient(0)->getEncryptedKey())); + static::assertEquals($expected_tag, Base64Url::encode($loaded_flattened_json->getTag())); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_json->getIV())); + static::assertEquals($expected_encrypted_key, Base64Url::encode($loaded_json->getRecipient(0)->getEncryptedKey())); + static::assertEquals($expected_tag, Base64Url::encode($loaded_json->getTag())); + + static::assertEquals($expected_payload, json_decode($loaded_compact_json->getPayload(), true)); + static::assertEquals($expected_payload, json_decode($loaded_flattened_json->getPayload(), true)); + static::assertEquals($expected_payload, json_decode($loaded_json->getPayload(), true)); + } + + /** + * Same input as before, but we perform the encryption first. + * + * @test + */ + public function pBES2HS512A256KWAndA128CBCHS256EncryptionBis(): void + { + $expected_payload = json_encode(['keys' => [ + [ + 'kty' => 'oct', + 'kid' => '77c7e2b8-6e13-45cf-8672-617b5b45243a', + 'use' => 'enc', + 'alg' => 'A128GCM', + 'k' => 'XctOhJAkA-pD9Lh7ZgW_2A', + ], [ + 'kty' => 'oct', + 'kid' => '81b20965-8332-43d9-a468-82160ad91ac8', + 'use' => 'enc', + 'alg' => 'A128KW', + 'k' => 'GZy6sIZ6wl9NJOKB-jnmVQ', + ], [ + 'kty' => 'oct', + 'kid' => '18ec08e1-bfa9-4d95-b205-2b4dd1d4321d', + 'use' => 'enc', + 'alg' => 'A256GCMKW', + 'k' => 'qC57l_uxcm7Nm3K-ct4GFjx8tM1U8CZ0NLBvdQstiS8', + ], + ]]); + + $private_key = new JWK([ + 'kty' => 'oct', + 'use' => 'enc', + 'k' => Base64Url::encode("entrap_o\xe2\x80\x93peter_long\xe2\x80\x93credit_tun"), + ]); + + $protectedHeader = [ + 'alg' => 'PBES2-HS512+A256KW', + 'cty' => 'jwk-set+json', + 'enc' => 'A128CBC-HS256', + ]; + + $jweBuilder = $this->getJWEBuilderFactory()->create(['PBES2-HS512+A256KW'], ['A128CBC-HS256'], ['DEF']); + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['PBES2-HS512+A256KW'], ['A128CBC-HS256'], ['DEF']); + + $jwe = $jweBuilder + ->create()->withPayload($expected_payload) + ->withSharedProtectedHeader($protectedHeader) + ->addRecipient($private_key) + ->build() + ; + + $loaded_flattened_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_json_flattened', $jwe, 0)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_flattened_json, $private_key, 0)); + + $loaded_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_json_general', $jwe)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $private_key, 0)); + + static::assertTrue(array_key_exists('p2s', $loaded_flattened_json->getSharedProtectedHeader())); + static::assertTrue(array_key_exists('p2c', $loaded_flattened_json->getSharedProtectedHeader())); + + static::assertTrue(array_key_exists('p2s', $loaded_json->getSharedProtectedHeader())); + static::assertTrue(array_key_exists('p2c', $loaded_json->getSharedProtectedHeader())); + + static::assertEquals($expected_payload, $loaded_flattened_json->getPayload()); + static::assertEquals($expected_payload, $loaded_json->getPayload()); + } +} diff --git a/vendor/web-token/jwt-encryption/Tests/RFC7520/RSA1_5AndA128CBC_HS256EncryptionTest.php b/vendor/web-token/jwt-encryption/Tests/RFC7520/RSA1_5AndA128CBC_HS256EncryptionTest.php new file mode 100644 index 000000000..55f828c62 --- /dev/null +++ b/vendor/web-token/jwt-encryption/Tests/RFC7520/RSA1_5AndA128CBC_HS256EncryptionTest.php @@ -0,0 +1,159 @@ + 'RSA', + 'kid' => 'frodo.baggins@hobbiton.example', + 'use' => 'enc', + 'n' => 'maxhbsmBtdQ3CNrKvprUE6n9lYcregDMLYNeTAWcLj8NnPU9XIYegTHVHQjxKDSHP2l-F5jS7sppG1wgdAqZyhnWvXhYNvcM7RfgKxqNx_xAHx6f3yy7s-M9PSNCwPC2lh6UAkR4I00EhV9lrypM9Pi4lBUop9t5fS9W5UNwaAllhrd-osQGPjIeI1deHTwx-ZTHu3C60Pu_LJIl6hKn9wbwaUmA4cR5Bd2pgbaY7ASgsjCUbtYJaNIHSoHXprUdJZKUMAzV0WOKPfA6OPI4oypBadjvMZ4ZAj3BnXaSYsEZhaueTXvZB4eZOAjIyh2e_VOIKVMsnDrJYAVotGlvMQ', + 'e' => 'AQAB', + 'd' => 'Kn9tgoHfiTVi8uPu5b9TnwyHwG5dK6RE0uFdlpCGnJN7ZEi963R7wybQ1PLAHmpIbNTztfrheoAniRV1NCIqXaW_qS461xiDTp4ntEPnqcKsyO5jMAji7-CL8vhpYYowNFvIesgMoVaPRYMYT9TW63hNM0aWs7USZ_hLg6Oe1mY0vHTI3FucjSM86Nff4oIENt43r2fspgEPGRrdE6fpLc9Oaq-qeP1GFULimrRdndm-P8q8kvN3KHlNAtEgrQAgTTgz80S-3VD0FgWfgnb1PNmiuPUxO8OpI9KDIfu_acc6fg14nsNaJqXe6RESvhGPH2afjHqSy_Fd2vpzj85bQQ', + 'p' => '2DwQmZ43FoTnQ8IkUj3BmKRf5Eh2mizZA5xEJ2MinUE3sdTYKSLtaEoekX9vbBZuWxHdVhM6UnKCJ_2iNk8Z0ayLYHL0_G21aXf9-unynEpUsH7HHTklLpYAzOOx1ZgVljoxAdWNn3hiEFrjZLZGS7lOH-a3QQlDDQoJOJ2VFmU', + 'q' => 'te8LY4-W7IyaqH1ExujjMqkTAlTeRbv0VLQnfLY2xINnrWdwiQ93_VF099aP1ESeLja2nw-6iKIe-qT7mtCPozKfVtUYfz5HrJ_XY2kfexJINb9lhZHMv5p1skZpeIS-GPHCC6gRlKo1q-idn_qxyusfWv7WAxlSVfQfk8d6Et0', + 'dp' => 'UfYKcL_or492vVc0PzwLSplbg4L3-Z5wL48mwiswbpzOyIgd2xHTHQmjJpFAIZ8q-zf9RmgJXkDrFs9rkdxPtAsL1WYdeCT5c125Fkdg317JVRDo1inX7x2Kdh8ERCreW8_4zXItuTl_KiXZNU5lvMQjWbIw2eTx1lpsflo0rYU', + 'dq' => 'iEgcO-QfpepdH8FWd7mUFyrXdnOkXJBCogChY6YKuIHGc_p8Le9MbpFKESzEaLlN1Ehf3B6oGBl5Iz_ayUlZj2IoQZ82znoUrpa9fVYNot87ACfzIG7q9Mv7RiPAderZi03tkVXAdaBau_9vs5rS-7HMtxkVrxSUvJY14TkXlHE', + 'qi' => 'kC-lzZOqoFaZCr5l0tOVtREKoVqaAYhQiqIRGL-MzS4sCmRkxm5vZlXYx6RtE1n_AagjqajlkjieGlxTTThHD8Iga6foGBMaAr5uR1hGQpSc7Gl7CF1DZkBJMTQN6EshYzZfxW08mIO8M6Rzuh0beL6fG9mkDcIyPrBXx2bQ_mM', + ]); + + $protectedHeader = [ + 'alg' => 'RSA1_5', + 'kid' => 'frodo.baggins@hobbiton.example', + 'enc' => 'A128CBC-HS256', + ]; + + $expected_compact_json = 'eyJhbGciOiJSU0ExXzUiLCJraWQiOiJmcm9kby5iYWdnaW5zQGhvYmJpdG9uLmV4YW1wbGUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.laLxI0j-nLH-_BgLOXMozKxmy9gffy2gTdvqzfTihJBuuzxg0V7yk1WClnQePFvG2K-pvSlWc9BRIazDrn50RcRai__3TDON395H3c62tIouJJ4XaRvYHFjZTZ2GXfz8YAImcc91Tfk0WXC2F5Xbb71ClQ1DDH151tlpH77f2ff7xiSxh9oSewYrcGTSLUeeCt36r1Kt3OSj7EyBQXoZlN7IxbyhMAfgIe7Mv1rOTOI5I8NQqeXXW8VlzNmoxaGMny3YnGir5Wf6Qt2nBq4qDaPdnaAuuGUGEecelIO1wx1BpyIfgvfjOhMBs9M8XL223Fg47xlGsMXdfuY-4jaqVw.bbd5sTkYwhAIqfHsx8DayA.0fys_TY_na7f8dwSfXLiYdHaA2DxUjD67ieF7fcVbIR62JhJvGZ4_FNVSiGc_raa0HnLQ6s1P2sv3Xzl1p1l_o5wR_RsSzrS8Z-wnI3Jvo0mkpEEnlDmZvDu_k8OWzJv7eZVEqiWKdyVzFhPpiyQU28GLOpRc2VbVbK4dQKPdNTjPPEmRqcaGeTWZVyeSUvf5k59yJZxRuSvWFf6KrNtmRdZ8R4mDOjHSrM_s8uwIFcqt4r5GX8TKaI0zT5CbL5Qlw3sRc7u_hg0yKVOiRytEAEs3vZkcfLkP6nbXdC_PkMdNS-ohP78T2O6_7uInMGhFeX4ctHG7VelHGiT93JfWDEQi5_V9UN1rhXNrYu-0fVMkZAKX3VWi7lzA6BP430m.kvKuFBXHe5mQr4lqgobAUg'; + $expected_flattened_json = '{"protected":"eyJhbGciOiJSU0ExXzUiLCJraWQiOiJmcm9kby5iYWdnaW5zQGhvYmJpdG9uLmV4YW1wbGUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0","encrypted_key":"laLxI0j-nLH-_BgLOXMozKxmy9gffy2gTdvqzfTihJBuuzxg0V7yk1WClnQePFvG2K-pvSlWc9BRIazDrn50RcRai__3TDON395H3c62tIouJJ4XaRvYHFjZTZ2GXfz8YAImcc91Tfk0WXC2F5Xbb71ClQ1DDH151tlpH77f2ff7xiSxh9oSewYrcGTSLUeeCt36r1Kt3OSj7EyBQXoZlN7IxbyhMAfgIe7Mv1rOTOI5I8NQqeXXW8VlzNmoxaGMny3YnGir5Wf6Qt2nBq4qDaPdnaAuuGUGEecelIO1wx1BpyIfgvfjOhMBs9M8XL223Fg47xlGsMXdfuY-4jaqVw","iv":"bbd5sTkYwhAIqfHsx8DayA","ciphertext":"0fys_TY_na7f8dwSfXLiYdHaA2DxUjD67ieF7fcVbIR62JhJvGZ4_FNVSiGc_raa0HnLQ6s1P2sv3Xzl1p1l_o5wR_RsSzrS8Z-wnI3Jvo0mkpEEnlDmZvDu_k8OWzJv7eZVEqiWKdyVzFhPpiyQU28GLOpRc2VbVbK4dQKPdNTjPPEmRqcaGeTWZVyeSUvf5k59yJZxRuSvWFf6KrNtmRdZ8R4mDOjHSrM_s8uwIFcqt4r5GX8TKaI0zT5CbL5Qlw3sRc7u_hg0yKVOiRytEAEs3vZkcfLkP6nbXdC_PkMdNS-ohP78T2O6_7uInMGhFeX4ctHG7VelHGiT93JfWDEQi5_V9UN1rhXNrYu-0fVMkZAKX3VWi7lzA6BP430m","tag":"kvKuFBXHe5mQr4lqgobAUg"}'; + $expected_json = '{"recipients":[{"encrypted_key":"laLxI0j-nLH-_BgLOXMozKxmy9gffy2gTdvqzfTihJBuuzxg0V7yk1WClnQePFvG2K-pvSlWc9BRIazDrn50RcRai__3TDON395H3c62tIouJJ4XaRvYHFjZTZ2GXfz8YAImcc91Tfk0WXC2F5Xbb71ClQ1DDH151tlpH77f2ff7xiSxh9oSewYrcGTSLUeeCt36r1Kt3OSj7EyBQXoZlN7IxbyhMAfgIe7Mv1rOTOI5I8NQqeXXW8VlzNmoxaGMny3YnGir5Wf6Qt2nBq4qDaPdnaAuuGUGEecelIO1wx1BpyIfgvfjOhMBs9M8XL223Fg47xlGsMXdfuY-4jaqVw"}],"protected":"eyJhbGciOiJSU0ExXzUiLCJraWQiOiJmcm9kby5iYWdnaW5zQGhvYmJpdG9uLmV4YW1wbGUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0","iv":"bbd5sTkYwhAIqfHsx8DayA","ciphertext":"0fys_TY_na7f8dwSfXLiYdHaA2DxUjD67ieF7fcVbIR62JhJvGZ4_FNVSiGc_raa0HnLQ6s1P2sv3Xzl1p1l_o5wR_RsSzrS8Z-wnI3Jvo0mkpEEnlDmZvDu_k8OWzJv7eZVEqiWKdyVzFhPpiyQU28GLOpRc2VbVbK4dQKPdNTjPPEmRqcaGeTWZVyeSUvf5k59yJZxRuSvWFf6KrNtmRdZ8R4mDOjHSrM_s8uwIFcqt4r5GX8TKaI0zT5CbL5Qlw3sRc7u_hg0yKVOiRytEAEs3vZkcfLkP6nbXdC_PkMdNS-ohP78T2O6_7uInMGhFeX4ctHG7VelHGiT93JfWDEQi5_V9UN1rhXNrYu-0fVMkZAKX3VWi7lzA6BP430m","tag":"kvKuFBXHe5mQr4lqgobAUg"}'; + $expected_iv = 'bbd5sTkYwhAIqfHsx8DayA'; + $expected_encrypted_key = 'laLxI0j-nLH-_BgLOXMozKxmy9gffy2gTdvqzfTihJBuuzxg0V7yk1WClnQePFvG2K-pvSlWc9BRIazDrn50RcRai__3TDON395H3c62tIouJJ4XaRvYHFjZTZ2GXfz8YAImcc91Tfk0WXC2F5Xbb71ClQ1DDH151tlpH77f2ff7xiSxh9oSewYrcGTSLUeeCt36r1Kt3OSj7EyBQXoZlN7IxbyhMAfgIe7Mv1rOTOI5I8NQqeXXW8VlzNmoxaGMny3YnGir5Wf6Qt2nBq4qDaPdnaAuuGUGEecelIO1wx1BpyIfgvfjOhMBs9M8XL223Fg47xlGsMXdfuY-4jaqVw'; + $expected_ciphertext = '0fys_TY_na7f8dwSfXLiYdHaA2DxUjD67ieF7fcVbIR62JhJvGZ4_FNVSiGc_raa0HnLQ6s1P2sv3Xzl1p1l_o5wR_RsSzrS8Z-wnI3Jvo0mkpEEnlDmZvDu_k8OWzJv7eZVEqiWKdyVzFhPpiyQU28GLOpRc2VbVbK4dQKPdNTjPPEmRqcaGeTWZVyeSUvf5k59yJZxRuSvWFf6KrNtmRdZ8R4mDOjHSrM_s8uwIFcqt4r5GX8TKaI0zT5CbL5Qlw3sRc7u_hg0yKVOiRytEAEs3vZkcfLkP6nbXdC_PkMdNS-ohP78T2O6_7uInMGhFeX4ctHG7VelHGiT93JfWDEQi5_V9UN1rhXNrYu-0fVMkZAKX3VWi7lzA6BP430m'; + $expected_tag = 'kvKuFBXHe5mQr4lqgobAUg'; + + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['RSA1_5'], ['A128CBC-HS256'], ['DEF']); + + $loaded_compact_json = $this->getJWESerializerManager()->unserialize($expected_compact_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_compact_json, $private_key, 0)); + + $loaded_flattened_json = $this->getJWESerializerManager()->unserialize($expected_flattened_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_flattened_json, $private_key, 0)); + + $loaded_json = $this->getJWESerializerManager()->unserialize($expected_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $private_key, 0)); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_compact_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_compact_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_compact_json->getIV())); + static::assertEquals($expected_encrypted_key, Base64Url::encode($loaded_compact_json->getRecipient(0)->getEncryptedKey())); + static::assertEquals($expected_tag, Base64Url::encode($loaded_compact_json->getTag())); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_flattened_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_flattened_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_flattened_json->getIV())); + static::assertEquals($expected_encrypted_key, Base64Url::encode($loaded_flattened_json->getRecipient(0)->getEncryptedKey())); + static::assertEquals($expected_tag, Base64Url::encode($loaded_flattened_json->getTag())); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_json->getIV())); + static::assertEquals($expected_encrypted_key, Base64Url::encode($loaded_json->getRecipient(0)->getEncryptedKey())); + static::assertEquals($expected_tag, Base64Url::encode($loaded_json->getTag())); + + static::assertEquals($expected_payload, $loaded_compact_json->getPayload()); + static::assertEquals($expected_payload, $loaded_flattened_json->getPayload()); + static::assertEquals($expected_payload, $loaded_json->getPayload()); + } + + /** + * Same input as before, but we perform the encryption first. + * + * @test + */ + public function rSA15AndA128CBCHS256EncryptionBis(): void + { + $expected_payload = "You can trust us to stick with you through thick and thin\xe2\x80\x93to the bitter end. And you can trust us to keep any secret of yours\xe2\x80\x93closer than you keep it yourself. But you cannot trust us to let you face trouble alone, and go off without a word. We are your friends, Frodo."; + + $private_key = new JWK([ + 'kty' => 'RSA', + 'kid' => 'frodo.baggins@hobbiton.example', + 'use' => 'enc', + 'n' => 'maxhbsmBtdQ3CNrKvprUE6n9lYcregDMLYNeTAWcLj8NnPU9XIYegTHVHQjxKDSHP2l-F5jS7sppG1wgdAqZyhnWvXhYNvcM7RfgKxqNx_xAHx6f3yy7s-M9PSNCwPC2lh6UAkR4I00EhV9lrypM9Pi4lBUop9t5fS9W5UNwaAllhrd-osQGPjIeI1deHTwx-ZTHu3C60Pu_LJIl6hKn9wbwaUmA4cR5Bd2pgbaY7ASgsjCUbtYJaNIHSoHXprUdJZKUMAzV0WOKPfA6OPI4oypBadjvMZ4ZAj3BnXaSYsEZhaueTXvZB4eZOAjIyh2e_VOIKVMsnDrJYAVotGlvMQ', + 'e' => 'AQAB', + 'd' => 'Kn9tgoHfiTVi8uPu5b9TnwyHwG5dK6RE0uFdlpCGnJN7ZEi963R7wybQ1PLAHmpIbNTztfrheoAniRV1NCIqXaW_qS461xiDTp4ntEPnqcKsyO5jMAji7-CL8vhpYYowNFvIesgMoVaPRYMYT9TW63hNM0aWs7USZ_hLg6Oe1mY0vHTI3FucjSM86Nff4oIENt43r2fspgEPGRrdE6fpLc9Oaq-qeP1GFULimrRdndm-P8q8kvN3KHlNAtEgrQAgTTgz80S-3VD0FgWfgnb1PNmiuPUxO8OpI9KDIfu_acc6fg14nsNaJqXe6RESvhGPH2afjHqSy_Fd2vpzj85bQQ', + 'p' => '2DwQmZ43FoTnQ8IkUj3BmKRf5Eh2mizZA5xEJ2MinUE3sdTYKSLtaEoekX9vbBZuWxHdVhM6UnKCJ_2iNk8Z0ayLYHL0_G21aXf9-unynEpUsH7HHTklLpYAzOOx1ZgVljoxAdWNn3hiEFrjZLZGS7lOH-a3QQlDDQoJOJ2VFmU', + 'q' => 'te8LY4-W7IyaqH1ExujjMqkTAlTeRbv0VLQnfLY2xINnrWdwiQ93_VF099aP1ESeLja2nw-6iKIe-qT7mtCPozKfVtUYfz5HrJ_XY2kfexJINb9lhZHMv5p1skZpeIS-GPHCC6gRlKo1q-idn_qxyusfWv7WAxlSVfQfk8d6Et0', + 'dp' => 'UfYKcL_or492vVc0PzwLSplbg4L3-Z5wL48mwiswbpzOyIgd2xHTHQmjJpFAIZ8q-zf9RmgJXkDrFs9rkdxPtAsL1WYdeCT5c125Fkdg317JVRDo1inX7x2Kdh8ERCreW8_4zXItuTl_KiXZNU5lvMQjWbIw2eTx1lpsflo0rYU', + 'dq' => 'iEgcO-QfpepdH8FWd7mUFyrXdnOkXJBCogChY6YKuIHGc_p8Le9MbpFKESzEaLlN1Ehf3B6oGBl5Iz_ayUlZj2IoQZ82znoUrpa9fVYNot87ACfzIG7q9Mv7RiPAderZi03tkVXAdaBau_9vs5rS-7HMtxkVrxSUvJY14TkXlHE', + 'qi' => 'kC-lzZOqoFaZCr5l0tOVtREKoVqaAYhQiqIRGL-MzS4sCmRkxm5vZlXYx6RtE1n_AagjqajlkjieGlxTTThHD8Iga6foGBMaAr5uR1hGQpSc7Gl7CF1DZkBJMTQN6EshYzZfxW08mIO8M6Rzuh0beL6fG9mkDcIyPrBXx2bQ_mM', + ]); + + $protectedHeader = [ + 'alg' => 'RSA1_5', + 'kid' => 'frodo.baggins@hobbiton.example', + 'enc' => 'A128CBC-HS256', + ]; + + $jweBuilder = $this->getJWEBuilderFactory()->create(['RSA1_5'], ['A128CBC-HS256'], ['DEF']); + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['RSA1_5'], ['A128CBC-HS256'], ['DEF']); + + $jwe = $jweBuilder + ->create()->withPayload($expected_payload) + ->withSharedProtectedHeader($protectedHeader) + ->addRecipient($private_key) + ->build() + ; + + $loaded_compact_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_compact', $jwe, 0)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_compact_json, $private_key, 0)); + + $loaded_flattened_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_json_flattened', $jwe, 0)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_flattened_json, $private_key, 0)); + + $loaded_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_json_general', $jwe)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $private_key, 0)); + + static::assertEquals($protectedHeader, $loaded_compact_json->getSharedProtectedHeader()); + + static::assertEquals($protectedHeader, $loaded_flattened_json->getSharedProtectedHeader()); + + static::assertEquals($protectedHeader, $loaded_json->getSharedProtectedHeader()); + + static::assertEquals($expected_payload, $loaded_compact_json->getPayload()); + static::assertEquals($expected_payload, $loaded_flattened_json->getPayload()); + static::assertEquals($expected_payload, $loaded_json->getPayload()); + } +} diff --git a/vendor/web-token/jwt-encryption/Tests/RFC7520/RSA_OAEPAndA256GCMEncryptionTest.php b/vendor/web-token/jwt-encryption/Tests/RFC7520/RSA_OAEPAndA256GCMEncryptionTest.php new file mode 100644 index 000000000..ff3de940d --- /dev/null +++ b/vendor/web-token/jwt-encryption/Tests/RFC7520/RSA_OAEPAndA256GCMEncryptionTest.php @@ -0,0 +1,161 @@ + 'RSA', + 'kid' => 'samwise.gamgee@hobbiton.example', + 'use' => 'enc', + 'n' => 'wbdxI55VaanZXPY29Lg5hdmv2XhvqAhoxUkanfzf2-5zVUxa6prHRrI4pP1AhoqJRlZfYtWWd5mmHRG2pAHIlh0ySJ9wi0BioZBl1XP2e-C-FyXJGcTy0HdKQWlrfhTm42EW7Vv04r4gfao6uxjLGwfpGrZLarohiWCPnkNrg71S2CuNZSQBIPGjXfkmIy2tl_VWgGnL22GplyXj5YlBLdxXp3XeStsqo571utNfoUTU8E4qdzJ3U1DItoVkPGsMwlmmnJiwA7sXRItBCivR4M5qnZtdw-7v4WuR4779ubDuJ5nalMv2S66-RPcnFAzWSKxtBDnFJJDGIUe7Tzizjg1nms0Xq_yPub_UOlWn0ec85FCft1hACpWG8schrOBeNqHBODFskYpUc2LC5JA2TaPF2dA67dg1TTsC_FupfQ2kNGcE1LgprxKHcVWYQb86B-HozjHZcqtauBzFNV5tbTuB-TpkcvJfNcFLlH3b8mb-H_ox35FjqBSAjLKyoeqfKTpVjvXhd09knwgJf6VKq6UC418_TOljMVfFTWXUxlnfhOOnzW6HSSzD1c9WrCuVzsUMv54szidQ9wf1cYWf3g5qFDxDQKis99gcDaiCAwM3yEBIzuNeeCa5dartHDb1xEB_HcHSeYbghbMjGfasvKn0aZRsnTyC0xhWBlsolZE', + 'e' => 'AQAB', + 'alg' => 'RSA-OAEP', + 'd' => 'n7fzJc3_WG59VEOBTkayzuSMM780OJQuZjN_KbH8lOZG25ZoA7T4Bxcc0xQn5oZE5uSCIwg91oCt0JvxPcpmqzaJZg1nirjcWZ-oBtVk7gCAWq-B3qhfF3izlbkosrzjHajIcY33HBhsy4_WerrXg4MDNE4HYojy68TcxT2LYQRxUOCf5TtJXvM8olexlSGtVnQnDRutxEUCwiewfmmrfveEogLx9EA-KMgAjTiISXxqIXQhWUQX1G7v_mV_Hr2YuImYcNcHkRvp9E7ook0876DhkO8v4UOZLwA1OlUX98mkoqwc58A_Y2lBYbVx1_s5lpPsEqbbH-nqIjh1fL0gdNfihLxnclWtW7pCztLnImZAyeCWAG7ZIfv-Rn9fLIv9jZ6r7r-MSH9sqbuziHN2grGjD_jfRluMHa0l84fFKl6bcqN1JWxPVhzNZo01yDF-1LiQnqUYSepPf6X3a2SOdkqBRiquE6EvLuSYIDpJq3jDIsgoL8Mo1LoomgiJxUwL_GWEOGu28gplyzm-9Q0U0nyhEf1uhSR8aJAQWAiFImWH5W_IQT9I7-yrindr_2fWQ_i1UgMsGzA7aOGzZfPljRy6z-tY_KuBG00-28S_aWvjyUc-Alp8AUyKjBZ-7CWH32fGWK48j1t-zomrwjL_mnhsPbGs0c9WsWgRzI-K8gE', + 'p' => '7_2v3OQZzlPFcHyYfLABQ3XP85Es4hCdwCkbDeltaUXgVy9l9etKghvM4hRkOvbb01kYVuLFmxIkCDtpi-zLCYAdXKrAK3PtSbtzld_XZ9nlsYa_QZWpXB_IrtFjVfdKUdMz94pHUhFGFj7nr6NNxfpiHSHWFE1zD_AC3mY46J961Y2LRnreVwAGNw53p07Db8yD_92pDa97vqcZOdgtybH9q6uma-RFNhO1AoiJhYZj69hjmMRXx-x56HO9cnXNbmzNSCFCKnQmn4GQLmRj9sfbZRqL94bbtE4_e0Zrpo8RNo8vxRLqQNwIy85fc6BRgBJomt8QdQvIgPgWCv5HoQ', + 'q' => 'zqOHk1P6WN_rHuM7ZF1cXH0x6RuOHq67WuHiSknqQeefGBA9PWs6ZyKQCO-O6mKXtcgE8_Q_hA2kMRcKOcvHil1hqMCNSXlflM7WPRPZu2qCDcqssd_uMbP-DqYthH_EzwL9KnYoH7JQFxxmcv5An8oXUtTwk4knKjkIYGRuUwfQTus0w1NfjFAyxOOiAQ37ussIcE6C6ZSsM3n41UlbJ7TCqewzVJaPJN5cxjySPZPD3Vp01a9YgAD6a3IIaKJdIxJS1ImnfPevSJQBE79-EXe2kSwVgOzvt-gsmM29QQ8veHy4uAqca5dZzMs7hkkHtw1z0jHV90epQJJlXXnH8Q', + 'dp' => '19oDkBh1AXelMIxQFm2zZTqUhAzCIr4xNIGEPNoDt1jK83_FJA-xnx5kA7-1erdHdms_Ef67HsONNv5A60JaR7w8LHnDiBGnjdaUmmuO8XAxQJ_ia5mxjxNjS6E2yD44USo2JmHvzeeNczq25elqbTPLhUpGo1IZuG72FZQ5gTjXoTXC2-xtCDEUZfaUNh4IeAipfLugbpe0JAFlFfrTDAMUFpC3iXjxqzbEanflwPvj6V9iDSgjj8SozSM0dLtxvu0LIeIQAeEgT_yXcrKGmpKdSO08kLBx8VUjkbv_3Pn20Gyu2YEuwpFlM_H1NikuxJNKFGmnAq9LcnwwT0jvoQ', + 'dq' => 'S6p59KrlmzGzaQYQM3o0XfHCGvfqHLYjCO557HYQf72O9kLMCfd_1VBEqeD-1jjwELKDjck8kOBl5UvohK1oDfSP1DleAy-cnmL29DqWmhgwM1ip0CCNmkmsmDSlqkUXDi6sAaZuntyukyflI-qSQ3C_BafPyFaKrt1fgdyEwYa08pESKwwWisy7KnmoUvaJ3SaHmohFS78TJ25cfc10wZ9hQNOrIChZlkiOdFCtxDqdmCqNacnhgE3bZQjGp3n83ODSz9zwJcSUvODlXBPc2AycH6Ci5yjbxt4Ppox_5pjm6xnQkiPgj01GpsUssMmBN7iHVsrE7N2iznBNCeOUIQ', + 'qi' => 'FZhClBMywVVjnuUud-05qd5CYU0dK79akAgy9oX6RX6I3IIIPckCciRrokxglZn-omAY5CnCe4KdrnjFOT5YUZE7G_Pg44XgCXaarLQf4hl80oPEf6-jJ5Iy6wPRx7G2e8qLxnh9cOdf-kRqgOS3F48Ucvw3ma5V6KGMwQqWFeV31XtZ8l5cVI-I3NzBS7qltpUVgz2Ju021eyc7IlqgzR98qKONl27DuEES0aK0WE97jnsyO27Yp88Wa2RiBrEocM89QZI1seJiGDizHRUP4UZxw9zsXww46wy0P6f9grnYp7t8LkyDDk8eoI4KX6SNMNVcyVS9IWjlq8EzqZEKIA', + ]); + + $protectedHeader = [ + 'alg' => 'RSA-OAEP', + 'kid' => 'samwise.gamgee@hobbiton.example', + 'enc' => 'A256GCM', + ]; + + $expected_compact_json = 'eyJhbGciOiJSU0EtT0FFUCIsImtpZCI6InNhbXdpc2UuZ2FtZ2VlQGhvYmJpdG9uLmV4YW1wbGUiLCJlbmMiOiJBMjU2R0NNIn0.rT99rwrBTbTI7IJM8fU3Eli7226HEB7IchCxNuh7lCiud48LxeolRdtFF4nzQibeYOl5S_PJsAXZwSXtDePz9hk-BbtsTBqC2UsPOdwjC9NhNupNNu9uHIVftDyucvI6hvALeZ6OGnhNV4v1zx2k7O1D89mAzfw-_kT3tkuorpDU-CpBENfIHX1Q58-Aad3FzMuo3Fn9buEP2yXakLXYa15BUXQsupM4A1GD4_H4Bd7V3u9h8Gkg8BpxKdUV9ScfJQTcYm6eJEBz3aSwIaK4T3-dwWpuBOhROQXBosJzS1asnuHtVMt2pKIIfux5BC6huIvmY7kzV7W7aIUrpYm_3H4zYvyMeq5pGqFmW2k8zpO878TRlZx7pZfPYDSXZyS0CfKKkMozT_qiCwZTSz4duYnt8hS4Z9sGthXn9uDqd6wycMagnQfOTs_lycTWmY-aqWVDKhjYNRf03NiwRtb5BE-tOdFwCASQj3uuAgPGrO2AWBe38UjQb0lvXn1SpyvYZ3WFc7WOJYaTa7A8DRn6MC6T-xDmMuxC0G7S2rscw5lQQU06MvZTlFOt0UvfuKBa03cxA_nIBIhLMjY2kOTxQMmpDPTr6Cbo8aKaOnx6ASE5Jx9paBpnNmOOKH35j_QlrQhDWUN6A2Gg8iFayJ69xDEdHAVCGRzN3woEI2ozDRs.-nBoKLH0YkLZPSI9.o4k2cnGN8rSSw3IDo1YuySkqeS_t2m1GXklSgqBdpACm6UJuJowOHC5ytjqYgRL-I-soPlwqMUf4UgRWWeaOGNw6vGW-xyM01lTYxrXfVzIIaRdhYtEMRBvBWbEwP7ua1DRfvaOjgZv6Ifa3brcAM64d8p5lhhNcizPersuhw5f-pGYzseva-TUaL8iWnctc-sSwy7SQmRkfhDjwbz0fz6kFovEgj64X1I5s7E6GLp5fnbYGLa1QUiML7Cc2GxgvI7zqWo0YIEc7aCflLG1-8BboVWFdZKLK9vNoycrYHumwzKluLWEbSVmaPpOslY2n525DxDfWaVFUfKQxMF56vn4B9QMpWAbnypNimbM8zVOw.UCGiqJxhBI3IFVdPalHHvA'; + $expected_flattened_json = '{"protected":"eyJhbGciOiJSU0EtT0FFUCIsImtpZCI6InNhbXdpc2UuZ2FtZ2VlQGhvYmJpdG9uLmV4YW1wbGUiLCJlbmMiOiJBMjU2R0NNIn0","encrypted_key":"rT99rwrBTbTI7IJM8fU3Eli7226HEB7IchCxNuh7lCiud48LxeolRdtFF4nzQibeYOl5S_PJsAXZwSXtDePz9hk-BbtsTBqC2UsPOdwjC9NhNupNNu9uHIVftDyucvI6hvALeZ6OGnhNV4v1zx2k7O1D89mAzfw-_kT3tkuorpDU-CpBENfIHX1Q58-Aad3FzMuo3Fn9buEP2yXakLXYa15BUXQsupM4A1GD4_H4Bd7V3u9h8Gkg8BpxKdUV9ScfJQTcYm6eJEBz3aSwIaK4T3-dwWpuBOhROQXBosJzS1asnuHtVMt2pKIIfux5BC6huIvmY7kzV7W7aIUrpYm_3H4zYvyMeq5pGqFmW2k8zpO878TRlZx7pZfPYDSXZyS0CfKKkMozT_qiCwZTSz4duYnt8hS4Z9sGthXn9uDqd6wycMagnQfOTs_lycTWmY-aqWVDKhjYNRf03NiwRtb5BE-tOdFwCASQj3uuAgPGrO2AWBe38UjQb0lvXn1SpyvYZ3WFc7WOJYaTa7A8DRn6MC6T-xDmMuxC0G7S2rscw5lQQU06MvZTlFOt0UvfuKBa03cxA_nIBIhLMjY2kOTxQMmpDPTr6Cbo8aKaOnx6ASE5Jx9paBpnNmOOKH35j_QlrQhDWUN6A2Gg8iFayJ69xDEdHAVCGRzN3woEI2ozDRs","iv":"-nBoKLH0YkLZPSI9","ciphertext":"o4k2cnGN8rSSw3IDo1YuySkqeS_t2m1GXklSgqBdpACm6UJuJowOHC5ytjqYgRL-I-soPlwqMUf4UgRWWeaOGNw6vGW-xyM01lTYxrXfVzIIaRdhYtEMRBvBWbEwP7ua1DRfvaOjgZv6Ifa3brcAM64d8p5lhhNcizPersuhw5f-pGYzseva-TUaL8iWnctc-sSwy7SQmRkfhDjwbz0fz6kFovEgj64X1I5s7E6GLp5fnbYGLa1QUiML7Cc2GxgvI7zqWo0YIEc7aCflLG1-8BboVWFdZKLK9vNoycrYHumwzKluLWEbSVmaPpOslY2n525DxDfWaVFUfKQxMF56vn4B9QMpWAbnypNimbM8zVOw","tag":"UCGiqJxhBI3IFVdPalHHvA"}'; + $expected_json = '{"recipients": [{"encrypted_key":"rT99rwrBTbTI7IJM8fU3Eli7226HEB7IchCxNuh7lCiud48LxeolRdtFF4nzQibeYOl5S_PJsAXZwSXtDePz9hk-BbtsTBqC2UsPOdwjC9NhNupNNu9uHIVftDyucvI6hvALeZ6OGnhNV4v1zx2k7O1D89mAzfw-_kT3tkuorpDU-CpBENfIHX1Q58-Aad3FzMuo3Fn9buEP2yXakLXYa15BUXQsupM4A1GD4_H4Bd7V3u9h8Gkg8BpxKdUV9ScfJQTcYm6eJEBz3aSwIaK4T3-dwWpuBOhROQXBosJzS1asnuHtVMt2pKIIfux5BC6huIvmY7kzV7W7aIUrpYm_3H4zYvyMeq5pGqFmW2k8zpO878TRlZx7pZfPYDSXZyS0CfKKkMozT_qiCwZTSz4duYnt8hS4Z9sGthXn9uDqd6wycMagnQfOTs_lycTWmY-aqWVDKhjYNRf03NiwRtb5BE-tOdFwCASQj3uuAgPGrO2AWBe38UjQb0lvXn1SpyvYZ3WFc7WOJYaTa7A8DRn6MC6T-xDmMuxC0G7S2rscw5lQQU06MvZTlFOt0UvfuKBa03cxA_nIBIhLMjY2kOTxQMmpDPTr6Cbo8aKaOnx6ASE5Jx9paBpnNmOOKH35j_QlrQhDWUN6A2Gg8iFayJ69xDEdHAVCGRzN3woEI2ozDRs"}],"protected":"eyJhbGciOiJSU0EtT0FFUCIsImtpZCI6InNhbXdpc2UuZ2FtZ2VlQGhvYmJpdG9uLmV4YW1wbGUiLCJlbmMiOiJBMjU2R0NNIn0","iv":"-nBoKLH0YkLZPSI9","ciphertext":"o4k2cnGN8rSSw3IDo1YuySkqeS_t2m1GXklSgqBdpACm6UJuJowOHC5ytjqYgRL-I-soPlwqMUf4UgRWWeaOGNw6vGW-xyM01lTYxrXfVzIIaRdhYtEMRBvBWbEwP7ua1DRfvaOjgZv6Ifa3brcAM64d8p5lhhNcizPersuhw5f-pGYzseva-TUaL8iWnctc-sSwy7SQmRkfhDjwbz0fz6kFovEgj64X1I5s7E6GLp5fnbYGLa1QUiML7Cc2GxgvI7zqWo0YIEc7aCflLG1-8BboVWFdZKLK9vNoycrYHumwzKluLWEbSVmaPpOslY2n525DxDfWaVFUfKQxMF56vn4B9QMpWAbnypNimbM8zVOw","tag":"UCGiqJxhBI3IFVdPalHHvA"}'; + $expected_iv = '-nBoKLH0YkLZPSI9'; + $expected_encrypted_key = 'rT99rwrBTbTI7IJM8fU3Eli7226HEB7IchCxNuh7lCiud48LxeolRdtFF4nzQibeYOl5S_PJsAXZwSXtDePz9hk-BbtsTBqC2UsPOdwjC9NhNupNNu9uHIVftDyucvI6hvALeZ6OGnhNV4v1zx2k7O1D89mAzfw-_kT3tkuorpDU-CpBENfIHX1Q58-Aad3FzMuo3Fn9buEP2yXakLXYa15BUXQsupM4A1GD4_H4Bd7V3u9h8Gkg8BpxKdUV9ScfJQTcYm6eJEBz3aSwIaK4T3-dwWpuBOhROQXBosJzS1asnuHtVMt2pKIIfux5BC6huIvmY7kzV7W7aIUrpYm_3H4zYvyMeq5pGqFmW2k8zpO878TRlZx7pZfPYDSXZyS0CfKKkMozT_qiCwZTSz4duYnt8hS4Z9sGthXn9uDqd6wycMagnQfOTs_lycTWmY-aqWVDKhjYNRf03NiwRtb5BE-tOdFwCASQj3uuAgPGrO2AWBe38UjQb0lvXn1SpyvYZ3WFc7WOJYaTa7A8DRn6MC6T-xDmMuxC0G7S2rscw5lQQU06MvZTlFOt0UvfuKBa03cxA_nIBIhLMjY2kOTxQMmpDPTr6Cbo8aKaOnx6ASE5Jx9paBpnNmOOKH35j_QlrQhDWUN6A2Gg8iFayJ69xDEdHAVCGRzN3woEI2ozDRs'; + $expected_ciphertext = 'o4k2cnGN8rSSw3IDo1YuySkqeS_t2m1GXklSgqBdpACm6UJuJowOHC5ytjqYgRL-I-soPlwqMUf4UgRWWeaOGNw6vGW-xyM01lTYxrXfVzIIaRdhYtEMRBvBWbEwP7ua1DRfvaOjgZv6Ifa3brcAM64d8p5lhhNcizPersuhw5f-pGYzseva-TUaL8iWnctc-sSwy7SQmRkfhDjwbz0fz6kFovEgj64X1I5s7E6GLp5fnbYGLa1QUiML7Cc2GxgvI7zqWo0YIEc7aCflLG1-8BboVWFdZKLK9vNoycrYHumwzKluLWEbSVmaPpOslY2n525DxDfWaVFUfKQxMF56vn4B9QMpWAbnypNimbM8zVOw'; + $expected_tag = 'UCGiqJxhBI3IFVdPalHHvA'; + + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['RSA-OAEP'], ['A256GCM'], ['DEF']); + + $loaded_compact_json = $this->getJWESerializerManager()->unserialize($expected_compact_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_compact_json, $private_key, 0)); + + $loaded_flattened_json = $this->getJWESerializerManager()->unserialize($expected_flattened_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_flattened_json, $private_key, 0)); + + $loaded_json = $this->getJWESerializerManager()->unserialize($expected_json); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $private_key, 0)); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_compact_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_compact_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_compact_json->getIV())); + static::assertEquals($expected_encrypted_key, Base64Url::encode($loaded_compact_json->getRecipient(0)->getEncryptedKey())); + static::assertEquals($expected_tag, Base64Url::encode($loaded_compact_json->getTag())); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_flattened_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_flattened_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_flattened_json->getIV())); + static::assertEquals($expected_encrypted_key, Base64Url::encode($loaded_flattened_json->getRecipient(0)->getEncryptedKey())); + static::assertEquals($expected_tag, Base64Url::encode($loaded_flattened_json->getTag())); + + static::assertEquals($expected_ciphertext, Base64Url::encode($loaded_json->getCiphertext())); + static::assertEquals($protectedHeader, $loaded_json->getSharedProtectedHeader()); + static::assertEquals($expected_iv, Base64Url::encode($loaded_json->getIV())); + static::assertEquals($expected_encrypted_key, Base64Url::encode($loaded_json->getRecipient(0)->getEncryptedKey())); + static::assertEquals($expected_tag, Base64Url::encode($loaded_json->getTag())); + + static::assertEquals($expected_payload, $loaded_compact_json->getPayload()); + static::assertEquals($expected_payload, $loaded_flattened_json->getPayload()); + static::assertEquals($expected_payload, $loaded_json->getPayload()); + } + + /** + * Same input as before, but we perform the encryption first. + * + * @test + */ + public function rSAOAEPAndA256GCMEncryptionBis(): void + { + $expected_payload = "You can trust us to stick with you through thick and thin\xe2\x80\x93to the bitter end. And you can trust us to keep any secret of yours\xe2\x80\x93closer than you keep it yourself. But you cannot trust us to let you face trouble alone, and go off without a word. We are your friends, Frodo."; + + $private_key = new JWK([ + 'kty' => 'RSA', + 'kid' => 'samwise.gamgee@hobbiton.example', + 'use' => 'enc', + 'n' => 'wbdxI55VaanZXPY29Lg5hdmv2XhvqAhoxUkanfzf2-5zVUxa6prHRrI4pP1AhoqJRlZfYtWWd5mmHRG2pAHIlh0ySJ9wi0BioZBl1XP2e-C-FyXJGcTy0HdKQWlrfhTm42EW7Vv04r4gfao6uxjLGwfpGrZLarohiWCPnkNrg71S2CuNZSQBIPGjXfkmIy2tl_VWgGnL22GplyXj5YlBLdxXp3XeStsqo571utNfoUTU8E4qdzJ3U1DItoVkPGsMwlmmnJiwA7sXRItBCivR4M5qnZtdw-7v4WuR4779ubDuJ5nalMv2S66-RPcnFAzWSKxtBDnFJJDGIUe7Tzizjg1nms0Xq_yPub_UOlWn0ec85FCft1hACpWG8schrOBeNqHBODFskYpUc2LC5JA2TaPF2dA67dg1TTsC_FupfQ2kNGcE1LgprxKHcVWYQb86B-HozjHZcqtauBzFNV5tbTuB-TpkcvJfNcFLlH3b8mb-H_ox35FjqBSAjLKyoeqfKTpVjvXhd09knwgJf6VKq6UC418_TOljMVfFTWXUxlnfhOOnzW6HSSzD1c9WrCuVzsUMv54szidQ9wf1cYWf3g5qFDxDQKis99gcDaiCAwM3yEBIzuNeeCa5dartHDb1xEB_HcHSeYbghbMjGfasvKn0aZRsnTyC0xhWBlsolZE', + 'e' => 'AQAB', + 'alg' => 'RSA-OAEP', + 'd' => 'n7fzJc3_WG59VEOBTkayzuSMM780OJQuZjN_KbH8lOZG25ZoA7T4Bxcc0xQn5oZE5uSCIwg91oCt0JvxPcpmqzaJZg1nirjcWZ-oBtVk7gCAWq-B3qhfF3izlbkosrzjHajIcY33HBhsy4_WerrXg4MDNE4HYojy68TcxT2LYQRxUOCf5TtJXvM8olexlSGtVnQnDRutxEUCwiewfmmrfveEogLx9EA-KMgAjTiISXxqIXQhWUQX1G7v_mV_Hr2YuImYcNcHkRvp9E7ook0876DhkO8v4UOZLwA1OlUX98mkoqwc58A_Y2lBYbVx1_s5lpPsEqbbH-nqIjh1fL0gdNfihLxnclWtW7pCztLnImZAyeCWAG7ZIfv-Rn9fLIv9jZ6r7r-MSH9sqbuziHN2grGjD_jfRluMHa0l84fFKl6bcqN1JWxPVhzNZo01yDF-1LiQnqUYSepPf6X3a2SOdkqBRiquE6EvLuSYIDpJq3jDIsgoL8Mo1LoomgiJxUwL_GWEOGu28gplyzm-9Q0U0nyhEf1uhSR8aJAQWAiFImWH5W_IQT9I7-yrindr_2fWQ_i1UgMsGzA7aOGzZfPljRy6z-tY_KuBG00-28S_aWvjyUc-Alp8AUyKjBZ-7CWH32fGWK48j1t-zomrwjL_mnhsPbGs0c9WsWgRzI-K8gE', + 'p' => '7_2v3OQZzlPFcHyYfLABQ3XP85Es4hCdwCkbDeltaUXgVy9l9etKghvM4hRkOvbb01kYVuLFmxIkCDtpi-zLCYAdXKrAK3PtSbtzld_XZ9nlsYa_QZWpXB_IrtFjVfdKUdMz94pHUhFGFj7nr6NNxfpiHSHWFE1zD_AC3mY46J961Y2LRnreVwAGNw53p07Db8yD_92pDa97vqcZOdgtybH9q6uma-RFNhO1AoiJhYZj69hjmMRXx-x56HO9cnXNbmzNSCFCKnQmn4GQLmRj9sfbZRqL94bbtE4_e0Zrpo8RNo8vxRLqQNwIy85fc6BRgBJomt8QdQvIgPgWCv5HoQ', + 'q' => 'zqOHk1P6WN_rHuM7ZF1cXH0x6RuOHq67WuHiSknqQeefGBA9PWs6ZyKQCO-O6mKXtcgE8_Q_hA2kMRcKOcvHil1hqMCNSXlflM7WPRPZu2qCDcqssd_uMbP-DqYthH_EzwL9KnYoH7JQFxxmcv5An8oXUtTwk4knKjkIYGRuUwfQTus0w1NfjFAyxOOiAQ37ussIcE6C6ZSsM3n41UlbJ7TCqewzVJaPJN5cxjySPZPD3Vp01a9YgAD6a3IIaKJdIxJS1ImnfPevSJQBE79-EXe2kSwVgOzvt-gsmM29QQ8veHy4uAqca5dZzMs7hkkHtw1z0jHV90epQJJlXXnH8Q', + 'dp' => '19oDkBh1AXelMIxQFm2zZTqUhAzCIr4xNIGEPNoDt1jK83_FJA-xnx5kA7-1erdHdms_Ef67HsONNv5A60JaR7w8LHnDiBGnjdaUmmuO8XAxQJ_ia5mxjxNjS6E2yD44USo2JmHvzeeNczq25elqbTPLhUpGo1IZuG72FZQ5gTjXoTXC2-xtCDEUZfaUNh4IeAipfLugbpe0JAFlFfrTDAMUFpC3iXjxqzbEanflwPvj6V9iDSgjj8SozSM0dLtxvu0LIeIQAeEgT_yXcrKGmpKdSO08kLBx8VUjkbv_3Pn20Gyu2YEuwpFlM_H1NikuxJNKFGmnAq9LcnwwT0jvoQ', + 'dq' => 'S6p59KrlmzGzaQYQM3o0XfHCGvfqHLYjCO557HYQf72O9kLMCfd_1VBEqeD-1jjwELKDjck8kOBl5UvohK1oDfSP1DleAy-cnmL29DqWmhgwM1ip0CCNmkmsmDSlqkUXDi6sAaZuntyukyflI-qSQ3C_BafPyFaKrt1fgdyEwYa08pESKwwWisy7KnmoUvaJ3SaHmohFS78TJ25cfc10wZ9hQNOrIChZlkiOdFCtxDqdmCqNacnhgE3bZQjGp3n83ODSz9zwJcSUvODlXBPc2AycH6Ci5yjbxt4Ppox_5pjm6xnQkiPgj01GpsUssMmBN7iHVsrE7N2iznBNCeOUIQ', + 'qi' => 'FZhClBMywVVjnuUud-05qd5CYU0dK79akAgy9oX6RX6I3IIIPckCciRrokxglZn-omAY5CnCe4KdrnjFOT5YUZE7G_Pg44XgCXaarLQf4hl80oPEf6-jJ5Iy6wPRx7G2e8qLxnh9cOdf-kRqgOS3F48Ucvw3ma5V6KGMwQqWFeV31XtZ8l5cVI-I3NzBS7qltpUVgz2Ju021eyc7IlqgzR98qKONl27DuEES0aK0WE97jnsyO27Yp88Wa2RiBrEocM89QZI1seJiGDizHRUP4UZxw9zsXww46wy0P6f9grnYp7t8LkyDDk8eoI4KX6SNMNVcyVS9IWjlq8EzqZEKIA', + ]); + + $protectedHeader = [ + 'alg' => 'RSA-OAEP', + 'kid' => 'samwise.gamgee@hobbiton.example', + 'enc' => 'A256GCM', + ]; + + $jweBuilder = $this->getJWEBuilderFactory()->create(['RSA-OAEP'], ['A256GCM'], ['DEF']); + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['RSA-OAEP'], ['A256GCM'], ['DEF']); + + $jwe = $jweBuilder + ->create()->withPayload($expected_payload) + ->withSharedProtectedHeader($protectedHeader) + ->addRecipient($private_key) + ->build() + ; + + $loaded_compact_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_compact', $jwe, 0)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_compact_json, $private_key, 0)); + + $loaded_flattened_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_json_flattened', $jwe, 0)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_flattened_json, $private_key, 0)); + + $loaded_json = $this->getJWESerializerManager()->unserialize($this->getJWESerializerManager()->serialize('jwe_json_general', $jwe)); + static::assertTrue($jweDecrypter->decryptUsingKey($loaded_json, $private_key, 0)); + + static::assertEquals($protectedHeader, $loaded_compact_json->getSharedProtectedHeader()); + + static::assertEquals($protectedHeader, $loaded_flattened_json->getSharedProtectedHeader()); + + static::assertEquals($protectedHeader, $loaded_json->getSharedProtectedHeader()); + + static::assertEquals($expected_payload, $loaded_compact_json->getPayload()); + static::assertEquals($expected_payload, $loaded_flattened_json->getPayload()); + static::assertEquals($expected_payload, $loaded_json->getPayload()); + } +} diff --git a/vendor/web-token/jwt-encryption/Tests/RSAEncryptionTest.php b/vendor/web-token/jwt-encryption/Tests/RSAEncryptionTest.php new file mode 100644 index 000000000..a2c590e3d --- /dev/null +++ b/vendor/web-token/jwt-encryption/Tests/RSAEncryptionTest.php @@ -0,0 +1,152 @@ +getJWEDecrypterFactory()->create(['RSA1_5'], ['A128CBC-HS256'], ['DEF']); + + $loaded = $this->getJWESerializerManager()->unserialize('eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7PcHALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPhcCdZ6XDP0_F8rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPgwCp6X-nZZd9OHBv-B3oWh2TbqmScqXMR4gp_A.AxY8DCtDaGlsbGljb3RoZQ.KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY.9hH0vgRfYgPnAHOd8stkvw'); + + static::assertEquals('RSA1_5', $loaded->getSharedProtectedHeaderParameter('alg')); + static::assertEquals('A128CBC-HS256', $loaded->getSharedProtectedHeaderParameter('enc')); + static::assertNull($loaded->getPayload()); + static::assertTrue($jweDecrypter->decryptUsingKeySet($loaded, $this->getPrivateKeySet(), 0)); + static::assertEquals('Live long and prosper.', $loaded->getPayload()); + } + + /** + * @see https://tools.ietf.org/html/rfc7516#appendix-A.4 + * + * @test + */ + public function loadJWEJSONSerialization(): void + { + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['RSA1_5', 'A128KW'], ['A128CBC-HS256'], ['DEF']); + + $loaded = $this->getJWESerializerManager()->unserialize('{"protected":"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0","unprotected":{"jku":"https://server.example.com/keys.jwks"},"recipients":[{"header":{"alg":"RSA1_5","kid":"2011-04-29"},"encrypted_key":"UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7PcHALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPhcCdZ6XDP0_F8rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPgwCp6X-nZZd9OHBv-B3oWh2TbqmScqXMR4gp_A"},{"header":{"alg":"A128KW","kid":"7"},"encrypted_key":"6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ"}],"iv":"AxY8DCtDaGlsbGljb3RoZQ","ciphertext":"KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY","tag":"Mz-VPPyU4RlcuYv1IwIvzw"}'); + + static::assertEquals(2, $loaded->countRecipients()); + static::assertEquals('A128CBC-HS256', $loaded->getSharedProtectedHeaderParameter('enc')); + static::assertEquals('RSA1_5', $loaded->getRecipient(0)->getHeaderParameter('alg')); + static::assertEquals('A128KW', $loaded->getRecipient(1)->getHeaderParameter('alg')); + static::assertNull($loaded->getPayload()); + static::assertTrue($jweDecrypter->decryptUsingKeySet($loaded, $this->getPrivateKeySet(), 0)); + static::assertEquals('Live long and prosper.', $loaded->getPayload()); + } + + private function getPrivateKeySet(): JWKSet + { + $keys = ['keys' => [ + [ + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => 'weNJy2HscCSM6AEDTDg04biOvhFhyyWvOHQfeF_PxMQ', + 'y' => 'e8lnCO-AlStT-NJVX-crhB7QRYhiix03illJOVAOyck', + 'd' => 'VEmDZpDXXK8p8N0Cndsxs924q6nS1RXFASRl6BfUqdw', + ], + [ + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => 'gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0', + 'y' => 'SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps', + 'd' => '0_NxaRPUMQoAJt50Gz8YiTr8gRTwyEaCumd-MToTmIo', + ], + [ + 'kid' => '2010-12-29', + 'kty' => 'RSA', + 'n' => 'ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ', + 'e' => 'AQAB', + 'd' => 'Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97IjlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYTCBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLhBOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ', + ], + [ + 'kid' => 'e9bc097a-ce51-4036-9562-d2ade882db0d', + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => 'f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU', + 'y' => 'x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0', + 'd' => 'jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI', + ], + [ + 'kid' => '123456789', + 'kty' => 'RSA', + 'n' => 'tpS1ZmfVKVP5KofIhMBP0tSWc4qlh6fm2lrZSkuKxUjEaWjzZSzs72gEIGxraWusMdoRuV54xsWRyf5KeZT0S-I5Prle3Idi3gICiO4NwvMk6JwSBcJWwmSLFEKyUSnB2CtfiGc0_5rQCpcEt_Dn5iM-BNn7fqpoLIbks8rXKUIj8-qMVqkTXsEKeKinE23t1ykMldsNaaOH-hvGti5Jt2DMnH1JjoXdDXfxvSP_0gjUYb0ektudYFXoA6wekmQyJeImvgx4Myz1I4iHtkY_Cp7J4Mn1ejZ6HNmyvoTE_4OuY1uCeYv4UyXFc1s1uUyYtj4z57qsHGsS4dQ3A2MJsw', + 'e' => 'AQAB', + 'p' => '5BGU1c7af_5sFyfsa-onIJgo5BZu8uHvz3Uyb8OA0a-G9UPO1ShLYjX0wUfhZcFB7fwPtgmmYAN6wKGVce9eMAbX4PliPk3r-BcpZuPKkuLk_wFvgWAQ5Hqw2iEuwXLV0_e8c2gaUt_hyMC5-nFc4v0Bmv6NT6Pfry-UrK3BKWc', + 'd' => 'Kp0KuZwCZGL1BLgsVM-N0edMNitl9wN5Hf2WOYDoIqOZNAEKzdJuenIMhITJjRFUX05GVL138uyp2js_pqDdY9ipA7rAKThwGuDdNphZHech9ih3DGEPXs-YpmHqvIbCd3GoGm38MKwxYkddEpFnjo8rKna1_BpJthrFxjDRhw9DxJBycOdH2yWTyp62ZENPvneK40H2a57W4QScTgfecZqD59m2fGUaWaX5uUmIxaEmtGoJnd9RE4oywKhgN7_TK7wXRlqA4UoRPiH2ACrdU-_cLQL9Jc0u0GqZJK31LDbOeN95QgtSCc72k3Vtzy3CrVpp5TAA67s1Gj9Skn-CAQ', + 'q' => 'zPD-B-nrngwF-O99BHvb47XGKR7ON8JCI6JxavzIkusMXCB8rMyYW8zLs68L8JLAzWZ34oMq0FPUnysBxc5nTF8Nb4BZxTZ5-9cHfoKrYTI3YWsmVW2FpCJFEjMs4NXZ28PBkS9b4zjfS2KhNdkmCeOYU0tJpNfwmOTI90qeUdU', + 'dp' => 'aJrzw_kjWK9uDlTeaES2e4muv6bWbopYfrPHVWG7NPGoGdhnBnd70-jhgMEiTZSNU8VXw2u7prAR3kZ-kAp1DdwlqedYOzFsOJcPA0UZhbORyrBy30kbll_7u6CanFm6X4VyJxCpejd7jKNw6cCTFP1sfhWg5NVJ5EUTkPwE66M', + 'dq' => 'Swz1-m_vmTFN_pu1bK7vF7S5nNVrL4A0OFiEsGliCmuJWzOKdL14DiYxctvnw3H6qT2dKZZfV2tbse5N9-JecdldUjfuqAoLIe7dD7dKi42YOlTC9QXmqvTh1ohnJu8pmRFXEZQGUm_BVhoIb2_WPkjav6YSkguCUHt4HRd2YwE', + 'qi' => 'BocuCOEOq-oyLDALwzMXU8gOf3IL1Q1_BWwsdoANoh6i179psxgE4JXToWcpXZQQqub8ngwE6uR9fpd3m6N_PL4T55vbDDyjPKmrL2ttC2gOtx9KrpPh-Z7LQRo4BE48nHJJrystKHfFlaH2G7JxHNgMBYVADyttN09qEoav8Os', + ], + [ + 'kty' => 'RSA', + 'n' => 'oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUWcJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3Spsk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2asbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMStPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2djYgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw', + 'e' => 'AQAB', + 'd' => 'kLdtIj6GbDks_ApCSTYQtelcNttlKiOyPzMrXHeI-yk1F7-kpDxY4-WY5NWV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD93Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghkqDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vlt3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSndVTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ', + 'p' => '1r52Xk46c-LsfB5P442p7atdPUrxQSy4mti_tZI3Mgf2EuFVbUoDBvaRQ-SWxkbkmoEzL7JXroSBjSrK3YIQgYdMgyAEPTPjXv_hI2_1eTSPVZfzL0lffNn03IXqWF5MDFuoUYE0hzb2vhrlN_rKrbfDIwUbTrjjgieRbwC6Cl0', + 'q' => 'wLb35x7hmQWZsWJmB_vle87ihgZ19S8lBEROLIsZG4ayZVe9Hi9gDVCOBmUDdaDYVTSNx_8Fyw1YYa9XGrGnDew00J28cRUoeBB_jKI1oma0Orv1T9aXIWxKwd4gvxFImOWr3QRL9KEBRzk2RatUBnmDZJTIAfwTs0g68UZHvtc', + 'dp' => 'ZK-YwE7diUh0qR1tR7w8WHtolDx3MZ_OTowiFvgfeQ3SiresXjm9gZ5KLhMXvo-uz-KUJWDxS5pFQ_M0evdo1dKiRTjVw_x4NyqyXPM5nULPkcpU827rnpZzAJKpdhWAgqrXGKAECQH0Xt4taznjnd_zVpAmZZq60WPMBMfKcuE', + 'dq' => 'Dq0gfgJ1DdFGXiLvQEZnuKEN0UUmsJBxkjydc3j4ZYdBiMRAy86x0vHCjywcMlYYg4yoC4YZa9hNVcsjqA3FeiL19rk8g6Qn29Tt0cj8qqyFpz9vNDBUfCAiJVeESOjJDZPYHdHY8v1b-o-Z2X5tvLx-TCekf7oxyeKDUqKWjis', + 'qi' => 'VIMpMYbPf47dT1w_zDUXfPimsSegnMOA1zTaX7aGk_8urY6R8-ZW1FxU7AlWAyLWybqq6t16VFd7hQd0y6flUK4SlOydB61gwanOsXGOAOv82cHq0E3eL4HrtZkUuKvnPrMnsUUFlfUdybVzxyjz9JF_XyaY14ardLSjf4L_FNY', + ], + [ + 'kty' => 'RSA', + 'n' => 'sXchDaQebHnPiGvyDOAT4saGEUetSyo9MKLOoWFsueri23bOdgWp4Dy1WlUzewbgBHod5pcM9H95GQRV3JDXboIRROSBigeC5yjU1hGzHHyXss8UDprecbAYxknTcQkhslANGRUZmdTOQ5qTRsLAt6BTYuyvVRdhS8exSZEy_c4gs_7svlJJQ4H9_NxsiIoLwAEk7-Q3UXERGYw_75IDrGA84-lA_-Ct4eTlXHBIY2EaV7t7LjJaynVJCpkv4LKjTTAumiGUIuQhrNhZLuF_RJLqHpM2kgWFLU7-VTdL1VbC2tejvcI2BlMkEpk1BzBZI0KQB0GaDWFLN-aEAw3vRw', + 'e' => 'AQAB', + 'd' => 'VFCWOqXr8nvZNyaaJLXdnNPXZKRaWCjkU5Q2egQQpTBMwhprMzWzpR8Sxq1OPThh_J6MUD8Z35wky9b8eEO0pwNS8xlh1lOFRRBoNqDIKVOku0aZb-rynq8cxjDTLZQ6Fz7jSjR1Klop-YKaUHc9GsEofQqYruPhzSA-QgajZGPbE_0ZaVDJHfyd7UUBUKunFMScbflYAAOYJqVIVwaYR5zWEEceUjNnTNo_CVSj-VvXLO5VZfCUAVLgW4dpf1SrtZjSt34YLsRarSb127reG_DUwg9Ch-KyvjT1SkHgUWRVGcyly7uvVGRSDwsXypdrNinPA4jlhoNdizK2zF2CWQ', + 'p' => '9gY2w6I6S6L0juEKsbeDAwpd9WMfgqFoeA9vEyEUuk4kLwBKcoe1x4HG68ik918hdDSE9vDQSccA3xXHOAFOPJ8R9EeIAbTi1VwBYnbTp87X-xcPWlEPkrdoUKW60tgs1aNd_Nnc9LEVVPMS390zbFxt8TN_biaBgelNgbC95sM', + 'q' => 'uKlCKvKv_ZJMVcdIs5vVSU_6cPtYI1ljWytExV_skstvRSNi9r66jdd9-yBhVfuG4shsp2j7rGnIio901RBeHo6TPKWVVykPu1iYhQXw1jIABfw-MVsN-3bQ76WLdt2SDxsHs7q7zPyUyHXmps7ycZ5c72wGkUwNOjYelmkiNS0', + 'dp' => 'w0kZbV63cVRvVX6yk3C8cMxo2qCM4Y8nsq1lmMSYhG4EcL6FWbX5h9yuvngs4iLEFk6eALoUS4vIWEwcL4txw9LsWH_zKI-hwoReoP77cOdSL4AVcraHawlkpyd2TWjE5evgbhWtOxnZee3cXJBkAi64Ik6jZxbvk-RR3pEhnCs', + 'dq' => 'o_8V14SezckO6CNLKs_btPdFiO9_kC1DsuUTd2LAfIIVeMZ7jn1Gus_Ff7B7IVx3p5KuBGOVF8L-qifLb6nQnLysgHDh132NDioZkhH7mI7hPG-PYE_odApKdnqECHWw0J-F0JWnUd6D2B_1TvF9mXA2Qx-iGYn8OVV1Bsmp6qU', + 'qi' => 'eNho5yRBEBxhGBtQRww9QirZsB66TrfFReG_CcteI1aCneT0ELGhYlRlCtUkTRclIfuEPmNsNDPbLoLqqCVznFbvdB7x-Tl-m0l_eFTj2KiqwGqE9PZB9nNTwMVvH3VRRSLWACvPnSiwP8N5Usy-WRXS-V7TbpxIhvepTfE0NNo', + ], + [ + 'kty' => 'RSA', + 'n' => 'ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ', + 'e' => 'AQAB', + 'd' => 'Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97IjlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYTCBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLhBOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ', + 'p' => '4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPGBY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc', + 'q' => 'uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc', + 'dp' => 'BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3QCLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0', + 'dq' => 'h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-kyNlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU', + 'qi' => 'IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2oy26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLUW0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U', + ], + [ + 'kty' => 'EC', + 'crv' => 'P-521', + 'x' => 'AekpBQ8ST8a8VcfVOTNl353vSrDCLLJXmPk06wTjxrrjcBpXp5EOnYG_NjFZ6OvLFV1jSfS9tsz4qUxcWceqwQGk', + 'y' => 'ADSmRA43Z1DSNx_RvcLI87cdL07l6jQyyBXMoxVg_l2Th-x3S1WDhjDly79ajL4Kkd0AZMaZmh9ubmf63e3kyMj2', + 'd' => 'AY5pb7A0UFiB3RELSD64fTLOSV_jazdF7fLYyuTw8lOfRhWg6Y6rUrPAxerEzgdRhajnu0ferB0d53vM9mE15j2C', + ], + ]]; + + return JWKSet::createFromKeyData($keys); + } +} diff --git a/vendor/web-token/jwt-encryption/Tests/RSAKeyEncryptionTest.php b/vendor/web-token/jwt-encryption/Tests/RSAKeyEncryptionTest.php new file mode 100644 index 000000000..61da86a08 --- /dev/null +++ b/vendor/web-token/jwt-encryption/Tests/RSAKeyEncryptionTest.php @@ -0,0 +1,324 @@ +expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Wrong key type.'); + + $key = new JWK([ + 'kty' => 'EC', + ]); + + $rsa1_5 = new RSA15(); + + $header = []; + $data = 'Live long and Prosper.'; + + $additionalHeader = []; + $rsa1_5->encryptKey($key, $data, $header, $additionalHeader); + } + + /** + * @see https://tools.ietf.org/html/rfc7516#appendix-A.2 + * + * @test + */ + public function rSA15EncryptionAndDecryption(): void + { + $header = []; + $jwk = new JWK([ + 'kty' => 'RSA', + 'n' => 'sXchDaQebHnPiGvyDOAT4saGEUetSyo9MKLOoWFsueri23bOdgWp4Dy1WlUzewbgBHod5pcM9H95GQRV3JDXboIRROSBigeC5yjU1hGzHHyXss8UDprecbAYxknTcQkhslANGRUZmdTOQ5qTRsLAt6BTYuyvVRdhS8exSZEy_c4gs_7svlJJQ4H9_NxsiIoLwAEk7-Q3UXERGYw_75IDrGA84-lA_-Ct4eTlXHBIY2EaV7t7LjJaynVJCpkv4LKjTTAumiGUIuQhrNhZLuF_RJLqHpM2kgWFLU7-VTdL1VbC2tejvcI2BlMkEpk1BzBZI0KQB0GaDWFLN-aEAw3vRw', + 'e' => 'AQAB', + 'd' => 'VFCWOqXr8nvZNyaaJLXdnNPXZKRaWCjkU5Q2egQQpTBMwhprMzWzpR8Sxq1OPThh_J6MUD8Z35wky9b8eEO0pwNS8xlh1lOFRRBoNqDIKVOku0aZb-rynq8cxjDTLZQ6Fz7jSjR1Klop-YKaUHc9GsEofQqYruPhzSA-QgajZGPbE_0ZaVDJHfyd7UUBUKunFMScbflYAAOYJqVIVwaYR5zWEEceUjNnTNo_CVSj-VvXLO5VZfCUAVLgW4dpf1SrtZjSt34YLsRarSb127reG_DUwg9Ch-KyvjT1SkHgUWRVGcyly7uvVGRSDwsXypdrNinPA4jlhoNdizK2zF2CWQ', + 'p' => '9gY2w6I6S6L0juEKsbeDAwpd9WMfgqFoeA9vEyEUuk4kLwBKcoe1x4HG68ik918hdDSE9vDQSccA3xXHOAFOPJ8R9EeIAbTi1VwBYnbTp87X-xcPWlEPkrdoUKW60tgs1aNd_Nnc9LEVVPMS390zbFxt8TN_biaBgelNgbC95sM', + 'q' => 'uKlCKvKv_ZJMVcdIs5vVSU_6cPtYI1ljWytExV_skstvRSNi9r66jdd9-yBhVfuG4shsp2j7rGnIio901RBeHo6TPKWVVykPu1iYhQXw1jIABfw-MVsN-3bQ76WLdt2SDxsHs7q7zPyUyHXmps7ycZ5c72wGkUwNOjYelmkiNS0', + 'dp' => 'w0kZbV63cVRvVX6yk3C8cMxo2qCM4Y8nsq1lmMSYhG4EcL6FWbX5h9yuvngs4iLEFk6eALoUS4vIWEwcL4txw9LsWH_zKI-hwoReoP77cOdSL4AVcraHawlkpyd2TWjE5evgbhWtOxnZee3cXJBkAi64Ik6jZxbvk-RR3pEhnCs', + 'dq' => 'o_8V14SezckO6CNLKs_btPdFiO9_kC1DsuUTd2LAfIIVeMZ7jn1Gus_Ff7B7IVx3p5KuBGOVF8L-qifLb6nQnLysgHDh132NDioZkhH7mI7hPG-PYE_odApKdnqECHWw0J-F0JWnUd6D2B_1TvF9mXA2Qx-iGYn8OVV1Bsmp6qU', + 'qi' => 'eNho5yRBEBxhGBtQRww9QirZsB66TrfFReG_CcteI1aCneT0ELGhYlRlCtUkTRclIfuEPmNsNDPbLoLqqCVznFbvdB7x-Tl-m0l_eFTj2KiqwGqE9PZB9nNTwMVvH3VRRSLWACvPnSiwP8N5Usy-WRXS-V7TbpxIhvepTfE0NNo', + ]); + + $cek = [4, 211, 31, 197, 84, 157, 252, 254, 11, 100, 157, 250, 63, 170, 106, 206, 107, 124, 212, 45, 111, 107, 9, 219, 200, 177, 0, 240, 143, 156, 44, 207]; + foreach ($cek as $key => $value) { + $cek[$key] = str_pad(dechex($value), 2, '0', STR_PAD_LEFT); + } + $cek = hex2bin(implode('', $cek)); + + $from_specification = Base64Url::decode('UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7PcHALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPhcCdZ6XDP0_F8rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPgwCp6X-nZZd9OHBv-B3oWh2TbqmScqXMR4gp_A'); + + $rsa1_5 = new RSA15(); + $additionalHeader = []; + $encrypted = $rsa1_5->encryptKey($jwk, $cek, $header, $additionalHeader); + + static::assertEquals($cek, $rsa1_5->decryptKey($jwk, $encrypted, $header)); + static::assertEquals($cek, $rsa1_5->decryptKey($jwk, $from_specification, $header)); + } + + /** + * @see https://tools.ietf.org/html/rfc7516#appendix-A.1 + * + * @test + */ + public function rSAOAEPEncryptionAndDecryption(): void + { + $header = []; + $jwk = new JWK([ + 'kty' => 'RSA', + 'n' => 'oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUWcJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3Spsk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2asbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMStPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2djYgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw', + 'e' => 'AQAB', + 'd' => 'kLdtIj6GbDks_ApCSTYQtelcNttlKiOyPzMrXHeI-yk1F7-kpDxY4-WY5NWV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD93Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghkqDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vlt3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSndVTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ', + 'p' => '1r52Xk46c-LsfB5P442p7atdPUrxQSy4mti_tZI3Mgf2EuFVbUoDBvaRQ-SWxkbkmoEzL7JXroSBjSrK3YIQgYdMgyAEPTPjXv_hI2_1eTSPVZfzL0lffNn03IXqWF5MDFuoUYE0hzb2vhrlN_rKrbfDIwUbTrjjgieRbwC6Cl0', + 'q' => 'wLb35x7hmQWZsWJmB_vle87ihgZ19S8lBEROLIsZG4ayZVe9Hi9gDVCOBmUDdaDYVTSNx_8Fyw1YYa9XGrGnDew00J28cRUoeBB_jKI1oma0Orv1T9aXIWxKwd4gvxFImOWr3QRL9KEBRzk2RatUBnmDZJTIAfwTs0g68UZHvtc', + 'dp' => 'ZK-YwE7diUh0qR1tR7w8WHtolDx3MZ_OTowiFvgfeQ3SiresXjm9gZ5KLhMXvo-uz-KUJWDxS5pFQ_M0evdo1dKiRTjVw_x4NyqyXPM5nULPkcpU827rnpZzAJKpdhWAgqrXGKAECQH0Xt4taznjnd_zVpAmZZq60WPMBMfKcuE', + 'dq' => 'Dq0gfgJ1DdFGXiLvQEZnuKEN0UUmsJBxkjydc3j4ZYdBiMRAy86x0vHCjywcMlYYg4yoC4YZa9hNVcsjqA3FeiL19rk8g6Qn29Tt0cj8qqyFpz9vNDBUfCAiJVeESOjJDZPYHdHY8v1b-o-Z2X5tvLx-TCekf7oxyeKDUqKWjis', + 'qi' => 'VIMpMYbPf47dT1w_zDUXfPimsSegnMOA1zTaX7aGk_8urY6R8-ZW1FxU7AlWAyLWybqq6t16VFd7hQd0y6flUK4SlOydB61gwanOsXGOAOv82cHq0E3eL4HrtZkUuKvnPrMnsUUFlfUdybVzxyjz9JF_XyaY14ardLSjf4L_FNY', + ]); + + $cek = [177, 161, 244, 128, 84, 143, 225, 115, 63, 180, 3, 255, 107, 154, 212, 246, 138, 7, 110, 91, 112, 46, 34, 105, 47, 130, 203, 46, 122, 234, 64, 252]; + foreach ($cek as $key => $value) { + $cek[$key] = str_pad(dechex($value), 2, '0', STR_PAD_LEFT); + } + $cek = hex2bin(implode('', $cek)); + + $from_specification = Base64Url::decode('OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg'); + + $rsa_oaep = new RSAOAEP(); + $additionalHeader = []; + $encrypted = $rsa_oaep->encryptKey($jwk, $cek, $header, $additionalHeader); + + static::assertEquals($cek, $rsa_oaep->decryptKey($jwk, $encrypted, $header)); + static::assertEquals($cek, $rsa_oaep->decryptKey($jwk, $from_specification, $header)); + } + + /** + * @see https://tools.ietf.org/html/rfc7516#appendix-A.1 + * + * @test + */ + public function rSAOAEP256EncryptionAndDecryption(): void + { + $header = []; + $jwk = new JWK([ + 'kty' => 'RSA', + 'n' => 'oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUWcJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3Spsk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2asbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMStPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2djYgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw', + 'e' => 'AQAB', + 'd' => 'kLdtIj6GbDks_ApCSTYQtelcNttlKiOyPzMrXHeI-yk1F7-kpDxY4-WY5NWV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD93Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghkqDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vlt3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSndVTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ', + 'p' => '1r52Xk46c-LsfB5P442p7atdPUrxQSy4mti_tZI3Mgf2EuFVbUoDBvaRQ-SWxkbkmoEzL7JXroSBjSrK3YIQgYdMgyAEPTPjXv_hI2_1eTSPVZfzL0lffNn03IXqWF5MDFuoUYE0hzb2vhrlN_rKrbfDIwUbTrjjgieRbwC6Cl0', + 'q' => 'wLb35x7hmQWZsWJmB_vle87ihgZ19S8lBEROLIsZG4ayZVe9Hi9gDVCOBmUDdaDYVTSNx_8Fyw1YYa9XGrGnDew00J28cRUoeBB_jKI1oma0Orv1T9aXIWxKwd4gvxFImOWr3QRL9KEBRzk2RatUBnmDZJTIAfwTs0g68UZHvtc', + 'dp' => 'ZK-YwE7diUh0qR1tR7w8WHtolDx3MZ_OTowiFvgfeQ3SiresXjm9gZ5KLhMXvo-uz-KUJWDxS5pFQ_M0evdo1dKiRTjVw_x4NyqyXPM5nULPkcpU827rnpZzAJKpdhWAgqrXGKAECQH0Xt4taznjnd_zVpAmZZq60WPMBMfKcuE', + 'dq' => 'Dq0gfgJ1DdFGXiLvQEZnuKEN0UUmsJBxkjydc3j4ZYdBiMRAy86x0vHCjywcMlYYg4yoC4YZa9hNVcsjqA3FeiL19rk8g6Qn29Tt0cj8qqyFpz9vNDBUfCAiJVeESOjJDZPYHdHY8v1b-o-Z2X5tvLx-TCekf7oxyeKDUqKWjis', + 'qi' => 'VIMpMYbPf47dT1w_zDUXfPimsSegnMOA1zTaX7aGk_8urY6R8-ZW1FxU7AlWAyLWybqq6t16VFd7hQd0y6flUK4SlOydB61gwanOsXGOAOv82cHq0E3eL4HrtZkUuKvnPrMnsUUFlfUdybVzxyjz9JF_XyaY14ardLSjf4L_FNY', + ]); + + $cek = [177, 161, 244, 128, 84, 143, 225, 115, 63, 180, 3, 255, 107, 154, 212, 246, 138, 7, 110, 91, 112, 46, 34, 105, 47, 130, 203, 46, 122, 234, 64, 252]; + foreach ($cek as $key => $value) { + $cek[$key] = str_pad(dechex($value), 2, '0', STR_PAD_LEFT); + } + $cek = hex2bin(implode('', $cek)); + + $rsa_oaep_256 = new RSAOAEP256(); + $additionalHeader = []; + $encrypted = $rsa_oaep_256->encryptKey($jwk, $cek, $header, $additionalHeader); + + static::assertEquals($cek, $rsa_oaep_256->decryptKey($jwk, $encrypted, $header)); + } + + /** + * @see https://tools.ietf.org/html/rfc7516#appendix-A.1 + * + * @test + */ + public function loadJWK1(): void + { + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['RSA-OAEP'], ['A256GCM'], ['DEF']); + + $loaded = $this->getJWESerializerManager()->unserialize('eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg.48V1_ALb6US04U3b.5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A.XFBoMYUZodetZdvTiFvSkQ'); + + static::assertEquals('RSA-OAEP', $loaded->getSharedProtectedHeaderParameter('alg')); + static::assertEquals('A256GCM', $loaded->getSharedProtectedHeaderParameter('enc')); + static::assertNull($loaded->getPayload()); + static::assertTrue($jweDecrypter->decryptUsingKeySet($loaded, $this->getPrivateKeySet(), 0)); + static::assertEquals('The true sign of intelligence is not knowledge but imagination.', $loaded->getPayload()); + } + + /** + * @see https://tools.ietf.org/html/rfc7516#appendix-A.2 + * + * @test + */ + public function loadJWK2(): void + { + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['RSA1_5'], ['A128CBC-HS256'], ['DEF']); + + $loaded = $this->getJWESerializerManager()->unserialize('eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7PcHALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPhcCdZ6XDP0_F8rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPgwCp6X-nZZd9OHBv-B3oWh2TbqmScqXMR4gp_A.AxY8DCtDaGlsbGljb3RoZQ.KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY.9hH0vgRfYgPnAHOd8stkvw'); + + static::assertNull($loaded->getPayload()); + static::assertTrue($jweDecrypter->decryptUsingKeySet($loaded, $this->getPrivateKeySet(), 0)); + static::assertEquals('Live long and prosper.', $loaded->getPayload()); + } + + /** + * @see https://tools.ietf.org/html/rfc7516#appendix-A.3 + * + * @test + */ + public function loadJWK3(): void + { + $jweDecrypter = $this->getJWEDecrypterFactory()->create(['A128KW'], ['A128CBC-HS256'], ['DEF']); + + $loaded = $this->getJWESerializerManager()->unserialize('eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ.AxY8DCtDaGlsbGljb3RoZQ.KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY.U0m_YmjN04DJvceFICbCVQ'); + + static::assertEquals('A128KW', $loaded->getSharedProtectedHeaderParameter('alg')); + static::assertEquals('A128CBC-HS256', $loaded->getSharedProtectedHeaderParameter('enc')); + static::assertNull($loaded->getPayload()); + static::assertTrue($jweDecrypter->decryptUsingKeySet($loaded, $this->getSymmetricKeySet(), 0)); + static::assertEquals('Live long and prosper.', $loaded->getPayload()); + } + + private function getPrivateKeySet(): JWKSet + { + $keys = ['keys' => [ + [ + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => 'weNJy2HscCSM6AEDTDg04biOvhFhyyWvOHQfeF_PxMQ', + 'y' => 'e8lnCO-AlStT-NJVX-crhB7QRYhiix03illJOVAOyck', + 'd' => 'VEmDZpDXXK8p8N0Cndsxs924q6nS1RXFASRl6BfUqdw', + ], + [ + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => 'gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0', + 'y' => 'SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps', + 'd' => '0_NxaRPUMQoAJt50Gz8YiTr8gRTwyEaCumd-MToTmIo', + ], + [ + 'kid' => '2010-12-29', + 'kty' => 'RSA', + 'n' => 'ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ', + 'e' => 'AQAB', + 'd' => 'Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97IjlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYTCBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLhBOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ', + ], + [ + 'kid' => 'e9bc097a-ce51-4036-9562-d2ade882db0d', + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => 'f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU', + 'y' => 'x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0', + 'd' => 'jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI', + ], + [ + 'kid' => '123456789', + 'kty' => 'RSA', + 'n' => 'tpS1ZmfVKVP5KofIhMBP0tSWc4qlh6fm2lrZSkuKxUjEaWjzZSzs72gEIGxraWusMdoRuV54xsWRyf5KeZT0S-I5Prle3Idi3gICiO4NwvMk6JwSBcJWwmSLFEKyUSnB2CtfiGc0_5rQCpcEt_Dn5iM-BNn7fqpoLIbks8rXKUIj8-qMVqkTXsEKeKinE23t1ykMldsNaaOH-hvGti5Jt2DMnH1JjoXdDXfxvSP_0gjUYb0ektudYFXoA6wekmQyJeImvgx4Myz1I4iHtkY_Cp7J4Mn1ejZ6HNmyvoTE_4OuY1uCeYv4UyXFc1s1uUyYtj4z57qsHGsS4dQ3A2MJsw', + 'e' => 'AQAB', + 'p' => '5BGU1c7af_5sFyfsa-onIJgo5BZu8uHvz3Uyb8OA0a-G9UPO1ShLYjX0wUfhZcFB7fwPtgmmYAN6wKGVce9eMAbX4PliPk3r-BcpZuPKkuLk_wFvgWAQ5Hqw2iEuwXLV0_e8c2gaUt_hyMC5-nFc4v0Bmv6NT6Pfry-UrK3BKWc', + 'd' => 'Kp0KuZwCZGL1BLgsVM-N0edMNitl9wN5Hf2WOYDoIqOZNAEKzdJuenIMhITJjRFUX05GVL138uyp2js_pqDdY9ipA7rAKThwGuDdNphZHech9ih3DGEPXs-YpmHqvIbCd3GoGm38MKwxYkddEpFnjo8rKna1_BpJthrFxjDRhw9DxJBycOdH2yWTyp62ZENPvneK40H2a57W4QScTgfecZqD59m2fGUaWaX5uUmIxaEmtGoJnd9RE4oywKhgN7_TK7wXRlqA4UoRPiH2ACrdU-_cLQL9Jc0u0GqZJK31LDbOeN95QgtSCc72k3Vtzy3CrVpp5TAA67s1Gj9Skn-CAQ', + 'q' => 'zPD-B-nrngwF-O99BHvb47XGKR7ON8JCI6JxavzIkusMXCB8rMyYW8zLs68L8JLAzWZ34oMq0FPUnysBxc5nTF8Nb4BZxTZ5-9cHfoKrYTI3YWsmVW2FpCJFEjMs4NXZ28PBkS9b4zjfS2KhNdkmCeOYU0tJpNfwmOTI90qeUdU', + 'dp' => 'aJrzw_kjWK9uDlTeaES2e4muv6bWbopYfrPHVWG7NPGoGdhnBnd70-jhgMEiTZSNU8VXw2u7prAR3kZ-kAp1DdwlqedYOzFsOJcPA0UZhbORyrBy30kbll_7u6CanFm6X4VyJxCpejd7jKNw6cCTFP1sfhWg5NVJ5EUTkPwE66M', + 'dq' => 'Swz1-m_vmTFN_pu1bK7vF7S5nNVrL4A0OFiEsGliCmuJWzOKdL14DiYxctvnw3H6qT2dKZZfV2tbse5N9-JecdldUjfuqAoLIe7dD7dKi42YOlTC9QXmqvTh1ohnJu8pmRFXEZQGUm_BVhoIb2_WPkjav6YSkguCUHt4HRd2YwE', + 'qi' => 'BocuCOEOq-oyLDALwzMXU8gOf3IL1Q1_BWwsdoANoh6i179psxgE4JXToWcpXZQQqub8ngwE6uR9fpd3m6N_PL4T55vbDDyjPKmrL2ttC2gOtx9KrpPh-Z7LQRo4BE48nHJJrystKHfFlaH2G7JxHNgMBYVADyttN09qEoav8Os', + ], + [ + 'kty' => 'RSA', + 'n' => 'oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUWcJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3Spsk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2asbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMStPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2djYgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw', + 'e' => 'AQAB', + 'd' => 'kLdtIj6GbDks_ApCSTYQtelcNttlKiOyPzMrXHeI-yk1F7-kpDxY4-WY5NWV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD93Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghkqDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vlt3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSndVTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ', + 'p' => '1r52Xk46c-LsfB5P442p7atdPUrxQSy4mti_tZI3Mgf2EuFVbUoDBvaRQ-SWxkbkmoEzL7JXroSBjSrK3YIQgYdMgyAEPTPjXv_hI2_1eTSPVZfzL0lffNn03IXqWF5MDFuoUYE0hzb2vhrlN_rKrbfDIwUbTrjjgieRbwC6Cl0', + 'q' => 'wLb35x7hmQWZsWJmB_vle87ihgZ19S8lBEROLIsZG4ayZVe9Hi9gDVCOBmUDdaDYVTSNx_8Fyw1YYa9XGrGnDew00J28cRUoeBB_jKI1oma0Orv1T9aXIWxKwd4gvxFImOWr3QRL9KEBRzk2RatUBnmDZJTIAfwTs0g68UZHvtc', + 'dp' => 'ZK-YwE7diUh0qR1tR7w8WHtolDx3MZ_OTowiFvgfeQ3SiresXjm9gZ5KLhMXvo-uz-KUJWDxS5pFQ_M0evdo1dKiRTjVw_x4NyqyXPM5nULPkcpU827rnpZzAJKpdhWAgqrXGKAECQH0Xt4taznjnd_zVpAmZZq60WPMBMfKcuE', + 'dq' => 'Dq0gfgJ1DdFGXiLvQEZnuKEN0UUmsJBxkjydc3j4ZYdBiMRAy86x0vHCjywcMlYYg4yoC4YZa9hNVcsjqA3FeiL19rk8g6Qn29Tt0cj8qqyFpz9vNDBUfCAiJVeESOjJDZPYHdHY8v1b-o-Z2X5tvLx-TCekf7oxyeKDUqKWjis', + 'qi' => 'VIMpMYbPf47dT1w_zDUXfPimsSegnMOA1zTaX7aGk_8urY6R8-ZW1FxU7AlWAyLWybqq6t16VFd7hQd0y6flUK4SlOydB61gwanOsXGOAOv82cHq0E3eL4HrtZkUuKvnPrMnsUUFlfUdybVzxyjz9JF_XyaY14ardLSjf4L_FNY', + ], + [ + 'kty' => 'RSA', + 'n' => 'sXchDaQebHnPiGvyDOAT4saGEUetSyo9MKLOoWFsueri23bOdgWp4Dy1WlUzewbgBHod5pcM9H95GQRV3JDXboIRROSBigeC5yjU1hGzHHyXss8UDprecbAYxknTcQkhslANGRUZmdTOQ5qTRsLAt6BTYuyvVRdhS8exSZEy_c4gs_7svlJJQ4H9_NxsiIoLwAEk7-Q3UXERGYw_75IDrGA84-lA_-Ct4eTlXHBIY2EaV7t7LjJaynVJCpkv4LKjTTAumiGUIuQhrNhZLuF_RJLqHpM2kgWFLU7-VTdL1VbC2tejvcI2BlMkEpk1BzBZI0KQB0GaDWFLN-aEAw3vRw', + 'e' => 'AQAB', + 'd' => 'VFCWOqXr8nvZNyaaJLXdnNPXZKRaWCjkU5Q2egQQpTBMwhprMzWzpR8Sxq1OPThh_J6MUD8Z35wky9b8eEO0pwNS8xlh1lOFRRBoNqDIKVOku0aZb-rynq8cxjDTLZQ6Fz7jSjR1Klop-YKaUHc9GsEofQqYruPhzSA-QgajZGPbE_0ZaVDJHfyd7UUBUKunFMScbflYAAOYJqVIVwaYR5zWEEceUjNnTNo_CVSj-VvXLO5VZfCUAVLgW4dpf1SrtZjSt34YLsRarSb127reG_DUwg9Ch-KyvjT1SkHgUWRVGcyly7uvVGRSDwsXypdrNinPA4jlhoNdizK2zF2CWQ', + 'p' => '9gY2w6I6S6L0juEKsbeDAwpd9WMfgqFoeA9vEyEUuk4kLwBKcoe1x4HG68ik918hdDSE9vDQSccA3xXHOAFOPJ8R9EeIAbTi1VwBYnbTp87X-xcPWlEPkrdoUKW60tgs1aNd_Nnc9LEVVPMS390zbFxt8TN_biaBgelNgbC95sM', + 'q' => 'uKlCKvKv_ZJMVcdIs5vVSU_6cPtYI1ljWytExV_skstvRSNi9r66jdd9-yBhVfuG4shsp2j7rGnIio901RBeHo6TPKWVVykPu1iYhQXw1jIABfw-MVsN-3bQ76WLdt2SDxsHs7q7zPyUyHXmps7ycZ5c72wGkUwNOjYelmkiNS0', + 'dp' => 'w0kZbV63cVRvVX6yk3C8cMxo2qCM4Y8nsq1lmMSYhG4EcL6FWbX5h9yuvngs4iLEFk6eALoUS4vIWEwcL4txw9LsWH_zKI-hwoReoP77cOdSL4AVcraHawlkpyd2TWjE5evgbhWtOxnZee3cXJBkAi64Ik6jZxbvk-RR3pEhnCs', + 'dq' => 'o_8V14SezckO6CNLKs_btPdFiO9_kC1DsuUTd2LAfIIVeMZ7jn1Gus_Ff7B7IVx3p5KuBGOVF8L-qifLb6nQnLysgHDh132NDioZkhH7mI7hPG-PYE_odApKdnqECHWw0J-F0JWnUd6D2B_1TvF9mXA2Qx-iGYn8OVV1Bsmp6qU', + 'qi' => 'eNho5yRBEBxhGBtQRww9QirZsB66TrfFReG_CcteI1aCneT0ELGhYlRlCtUkTRclIfuEPmNsNDPbLoLqqCVznFbvdB7x-Tl-m0l_eFTj2KiqwGqE9PZB9nNTwMVvH3VRRSLWACvPnSiwP8N5Usy-WRXS-V7TbpxIhvepTfE0NNo', + ], + [ + 'kty' => 'RSA', + 'n' => 'ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ', + 'e' => 'AQAB', + 'd' => 'Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97IjlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYTCBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLhBOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ', + 'p' => '4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPGBY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc', + 'q' => 'uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc', + 'dp' => 'BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3QCLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0', + 'dq' => 'h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-kyNlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU', + 'qi' => 'IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2oy26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLUW0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U', + ], + [ + 'kty' => 'EC', + 'crv' => 'P-521', + 'x' => 'AekpBQ8ST8a8VcfVOTNl353vSrDCLLJXmPk06wTjxrrjcBpXp5EOnYG_NjFZ6OvLFV1jSfS9tsz4qUxcWceqwQGk', + 'y' => 'ADSmRA43Z1DSNx_RvcLI87cdL07l6jQyyBXMoxVg_l2Th-x3S1WDhjDly79ajL4Kkd0AZMaZmh9ubmf63e3kyMj2', + 'd' => 'AY5pb7A0UFiB3RELSD64fTLOSV_jazdF7fLYyuTw8lOfRhWg6Y6rUrPAxerEzgdRhajnu0ferB0d53vM9mE15j2C', + ], + ]]; + + return JWKSet::createFromKeyData($keys); + } + + private function getSymmetricKeySet(): JWKSet + { + $keys = ['keys' => [ + [ + 'kid' => 'DIR_1', + 'kty' => 'oct', + 'k' => Base64Url::encode(hex2bin('00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F')), + ], + [ + 'kty' => 'oct', + 'k' => 'f5aN5V6iihwQVqP-tPNNtkIJNCwUb9-JukCIKkF0rNfxqxA771RJynYAT2xtzAP0MYaR7U5fMP_wvbRQq5l38Q', + ], + [ + 'kty' => 'oct', + 'k' => 'GawgguFyGrWKav7AX4VKUg', + ], + [ + 'kty' => 'oct', + 'k' => 'AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow', + ], + ]]; + + return JWKSet::createFromKeyData($keys); + } +} diff --git a/vendor/web-token/jwt-encryption/Tests/RSAKeyWithoutAllPrimesTest.php b/vendor/web-token/jwt-encryption/Tests/RSAKeyWithoutAllPrimesTest.php new file mode 100644 index 000000000..7180a92a6 --- /dev/null +++ b/vendor/web-token/jwt-encryption/Tests/RSAKeyWithoutAllPrimesTest.php @@ -0,0 +1,127 @@ +getPrivateKey(); + + $claims = JsonConverter::encode(['foo' => 'bar']); + + $jweBuilder = $this->getJWEBuilderFactory()->create([$encryption_algorithm], ['A256GCM'], ['DEF']); + $jweDecrypter = $this->getJWEDecrypterFactory()->create([$encryption_algorithm], ['A256GCM'], ['DEF']); + + $jwt = $jweBuilder + ->create()->withPayload($claims) + ->withSharedProtectedHeader(['alg' => $encryption_algorithm, 'enc' => 'A256GCM']) + ->addRecipient($key) + ->build() + ; + $jwt = $this->getJWESerializerManager()->serialize('jwe_compact', $jwt, 0); + + $loaded = $this->getJWESerializerManager()->unserialize($jwt); + + static::assertTrue($jweDecrypter->decryptUsingKey($loaded, $key, 0)); + } + + /** + * @dataProvider dataEncryptionAlgorithms + * + * @test + */ + public function encryptionAlgorithmsWithMinimalRsaKey(string $encryption_algorithm): void + { + $key = $this->getMinimalPrivateKey(); + + $claims = JsonConverter::encode(['foo' => 'bar']); + + $jweBuilder = $this->getJWEBuilderFactory()->create([$encryption_algorithm], ['A256GCM'], ['DEF']); + $jweDecrypter = $this->getJWEDecrypterFactory()->create([$encryption_algorithm], ['A256GCM'], ['DEF']); + + $jwt = $jweBuilder + ->create()->withPayload($claims) + ->withSharedProtectedHeader(['alg' => $encryption_algorithm, 'enc' => 'A256GCM']) + ->addRecipient($key) + ->build() + ; + $jwt = $this->getJWESerializerManager()->serialize('jwe_compact', $jwt, 0); + + $loaded = $this->getJWESerializerManager()->unserialize($jwt); + + static::assertTrue($jweDecrypter->decryptUsingKey($loaded, $key, 0)); + } + + public function dataEncryptionAlgorithms(): array + { + return [ + ['RSA1_5'], + ['RSA-OAEP'], + ['RSA-OAEP-256'], + ]; + } + + public function dataEncryptionAlgorithmsWithSimpleKey(): array + { + return [ + ['RSA-OAEP'], + ['RSA-OAEP-256'], + ]; + } + + private function getPrivateKey(): JWK + { + return new JWK( + [ + 'kty' => 'RSA', + 'kid' => 'private', + 'n' => '2NRPORHXd7wPU6atHqmSfWgEPvsP8HVUkY2AwQQAc8x1J509X5HFxeSXnQym9eAnZHl0JCPbvHoPH4QHlvITYoh0MSgFm2aOPyqOD-XcNdKWtnNX2JIurUCyVlwSwtlmy2ZbCz8YuUmFO0iacahfK1wbWT5QoY-pU3UxnMzDhlBslZN5uL7nRE8Sh_8BthsrMdYeGIMY55kh-P7xTs3MHzpOKhFSrOhdN6aO3HWYUuMAdoMNB-hJvckb2PbCy0_K1Wm3SBHtXn-cuMIUF00W9AR3amp3u3hLa2rcz29jEFXTr2FxKyLH4SdlnFFMJl2vaXuxM4PXgLN33Kj34PfKgc8ljDJ7oaSI9bKt7gunXOLv_o4XWYDq91cvUkOIDAsvqxzzHPZBt0Hru7roW3btkUOiqR6RWy-Cw272yiSEC5QA93m_vklD1KajoFeWN0BW2lWGlfGieZldvKX0sumk1TZuLhlHPHSKYcpeCfahT-jLr1yAeHql6qRN_a0BiHu-SSSjts6InmF1pAELznZ3Jn9-QXX78LsY3xaqOlYqHbCohxXorlYRi4so6eMGILtXjqHOoISb13Ez4YNOQmV4ygmyABRkE0AQG5KLy5cZB7LZn7zqw869UjXxWrmiOaBeDqOkxww6qiWIEDwPIouRLwOfPFtC4LGlb9LmG9Hlhp8', + 'e' => 'AQAB', + 'd' => 'PsMls2VAsz3SSepjDg8Tgg1LvVc6w-WSdxc4f6ZC40H5X2AaVcGCN8f1QtZYta8Od_zX62Ydwq6qFftHnx-vEMRirZ_iD5td7VbKDDwCw-mTCnjUorGdpTSm6mx4WcJICPQ1wkmfRHLNh916JxAPjCN7Hxf0iu9kme3AUJzMs-IvrBQmFZ3cn18sBAWCX0358NEDoSDBYrhmpwZUnvTe8uMToQWmoroX0XX6wEGht8xRY_yHFxTb032U-_ZhaCxOj_uru8bEqKfTm39CBYSg8j0gu8LZqYAmhI9IHxsk16OgRJG2CkBlDv0yYk799dUEY0oUfs7Y4D4SoeKe7ZWMHgKMEqa7ONz18ORznxqKSQhi4hfNVgwMzaM0IoYP4KOfHuaK263zhJU0hMzURJ8KifECeOsDHBR6BhLJ9TYzUe4c9UU55nFNgRBwknKHFFrRAsgVETEzmZWHzWwGQIFtKIAVZ1cjkdMEL3BlbzzXVofXfbbCrPQqcABYx2BZ-J_P8-UFjeMo83VLrR5IHj0_8IhQZUmxZYJcpTIwrf-1A4JGlN2_eLqRymF8tZI6zIPJyo1C0M1CIB3EeHzi-70SbF8xFtGUB7hR234yo_SM-KqVdIk2Sjjta2bQ1KXjSEcvrS_358AMiP0-9JT_fHxTCyzra-SNYoZhdnrEFzoVwQE', + 'p' => '6fWvnj34kJtfMnO1j-qbPjFnaTevREBGAypMvUBU3Fx1Xx0nE7zdc7lln2Qq5-yTQtOQ2lpiE69HkQLR4pMU6V44SjFgVzcTzbFCnNgknEV54S5dyp4KojSWxBi6bt5GwaACkiElDEw9wgc-8JgaEkv4F7e-w44HBwPDECTjE_N0vIawpbD_y6zpifB8ziaAI3xTG4ssA1dt8WZuyQW8SR4FRsYnfkqy0twwHn02gs7XSl4NepkhSO7CY5-YC3U6LazAEZi2NTiUuZSw7F6KaRhsA8CnXTDE5JqFks_fXfLNCbtClON2JtrB1zY-l-2bHyh2a6unDtGn9ZN-Ec7BXw', + 'q' => '7UF_NblAyTxmj7Z2Jz1sZmz-Q3YHOcta00DjmHBhR9ItYRMQFMj-SUGPAtwvN-sk3_ThugaQt46SLT_I3Gy8433cHdW7o3So6HiMYVunyfhqnWznSWs6SvIoEh8rJOXkkIZ-DlRP8XyW5OOvi0cbWEQ1f1jbFyistMmnBClPvf2TKKPvShUl9qmvLxuU87j-_bgQmjVmtwZadnPOyPAxQ4_qqSfIiTOvMSxSycr58rTyu3khHQapGHkS5-2Y_w40GUSfVJ3XP48delYpK-PZP71hn89MJTnnfPOtvJAk1wbEev5wQFTJd-PGOudkGkuEIXryF4TGxRPltl5UeF0CwQ', + ] + ); + } + + private function getMinimalPrivateKey(): JWK + { + return new JWK( + [ + 'd' => 'JSqz6ijkk3dfdSEA_0iMT_1HeIJ1ft4msZ6qw7_1JSCGQAALeZ1yM0QHO3uX-Jr7HC7v1rGVcwsonAhei2qu3rk-w_iCnRL6QkkMNBnDQycwaWpwGsMBFF-UqstOJNggE4AHX-aDnbd4wbKVvdX7ieehPngbPkHcJFdg_iSZCQNoajz6XfEruyIi7_IFXYEGmH_UyEbQkgNtriZysutgYdolUjo9flUlh20HbuV3NwsPjGyDG4dUMpNpdBpSuRHYKLX6h3FjeLhItBmhBfuL7d-G3EXwKlwfNXXYivqY5NQAkFNrRbvFlc_ARIws3zAfykPDIWGWFiPiN3H-hXMgAQ', + 'e' => 'AQAB', + 'n' => 'gVf-iyhwLn2J2Up4EKjwdLYmk5n24gjGk4oQkCHVcE7j8wkS1iSzcu0ApVcMPLklEp_PWycZE12vL90gPeVjF2IPL_MKFL0b6Wy7A1f4kCDkKv7TDDjt1IIwbS-Jdp-2pG7bPb3tWjJUu6QZBLoXfRtW3cMDkQjXaVGixENORLAZs6qdu2MMKV94jetCiFd0JYCjxGVC0HW2OKnM21B_2R1NubOvMlWA7gypdpvmBYDGpkw4mjV3walWlCZObG7IH84Ovl7wOP8XLzqi2un4e6fNzy3rdp4OUSPYItF4ZX5qThWYY2R47Z5sbrZxHjNeDECKUeio0KPQNrgr6FSKSw', + 'kty' => 'RSA', + 'kid' => 'test-key', + ] + ); + } +} diff --git a/vendor/web-token/jwt-encryption/composer.json b/vendor/web-token/jwt-encryption/composer.json new file mode 100644 index 000000000..0d4f7f868 --- /dev/null +++ b/vendor/web-token/jwt-encryption/composer.json @@ -0,0 +1,52 @@ +{ + "name": "web-token/jwt-encryption", + "description": "Encryption component of the JWT Framework.", + "type": "library", + "license": "MIT", + "keywords": ["JWS", "JWT", "JWE", "JWA", "JWK", "JWKSet", "Jot", "Jose", "RFC7515", "RFC7516", "RFC7517", "RFC7518", "RFC7519", "RFC7520", "Bundle", "Symfony"], + "homepage": "https://github.com/web-token", + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + },{ + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-encryption/contributors" + } + ], + "autoload": { + "psr-4": { + "Jose\\Component\\Encryption\\": "" + } + }, + "require": { + "web-token/jwt-core": "^2.1" + }, + "require-dev": { + "phpunit/phpunit": "^8.0" + }, + "suggest": { + "web-token/jwt-encryption-algorithm-aescbc": "AES CBC Based Content Encryption Algorithms", + "web-token/jwt-encryption-algorithm-aesgcm": "AES GCM Based Content Encryption Algorithms", + "web-token/jwt-encryption-algorithm-aesgcmkw": "AES GCM Key Wrapping Based Key Encryption Algorithms", + "web-token/jwt-encryption-algorithm-aeskw": "AES Key Wrapping Based Key Encryption Algorithms", + "web-token/jwt-encryption-algorithm-dir": "Direct Key Encryption Algorithms", + "web-token/jwt-encryption-algorithm-ecdh-es": "ECDH-ES Based Key Encryption Algorithms", + "web-token/jwt-encryption-algorithm-pbes2": "PBES2 Based Key Encryption Algorithms", + "web-token/jwt-encryption-algorithm-rsa": "RSA Based Key Encryption Algorithms", + "web-token/jwt-encryption-algorithm-experimental": "Experimental Key and Signature Algorithms" + }, + "extra": { + "branch-alias": { + "v1.0": "1.0.x-dev", + "v1.1": "1.1.x-dev", + "v1.2": "1.2.x-dev", + "v1.3": "1.3.x-dev", + "v2.0": "2.0.x-dev", + "v2.1": "2.1.x-dev" + } + }, + "config": { + "sort-packages": true + } +} diff --git a/vendor/web-token/jwt-encryption/phpunit.xml.dist b/vendor/web-token/jwt-encryption/phpunit.xml.dist new file mode 100644 index 000000000..c8b3143f3 --- /dev/null +++ b/vendor/web-token/jwt-encryption/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + ./Tests/ + + + + + + ./ + + ./vendor + ./Tests + ./src + + + + diff --git a/vendor/web-token/jwt-signature-algorithm-ecdsa/.github/CONTRIBUTING.md b/vendor/web-token/jwt-signature-algorithm-ecdsa/.github/CONTRIBUTING.md new file mode 100644 index 000000000..fc360e5d8 --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-ecdsa/.github/CONTRIBUTING.md @@ -0,0 +1,4 @@ +# Contributing + +This repository is a sub repository of [the JWT Framework](https://github.com/web-token/jwt-framework) project and is READ ONLY. +Please do not submit any Pull Requests here. It will be automatically closed. diff --git a/vendor/web-token/jwt-signature-algorithm-ecdsa/.github/FUNDING.yml b/vendor/web-token/jwt-signature-algorithm-ecdsa/.github/FUNDING.yml new file mode 100644 index 000000000..7e2ca0e7e --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-ecdsa/.github/FUNDING.yml @@ -0,0 +1 @@ +patreon: FlorentMorselli diff --git a/vendor/web-token/jwt-signature-algorithm-ecdsa/.github/PULL_REQUEST_TEMPLATE.md b/vendor/web-token/jwt-signature-algorithm-ecdsa/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..d4ff96cb4 --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-ecdsa/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,3 @@ +Please do not submit any Pull Requests here. It will be automatically closed. + +You should submit it here: https://github.com/web-token/jwt-framework/pulls diff --git a/vendor/web-token/jwt-signature-algorithm-ecdsa/ECDSA.php b/vendor/web-token/jwt-signature-algorithm-ecdsa/ECDSA.php new file mode 100644 index 000000000..75b28a266 --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-ecdsa/ECDSA.php @@ -0,0 +1,80 @@ +checkKey($key); + if (!$key->has('d')) { + throw new InvalidArgumentException('The EC key is not private'); + } + $pem = ECKey::convertPrivateKeyToPEM($key); + openssl_sign($input, $signature, $pem, $this->getHashAlgorithm()); + + return ECSignature::fromAsn1($signature, $this->getSignaturePartLength()); + } + + public function verify(JWK $key, string $input, string $signature): bool + { + $this->checkKey($key); + + try { + $der = ECSignature::toAsn1($signature, $this->getSignaturePartLength()); + $pem = ECKey::convertPublicKeyToPEM($key); + + return 1 === openssl_verify($input, $der, $pem, $this->getHashAlgorithm()); + } catch (Throwable $e) { + return false; + } + } + + abstract protected function getHashAlgorithm(): string; + + abstract protected function getSignaturePartLength(): int; + + private function checkKey(JWK $key): void + { + if (!in_array($key->get('kty'), $this->allowedKeyTypes(), true)) { + throw new InvalidArgumentException('Wrong key type.'); + } + foreach (['x', 'y', 'crv'] as $k) { + if (!$key->has($k)) { + throw new InvalidArgumentException(sprintf('The key parameter "%s" is missing.', $k)); + } + } + } +} diff --git a/vendor/web-token/jwt-signature-algorithm-ecdsa/ES256.php b/vendor/web-token/jwt-signature-algorithm-ecdsa/ES256.php new file mode 100644 index 000000000..1999e7d4b --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-ecdsa/ES256.php @@ -0,0 +1,32 @@ +verify($key, $message, $signature); + + static::assertTrue($is_valid); + } + + public function dataWithVectors(): array + { + return [ + [ + new ES256(), + 'sample', + new JWK([ + 'kty' => 'EC', + 'crv' => 'P-256', + 'd' => Base64Url::encode($this->convertHexToBin('C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721')), + 'x' => Base64Url::encode($this->convertHexToBin('60FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB6')), + 'y' => Base64Url::encode($this->convertHexToBin('7903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299')), + ]), + sprintf( + '%s%s', + $this->convertHexToBin('EFD48B2AACB6A8FD1140DD9CD45E81D69D2C877B56AAF991C34D0EA84EAF3716'), + $this->convertHexToBin('F7CB1C942D657C41D436C7A1B6E29F65F3E900DBB9AFF4064DC4AB2F843ACDA8') + ), + ], + [ + new ES256(), + 'test', + new JWK([ + 'kty' => 'EC', + 'crv' => 'P-256', + 'd' => Base64Url::encode($this->convertHexToBin('C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721')), + 'x' => Base64Url::encode($this->convertHexToBin('60FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB6')), + 'y' => Base64Url::encode($this->convertHexToBin('7903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299')), + ]), + sprintf( + '%s%s', + $this->convertHexToBin('F1ABB023518351CD71D881567B1EA663ED3EFCF6C5132B354F28D3B0B7D38367'), + $this->convertHexToBin('019F4113742A2B14BD25926B49C649155F267E60D3814B4C0CC84250E46F0083') + ), + ], + [ + new ES384(), + 'sample', + new JWK([ + 'kty' => 'EC', + 'crv' => 'P-384', + 'd' => Base64Url::encode($this->convertHexToBin('6B9D3DAD2E1B8C1C05B19875B6659F4DE23C3B667BF297BA9AA47740787137D896D5724E4C70A825F872C9EA60D2EDF5')), + 'x' => Base64Url::encode($this->convertHexToBin('EC3A4E415B4E19A4568618029F427FA5DA9A8BC4AE92E02E06AAE5286B300C64DEF8F0EA9055866064A254515480BC13')), + 'y' => Base64Url::encode($this->convertHexToBin('8015D9B72D7D57244EA8EF9AC0C621896708A59367F9DFB9F54CA84B3F1C9DB1288B231C3AE0D4FE7344FD2533264720')), + ]), + sprintf( + '%s%s', + $this->convertHexToBin('94EDBB92A5ECB8AAD4736E56C691916B3F88140666CE9FA73D64C4EA95AD133C81A648152E44ACF96E36DD1E80FABE46'), + $this->convertHexToBin('99EF4AEB15F178CEA1FE40DB2603138F130E740A19624526203B6351D0A3A94FA329C145786E679E7B82C71A38628AC8') + ), + ], + [ + new ES384(), + 'test', + new JWK([ + 'kty' => 'EC', + 'crv' => 'P-384', + 'd' => Base64Url::encode($this->convertHexToBin('6B9D3DAD2E1B8C1C05B19875B6659F4DE23C3B667BF297BA9AA47740787137D896D5724E4C70A825F872C9EA60D2EDF5')), + 'x' => Base64Url::encode($this->convertHexToBin('EC3A4E415B4E19A4568618029F427FA5DA9A8BC4AE92E02E06AAE5286B300C64DEF8F0EA9055866064A254515480BC13')), + 'y' => Base64Url::encode($this->convertHexToBin('8015D9B72D7D57244EA8EF9AC0C621896708A59367F9DFB9F54CA84B3F1C9DB1288B231C3AE0D4FE7344FD2533264720')), + ]), + sprintf( + '%s%s', + $this->convertHexToBin('8203B63D3C853E8D77227FB377BCF7B7B772E97892A80F36AB775D509D7A5FEB0542A7F0812998DA8F1DD3CA3CF023DB'), + $this->convertHexToBin('DDD0760448D42D8A43AF45AF836FCE4DE8BE06B485E9B61B827C2F13173923E06A739F040649A667BF3B828246BAA5A5') + ), + ], + // A zero has been added at the beginning of each value from the RFC (cannot convert to binary of not an even length). + [ + new ES512(), + 'sample', + new JWK([ + 'kty' => 'EC', + 'crv' => 'P-521', + 'd' => Base64Url::encode($this->convertHexToBin('00FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538')), + 'x' => Base64Url::encode($this->convertHexToBin('01894550D0785932E00EAA23B694F213F8C3121F86DC97A04E5A7167DB4E5BCD371123D46E45DB6B5D5370A7F20FB633155D38FFA16D2BD761DCAC474B9A2F5023A4')), + 'y' => Base64Url::encode($this->convertHexToBin('00493101C962CD4D2FDDF782285E64584139C2F91B47F87FF82354D6630F746A28A0DB25741B5B34A828008B22ACC23F924FAAFBD4D33F81EA66956DFEAA2BFDFCF5')), + ]), + sprintf( + '%s%s', + $this->convertHexToBin('00C328FAFCBD79DD77850370C46325D987CB525569FB63C5D3BC53950E6D4C5F174E25A1EE9017B5D450606ADD152B534931D7D4E8455CC91F9B15BF05EC36E377FA'), + $this->convertHexToBin('00617CCE7CF5064806C467F678D3B4080D6F1CC50AF26CA209417308281B68AF282623EAA63E5B5C0723D8B8C37FF0777B1A20F8CCB1DCCC43997F1EE0E44DA4A67A') + ), + ], + [ + new ES512(), + 'test', + new JWK([ + 'kty' => 'EC', + 'crv' => 'P-521', + 'd' => Base64Url::encode($this->convertHexToBin('00FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538')), + 'x' => Base64Url::encode($this->convertHexToBin('01894550D0785932E00EAA23B694F213F8C3121F86DC97A04E5A7167DB4E5BCD371123D46E45DB6B5D5370A7F20FB633155D38FFA16D2BD761DCAC474B9A2F5023A4')), + 'y' => Base64Url::encode($this->convertHexToBin('00493101C962CD4D2FDDF782285E64584139C2F91B47F87FF82354D6630F746A28A0DB25741B5B34A828008B22ACC23F924FAAFBD4D33F81EA66956DFEAA2BFDFCF5')), + ]), + sprintf( + '%s%s', + $this->convertHexToBin('013E99020ABF5CEE7525D16B69B229652AB6BDF2AFFCAEF38773B4B7D08725F10CDB93482FDCC54EDCEE91ECA4166B2A7C6265EF0CE2BD7051B7CEF945BABD47EE6D'), + $this->convertHexToBin('01FBD0013C674AA79CB39849527916CE301C66EA7CE8B80682786AD60F98F7E78A19CA69EFF5C57400E3B3A0AD66CE0978214D13BAF4E9AC60752F7B155E2DE4DCE3') + ), + ], + ]; + } + + /** + * @param string $data + * + * @return string + */ + private function convertHexToBin($data) + { + return hex2bin($data); + } +} diff --git a/vendor/web-token/jwt-signature-algorithm-ecdsa/Tests/ECDSAFromRFC7520Test.php b/vendor/web-token/jwt-signature-algorithm-ecdsa/Tests/ECDSAFromRFC7520Test.php new file mode 100644 index 000000000..7e3bb2085 --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-ecdsa/Tests/ECDSAFromRFC7520Test.php @@ -0,0 +1,107 @@ + 'EC', + 'kid' => 'bilbo.baggins@hobbiton.example', + 'use' => 'sig', + 'crv' => 'P-521', + 'x' => 'AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt', + 'y' => 'AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVySsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1', + 'd' => 'AAhRON2r9cqXX1hg-RoI6R1tX5p2rUAYdmpHZoC1XNM56KtscrX6zbKipQrCW9CGZH3T4ubpnoTKLDYJ_fF3_rJt', + ]); + + /* + * Header + * @see https://tools.ietf.org/html/rfc7520#section-4.3.2 + */ + $header = [ + 'alg' => 'ES512', + 'kid' => 'bilbo.baggins@hobbiton.example', + ]; + + $jwsBuilder = new JWSBuilder( + new AlgorithmManager([new ES512()]) + ); + $jwsVerifier = new JWSVerifier( + new AlgorithmManager([new ES512()]) + ); + $compactSerializer = new Serializer\CompactSerializer( + ); + $jsonFlattenedSerializer = new Serializer\JSONFlattenedSerializer( + ); + $jsonGeneralSerializer = new Serializer\JSONGeneralSerializer( + ); + $jws = $jwsBuilder + ->create()->withPayload($payload) + ->addSignature($private_key, $header) + ->build() + ; + + static::assertTrue($jwsVerifier->verifyWithKey($jws, $private_key, 0)); + + /* + * Header + * @see https://tools.ietf.org/html/rfc7520#section-4.3.3 + */ + $expected_compact_json = 'eyJhbGciOiJFUzUxMiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhbXBsZSJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4.AE_R_YZCChjn4791jSQCrdPZCNYqHXCTZH0-JZGYNlaAjP2kqaluUIIUnC9qvbu9Plon7KRTzoNEuT4Va2cmL1eJAQy3mtPBu_u_sDDyYjnAMDxXPn7XrT0lw-kvAD890jl8e2puQens_IEKBpHABlsbEPX6sFY8OcGDqoRuBomu9xQ2'; + $expected_flattened_json = '{"payload":"SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4","protected":"eyJhbGciOiJFUzUxMiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhbXBsZSJ9","signature":"AE_R_YZCChjn4791jSQCrdPZCNYqHXCTZH0-JZGYNlaAjP2kqaluUIIUnC9qvbu9Plon7KRTzoNEuT4Va2cmL1eJAQy3mtPBu_u_sDDyYjnAMDxXPn7XrT0lw-kvAD890jl8e2puQens_IEKBpHABlsbEPX6sFY8OcGDqoRuBomu9xQ2"}'; + $expected_json = '{"payload":"SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4","signatures":[{"protected":"eyJhbGciOiJFUzUxMiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhbXBsZSJ9","signature":"AE_R_YZCChjn4791jSQCrdPZCNYqHXCTZH0-JZGYNlaAjP2kqaluUIIUnC9qvbu9Plon7KRTzoNEuT4Va2cmL1eJAQy3mtPBu_u_sDDyYjnAMDxXPn7XrT0lw-kvAD890jl8e2puQens_IEKBpHABlsbEPX6sFY8OcGDqoRuBomu9xQ2"}]}'; + + $loaded_compact_json = $compactSerializer->unserialize($expected_compact_json); + static::assertTrue($jwsVerifier->verifyWithKey($loaded_compact_json, $private_key, 0)); + + $loaded_flattened_json = $jsonFlattenedSerializer->unserialize($expected_flattened_json); + static::assertTrue($jwsVerifier->verifyWithKey($loaded_flattened_json, $private_key, 0)); + + $loaded_json = $jsonGeneralSerializer->unserialize($expected_json); + static::assertTrue($jwsVerifier->verifyWithKey($loaded_json, $private_key, 0)); + } +} diff --git a/vendor/web-token/jwt-signature-algorithm-ecdsa/Tests/ECDSASignatureTest.php b/vendor/web-token/jwt-signature-algorithm-ecdsa/Tests/ECDSASignatureTest.php new file mode 100644 index 000000000..9b3654314 --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-ecdsa/Tests/ECDSASignatureTest.php @@ -0,0 +1,205 @@ +expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Wrong key type.'); + $key = new JWK([ + 'kty' => 'RSA', + ]); + + $ecdsa = new ES256(); + $data = 'Live long and Prosper.'; + + $ecdsa->sign($key, $data); + } + + /** + * @test + * @covers \Jose\Component\Signature\Algorithm\ES256 + */ + public function eS256Verify(): void + { + $key = new JWK([ + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => 'f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU', + 'y' => 'x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0', + 'd' => 'jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI', + ]); + + $ecdsa = new ES256(); + $data = 'eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ'; + $signature = 'DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q'; + + $sign = $ecdsa->sign($key, $data); + + static::assertTrue($ecdsa->verify($key, $data, $sign)); + static::assertTrue($ecdsa->verify($key, $data, Base64Url::decode($signature))); + } + + /** + * @test + * @covers \Jose\Component\Signature\Algorithm\ES256 + */ + public function eS256SignVerify(): void + { + $key = new JWK([ + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => 'f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU', + 'y' => 'x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0', + 'd' => 'jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI', + ]); + + $ecdsa = new ES256(); + + $data = 'eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ'; + $signature = $ecdsa->sign($key, $data); + + static::assertTrue($ecdsa->verify($key, $data, $signature)); + } + + /** + * @test + * @covers \Jose\Component\Signature\Algorithm\ES256 + */ + public function keyNotPrivate(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The EC key is not private'); + $key = new JWK([ + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => 'f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU', + 'y' => 'x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0', + ]); + + $ecdsa = new ES256(); + + $data = 'eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ'; + $ecdsa->sign($key, $data); + } + + /** + * @test + * @covers \Jose\Component\Signature\Algorithm\ES384 + */ + public function eS384SignVerify(): void + { + $key = new JWK([ + 'kty' => 'EC', + 'crv' => 'P-384', + 'd' => 'pcSSXrbeZEOaBIs7IwqcU9M_OOM81XhZuOHoGgmS_2PdECwcdQcXzv7W8-lYL0cr', + 'x' => '6f-XZsg2Tvn0EoEapQ-ylMYNtsm8CPf0cb8HI2EkfY9Bqpt3QMzwlM7mVsFRmaMZ', + 'y' => 'b8nOnRwmpmEnvA2U8ydS-dbnPv7bwYl-q1qNeh8Wpjor3VO-RTt4ce0Pn25oGGWU', + ]); + + $ecdsa = new ES384(); + + $data = 'eyJhbGciOiJFUzUxMiJ9.UGF5bG9hZA'; + $signature = $ecdsa->sign($key, $data); + + static::assertTrue($ecdsa->verify($key, $data, $signature)); + } + + /** + * @test + * @covers \Jose\Component\Signature\Algorithm\ES512 + */ + public function eS512Verify(): void + { + $key = new JWK([ + 'kty' => 'EC', + 'crv' => 'P-521', + 'x' => 'AekpBQ8ST8a8VcfVOTNl353vSrDCLLJXmPk06wTjxrrjcBpXp5EOnYG_NjFZ6OvLFV1jSfS9tsz4qUxcWceqwQGk', + 'y' => 'ADSmRA43Z1DSNx_RvcLI87cdL07l6jQyyBXMoxVg_l2Th-x3S1WDhjDly79ajL4Kkd0AZMaZmh9ubmf63e3kyMj2', + 'd' => 'AY5pb7A0UFiB3RELSD64fTLOSV_jazdF7fLYyuTw8lOfRhWg6Y6rUrPAxerEzgdRhajnu0ferB0d53vM9mE15j2C', + ]); + + $ecdsa = new ES512(); + $data = 'eyJhbGciOiJFUzUxMiJ9.UGF5bG9hZA'; + $signature = 'AdwMgeerwtHoh-l192l60hp9wAHZFVJbLfD_UxMi70cwnZOYaRI1bKPWROc-mZZqwqT2SI-KGDKB34XO0aw_7XdtAG8GaSwFKdCAPZgoXD2YBJZCPEX3xKpRwcdOO8KpEHwJjyqOgzDO7iKvU8vcnwNrmxYbSW9ERBXukOXolLzeO_Jn'; + + $sign = $ecdsa->sign($key, $data); + + static::assertTrue($ecdsa->verify($key, $data, $sign)); + static::assertTrue($ecdsa->verify($key, $data, Base64Url::decode($signature))); + } + + /** + * @test + * @covers \Jose\Component\Signature\Algorithm\ES512 + */ + public function eS512SignVerify(): void + { + $key = new JWK([ + 'kty' => 'EC', + 'crv' => 'P-521', + 'x' => 'AekpBQ8ST8a8VcfVOTNl353vSrDCLLJXmPk06wTjxrrjcBpXp5EOnYG_NjFZ6OvLFV1jSfS9tsz4qUxcWceqwQGk', + 'y' => 'ADSmRA43Z1DSNx_RvcLI87cdL07l6jQyyBXMoxVg_l2Th-x3S1WDhjDly79ajL4Kkd0AZMaZmh9ubmf63e3kyMj2', + 'd' => 'AY5pb7A0UFiB3RELSD64fTLOSV_jazdF7fLYyuTw8lOfRhWg6Y6rUrPAxerEzgdRhajnu0ferB0d53vM9mE15j2C', + ]); + + $ecdsa = new ES512(); + + $data = 'eyJhbGciOiJFUzUxMiJ9.UGF5bG9hZA'; + $signature = $ecdsa->sign($key, $data); + + static::assertTrue($ecdsa->verify($key, $data, $signature)); + } + + /** + * @test + * @covers \Jose\Component\Signature\Algorithm\ES256 + */ + public function badSignature(): void + { + $key = new JWK([ + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => 'f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU', + 'y' => 'x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0', + ]); + + $ecdsa = new ES256(); + + $data = 'eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ'; + $signature = 'DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3'; + + static::assertFalse($ecdsa->verify($key, $data, Base64Url::decode($signature))); + } +} diff --git a/vendor/web-token/jwt-signature-algorithm-ecdsa/composer.json b/vendor/web-token/jwt-signature-algorithm-ecdsa/composer.json new file mode 100644 index 000000000..ed896c0a9 --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-ecdsa/composer.json @@ -0,0 +1,42 @@ +{ + "name": "web-token/jwt-signature-algorithm-ecdsa", + "description": "ECDSA Based Signature Algorithms the JWT Framework.", + "type": "library", + "license": "MIT", + "keywords": ["JWS", "JWT", "JWE", "JWA", "JWK", "JWKSet", "Jot", "Jose", "RFC7515", "RFC7516", "RFC7517", "RFC7518", "RFC7519", "RFC7520", "Bundle", "Symfony"], + "homepage": "https://github.com/web-token", + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + },{ + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-framework/contributors" + } + ], + "autoload": { + "psr-4": { + "Jose\\Component\\Signature\\Algorithm\\": "" + } + }, + "require": { + "ext-openssl": "*", + "web-token/jwt-signature": "^2.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.0" + }, + "extra": { + "branch-alias": { + "v1.0": "1.0.x-dev", + "v1.1": "1.1.x-dev", + "v1.2": "1.2.x-dev", + "v1.3": "1.3.x-dev", + "v2.0": "2.0.x-dev", + "v2.1": "2.1.x-dev" + } + }, + "config": { + "sort-packages": true + } +} diff --git a/vendor/web-token/jwt-signature-algorithm-ecdsa/phpunit.xml.dist b/vendor/web-token/jwt-signature-algorithm-ecdsa/phpunit.xml.dist new file mode 100644 index 000000000..c8b3143f3 --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-ecdsa/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + ./Tests/ + + + + + + ./ + + ./vendor + ./Tests + ./src + + + + diff --git a/vendor/web-token/jwt-signature-algorithm-hmac/.github/CONTRIBUTING.md b/vendor/web-token/jwt-signature-algorithm-hmac/.github/CONTRIBUTING.md new file mode 100644 index 000000000..fc360e5d8 --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-hmac/.github/CONTRIBUTING.md @@ -0,0 +1,4 @@ +# Contributing + +This repository is a sub repository of [the JWT Framework](https://github.com/web-token/jwt-framework) project and is READ ONLY. +Please do not submit any Pull Requests here. It will be automatically closed. diff --git a/vendor/web-token/jwt-signature-algorithm-hmac/.github/FUNDING.yml b/vendor/web-token/jwt-signature-algorithm-hmac/.github/FUNDING.yml new file mode 100644 index 000000000..7e2ca0e7e --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-hmac/.github/FUNDING.yml @@ -0,0 +1 @@ +patreon: FlorentMorselli diff --git a/vendor/web-token/jwt-signature-algorithm-hmac/.github/PULL_REQUEST_TEMPLATE.md b/vendor/web-token/jwt-signature-algorithm-hmac/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..d4ff96cb4 --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-hmac/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,3 @@ +Please do not submit any Pull Requests here. It will be automatically closed. + +You should submit it here: https://github.com/web-token/jwt-framework/pulls diff --git a/vendor/web-token/jwt-signature-algorithm-hmac/HMAC.php b/vendor/web-token/jwt-signature-algorithm-hmac/HMAC.php new file mode 100644 index 000000000..371b1dd0e --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-hmac/HMAC.php @@ -0,0 +1,61 @@ +hash($key, $input), $signature); + } + + public function hash(JWK $key, string $input): string + { + $k = $this->getKey($key); + + return hash_hmac($this->getHashAlgorithm(), $input, $k, true); + } + + /** + * @throws InvalidArgumentException if the key is invalid + */ + protected function getKey(JWK $key): string + { + if (!in_array($key->get('kty'), $this->allowedKeyTypes(), true)) { + throw new InvalidArgumentException('Wrong key type.'); + } + if (!$key->has('k')) { + throw new InvalidArgumentException('The key parameter "k" is missing.'); + } + $k = $key->get('k'); + if (!is_string($k)) { + throw new InvalidArgumentException('The key parameter "k" is invalid.'); + } + + return Base64Url::decode($k); + } + + abstract protected function getHashAlgorithm(): string; +} diff --git a/vendor/web-token/jwt-signature-algorithm-hmac/HS256.php b/vendor/web-token/jwt-signature-algorithm-hmac/HS256.php new file mode 100644 index 000000000..a1790139e --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-hmac/HS256.php @@ -0,0 +1,43 @@ + 'oct', + 'kid' => '018c0ae5-4d9b-471b-bfd6-eef314bc7037', + 'use' => 'sig', + 'alg' => 'HS256', + 'k' => 'hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg', + ]); + + /* + * Header + * @see https://tools.ietf.org/html/rfc7520#section-4.4.2 + */ + $header = [ + 'alg' => 'HS256', + 'kid' => '018c0ae5-4d9b-471b-bfd6-eef314bc7037', + ]; + + $jwsBuilder = new JWSBuilder( + new AlgorithmManager([new HS256()]) + ); + $jwsVerifier = new JWSVerifier( + new AlgorithmManager([new HS256()]) + ); + $compactSerializer = new Serializer\CompactSerializer( + ); + $jsonFlattenedSerializer = new Serializer\JSONFlattenedSerializer( + ); + $jsonGeneralSerializer = new Serializer\JSONGeneralSerializer( + ); + $jws = $jwsBuilder + ->create()->withPayload($payload) + ->addSignature($key, $header) + ->build() + ; + + /* + * Header + * @see https://tools.ietf.org/html/rfc7520#section-4.4.3 + */ + $expected_compact_json = 'eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LWVlZjMxNGJjNzAzNyJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4.s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0'; + $expected_flattened_json = '{"payload":"SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4","protected":"eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LWVlZjMxNGJjNzAzNyJ9","signature":"s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0"}'; + $expected_json = '{"payload":"SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4","signatures":[{"protected":"eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LWVlZjMxNGJjNzAzNyJ9","signature":"s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0"}]}'; + + static::assertEquals($expected_compact_json, $compactSerializer->serialize($jws, 0)); + + // We decode the json to compare the 2 arrays otherwise the test may fail as the order may be different + static::assertEquals(json_decode($expected_flattened_json, true), json_decode($jsonFlattenedSerializer->serialize($jws, 0), true)); + static::assertEquals(json_decode($expected_json, true), json_decode($jsonGeneralSerializer->serialize($jws, 0), true)); + + $loaded_compact_json = $compactSerializer->unserialize($expected_compact_json); + static::assertTrue($jwsVerifier->verifyWithKey($loaded_compact_json, $key, 0)); + + $loaded_flattened_json = $jsonFlattenedSerializer->unserialize($expected_flattened_json); + static::assertTrue($jwsVerifier->verifyWithKey($loaded_flattened_json, $key, 0)); + + $loaded_json = $jsonGeneralSerializer->unserialize($expected_json); + static::assertTrue($jwsVerifier->verifyWithKey($loaded_json, $key, 0)); + } + + /** + * @see https://tools.ietf.org/html/rfc7520#section-4.5 + * + * @test + */ + public function hS256WithDetachedPayload(): void + { + /* + * Payload + * Symmetric Key + * @see https://tools.ietf.org/html/rfc7520#section-3.5 + * @see https://tools.ietf.org/html/rfc7520#section-4.5.1 + */ + $payload = "It\xe2\x80\x99s a dangerous business, Frodo, going out your door. You step onto the road, and if you don't keep your feet, there\xe2\x80\x99s no knowing where you might be swept off to."; + $key = new JWK([ + 'kty' => 'oct', + 'kid' => '018c0ae5-4d9b-471b-bfd6-eef314bc7037', + 'use' => 'sig', + 'alg' => 'HS256', + 'k' => 'hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg', + ]); + + /* + * Header + * @see https://tools.ietf.org/html/rfc7520#section-4.5.2 + */ + $header = [ + 'alg' => 'HS256', + 'kid' => '018c0ae5-4d9b-471b-bfd6-eef314bc7037', + ]; + + $jwsBuilder = new JWSBuilder( + new AlgorithmManager([new HS256()]) + ); + $jwsVerifier = new JWSVerifier( + new AlgorithmManager([new HS256()]) + ); + $compactSerializer = new Serializer\CompactSerializer( + ); + $jsonFlattenedSerializer = new Serializer\JSONFlattenedSerializer( + ); + $jsonGeneralSerializer = new Serializer\JSONGeneralSerializer( + ); + $jws = $jwsBuilder + ->create()->withPayload($payload, true) + ->addSignature($key, $header) + ->build() + ; + + /* + * Header + * @see https://tools.ietf.org/html/rfc7520#section-4.5.3 + */ + $expected_compact_json = 'eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LWVlZjMxNGJjNzAzNyJ9..s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0'; + $expected_flattened_json = '{"protected":"eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LWVlZjMxNGJjNzAzNyJ9","signature":"s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0"}'; + $expected_json = '{"signatures":[{"protected":"eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LWVlZjMxNGJjNzAzNyJ9","signature":"s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0"}]}'; + + static::assertEquals($expected_compact_json, $compactSerializer->serialize($jws, 0)); + + // We decode the json to compare the 2 arrays otherwise the test may fail as the order may be different + static::assertEquals(json_decode($expected_flattened_json, true), json_decode($jsonFlattenedSerializer->serialize($jws, 0), true)); + + static::assertEquals(json_decode($expected_json, true), json_decode($jsonGeneralSerializer->serialize($jws, 0), true)); + + $loaded_compact_json = $compactSerializer->unserialize($expected_compact_json); + static::assertTrue($jwsVerifier->verifyWithKey($loaded_compact_json, $key, 0, $payload)); + + $loaded_flattened_json = $jsonFlattenedSerializer->unserialize($expected_flattened_json); + static::assertTrue($jwsVerifier->verifyWithKey($loaded_flattened_json, $key, 0, $payload)); + + $loaded_json = $jsonGeneralSerializer->unserialize($expected_json); + static::assertTrue($jwsVerifier->verifyWithKey($loaded_json, $key, 0, $payload)); + } + + /** + * @see https://tools.ietf.org/html/rfc7520#section-4.6 + * + * @test + */ + public function hS256WithUnprotectedHeader(): void + { + /* + * Payload + * Symmetric Key + * @see https://tools.ietf.org/html/rfc7520#section-3.5 + * @see https://tools.ietf.org/html/rfc7520#section-4.6.1 + */ + $payload = "It\xe2\x80\x99s a dangerous business, Frodo, going out your door. You step onto the road, and if you don't keep your feet, there\xe2\x80\x99s no knowing where you might be swept off to."; + $key = new JWK([ + 'kty' => 'oct', + 'kid' => '018c0ae5-4d9b-471b-bfd6-eef314bc7037', + 'use' => 'sig', + 'alg' => 'HS256', + 'k' => 'hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg', + ]); + + /* + * Header + * @see https://tools.ietf.org/html/rfc7520#section-4.6.2 + */ + $protectedHeader = [ + 'alg' => 'HS256', + ]; + $unprotectedHeader = [ + 'kid' => '018c0ae5-4d9b-471b-bfd6-eef314bc7037', + ]; + + $jwsBuilder = new JWSBuilder( + new AlgorithmManager([new HS256()]) + ); + $jwsVerifier = new JWSVerifier( + new AlgorithmManager([new HS256()]) + ); + $compactSerializer = new Serializer\CompactSerializer( + ); + $jsonFlattenedSerializer = new Serializer\JSONFlattenedSerializer( + ); + $jsonGeneralSerializer = new Serializer\JSONGeneralSerializer( + ); + $jws = $jwsBuilder + ->create()->withPayload($payload) + ->addSignature($key, $protectedHeader, $unprotectedHeader) + ->build() + ; + + /* + * Header + * @see https://tools.ietf.org/html/rfc7520#section-4.6.3 + */ + $expected_flattened_json = '{"payload": "SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4","protected": "eyJhbGciOiJIUzI1NiJ9","header": {"kid": "018c0ae5-4d9b-471b-bfd6-eef314bc7037"},"signature": "bWUSVaxorn7bEF1djytBd0kHv70Ly5pvbomzMWSOr20"}'; + $expected_json = '{"payload":"SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4","signatures":[{"protected":"eyJhbGciOiJIUzI1NiJ9","header":{"kid":"018c0ae5-4d9b-471b-bfd6-eef314bc7037"},"signature":"bWUSVaxorn7bEF1djytBd0kHv70Ly5pvbomzMWSOr20"}]}'; + + // We decode the json to compare the 2 arrays otherwise the test may fail as the order may be different + static::assertEquals(json_decode($expected_flattened_json, true), json_decode($jsonFlattenedSerializer->serialize($jws, 0), true)); + static::assertEquals(json_decode($expected_json, true), json_decode($jsonGeneralSerializer->serialize($jws, 0), true)); + + $loaded_flattened_json = $jsonFlattenedSerializer->unserialize($expected_flattened_json); + static::assertTrue($jwsVerifier->verifyWithKey($loaded_flattened_json, $key, 0)); + + $loaded_json = $jsonGeneralSerializer->unserialize($expected_json); + static::assertTrue($jwsVerifier->verifyWithKey($loaded_json, $key, 0)); + } + + /** + * @see https://tools.ietf.org/html/rfc7520#section-4.7 + * + * @test + */ + public function hS256WithoutProtectedHeader(): void + { + /* + * Payload + * Symmetric Key + * @see https://tools.ietf.org/html/rfc7520#section-3.5 + * @see https://tools.ietf.org/html/rfc7520#section-4.7.1 + */ + $payload = "It\xe2\x80\x99s a dangerous business, Frodo, going out your door. You step onto the road, and if you don't keep your feet, there\xe2\x80\x99s no knowing where you might be swept off to."; + $key = new JWK([ + 'kty' => 'oct', + 'kid' => '018c0ae5-4d9b-471b-bfd6-eef314bc7037', + 'use' => 'sig', + 'alg' => 'HS256', + 'k' => 'hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg', + ]); + + /* + * Header + * @see https://tools.ietf.org/html/rfc7520#section-4.7.2 + */ + $unprotectedHeader = [ + 'alg' => 'HS256', + 'kid' => '018c0ae5-4d9b-471b-bfd6-eef314bc7037', + ]; + + $jwsBuilder = new JWSBuilder( + new AlgorithmManager([new HS256()]) + ); + $jwsVerifier = new JWSVerifier( + new AlgorithmManager([new HS256()]) + ); + $jsonFlattenedSerializer = new Serializer\JSONFlattenedSerializer( + ); + $jsonGeneralSerializer = new Serializer\JSONGeneralSerializer( + ); + $jws = $jwsBuilder + ->create()->withPayload($payload) + ->addSignature($key, [], $unprotectedHeader) + ->build() + ; + + /* + * Header + * @see https://tools.ietf.org/html/rfc7520#section-4.7.3 + */ + $expected_flattened_json = '{"payload":"SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4","header":{"alg":"HS256","kid":"018c0ae5-4d9b-471b-bfd6-eef314bc7037"},"signature":"xuLifqLGiblpv9zBpuZczWhNj1gARaLV3UxvxhJxZuk"}'; + $expected_json = '{"payload":"SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4","signatures":[{"header":{"alg":"HS256","kid":"018c0ae5-4d9b-471b-bfd6-eef314bc7037"},"signature":"xuLifqLGiblpv9zBpuZczWhNj1gARaLV3UxvxhJxZuk"}]}'; + + // We decode the json to compare the 2 arrays otherwise the test may fail as the order may be different + static::assertEquals(json_decode($expected_flattened_json, true), json_decode($jsonFlattenedSerializer->serialize($jws, 0), true)); + static::assertEquals(json_decode($expected_json, true), json_decode($jsonGeneralSerializer->serialize($jws, 0), true)); + + $loaded_flattened_json = $jsonFlattenedSerializer->unserialize($expected_flattened_json); + static::assertTrue($jwsVerifier->verifyWithKey($loaded_flattened_json, $key, 0)); + + $loaded_json = $jsonGeneralSerializer->unserialize($expected_json); + static::assertTrue($jwsVerifier->verifyWithKey($loaded_json, $key, 0)); + } +} diff --git a/vendor/web-token/jwt-signature-algorithm-hmac/Tests/HMACSignatureTest.php b/vendor/web-token/jwt-signature-algorithm-hmac/Tests/HMACSignatureTest.php new file mode 100644 index 000000000..d6e35eef1 --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-hmac/Tests/HMACSignatureTest.php @@ -0,0 +1,116 @@ +expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Wrong key type.'); + $key = new JWK([ + 'kty' => 'EC', + ]); + + $hmac = new HS256(); + $data = 'Live long and Prosper.'; + + $hmac->hash($key, $data); + } + + /** + * @test + */ + public function signatureHasBadBadLength(): void + { + $key = new JWK([ + 'kty' => 'oct', + 'k' => 'foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo', + ]); + $hmac = new HS256(); + $data = 'Live long and Prosper.'; + + static::assertFalse($hmac->verify($key, $data, hex2bin('326eb338c465d3587f3349df0b96ba81'))); + } + + /** + * @test + */ + public function hS256SignAndVerify(): void + { + $key = new JWK([ + 'kty' => 'oct', + 'k' => 'foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo', + ]); + $hmac = new HS256(); + $data = 'Live long and Prosper.'; + + $signature = $hmac->hash($key, $data); + + static::assertEquals(hex2bin('7ed268ef179f530a4a1c56225c352a6782cf5379085c484b4f355b6744d6f19d'), $signature); + static::assertTrue($hmac->verify($key, $data, $signature)); + } + + /** + * @test + */ + public function hS384SignAndVerify(): void + { + $key = new JWK([ + 'kty' => 'oct', + 'k' => 'foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo', + ]); + $hmac = new HS384(); + $data = 'Live long and Prosper.'; + + $signature = $hmac->hash($key, $data); + + static::assertEquals(hex2bin('903ce2ef2878090d6117f88210d5a822d260fae66760186cb3326770748b9fa47c2d4531a4d5d868f99bcf7ea45c1ab4'), $signature); + static::assertTrue($hmac->verify($key, $data, $signature)); + } + + /** + * @test + */ + public function hS512SignAndVerify(): void + { + $key = new JWK([ + 'kty' => 'oct', + 'k' => 'foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo', + ]); + $hmac = new HS512(); + $data = 'Live long and Prosper.'; + + $signature = $hmac->hash($key, $data); + + static::assertEquals(hex2bin('e8b36712b6c6dc422eec77f31ce372ccac769450413238158bd702069630456a148d0c10dd3a661a774217fb90b0d5f94fa6c3c985438bade92ff975b9e4dc04'), $signature); + static::assertTrue($hmac->verify($key, $data, $signature)); + } +} diff --git a/vendor/web-token/jwt-signature-algorithm-hmac/composer.json b/vendor/web-token/jwt-signature-algorithm-hmac/composer.json new file mode 100644 index 000000000..cb6dab8d1 --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-hmac/composer.json @@ -0,0 +1,41 @@ +{ + "name": "web-token/jwt-signature-algorithm-hmac", + "description": "HMAC Based Signature Algorithms the JWT Framework.", + "type": "library", + "license": "MIT", + "keywords": ["JWS", "JWT", "JWE", "JWA", "JWK", "JWKSet", "Jot", "Jose", "RFC7515", "RFC7516", "RFC7517", "RFC7518", "RFC7519", "RFC7520", "Bundle", "Symfony"], + "homepage": "https://github.com/web-token", + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + },{ + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-framework/contributors" + } + ], + "autoload": { + "psr-4": { + "Jose\\Component\\Signature\\Algorithm\\": "" + } + }, + "require": { + "web-token/jwt-signature": "^2.1" + }, + "require-dev": { + "phpunit/phpunit": "^8.0" + }, + "extra": { + "branch-alias": { + "v1.0": "1.0.x-dev", + "v1.1": "1.1.x-dev", + "v1.2": "1.2.x-dev", + "v1.3": "1.3.x-dev", + "v2.0": "2.0.x-dev", + "v2.1": "2.1.x-dev" + } + }, + "config": { + "sort-packages": true + } +} diff --git a/vendor/web-token/jwt-signature-algorithm-hmac/phpunit.xml.dist b/vendor/web-token/jwt-signature-algorithm-hmac/phpunit.xml.dist new file mode 100644 index 000000000..c8b3143f3 --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-hmac/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + ./Tests/ + + + + + + ./ + + ./vendor + ./Tests + ./src + + + + diff --git a/vendor/web-token/jwt-signature-algorithm-rsa/.github/CONTRIBUTING.md b/vendor/web-token/jwt-signature-algorithm-rsa/.github/CONTRIBUTING.md new file mode 100644 index 000000000..fc360e5d8 --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-rsa/.github/CONTRIBUTING.md @@ -0,0 +1,4 @@ +# Contributing + +This repository is a sub repository of [the JWT Framework](https://github.com/web-token/jwt-framework) project and is READ ONLY. +Please do not submit any Pull Requests here. It will be automatically closed. diff --git a/vendor/web-token/jwt-signature-algorithm-rsa/.github/FUNDING.yml b/vendor/web-token/jwt-signature-algorithm-rsa/.github/FUNDING.yml new file mode 100644 index 000000000..7e2ca0e7e --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-rsa/.github/FUNDING.yml @@ -0,0 +1 @@ +patreon: FlorentMorselli diff --git a/vendor/web-token/jwt-signature-algorithm-rsa/.github/PULL_REQUEST_TEMPLATE.md b/vendor/web-token/jwt-signature-algorithm-rsa/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..d4ff96cb4 --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-rsa/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,3 @@ +Please do not submit any Pull Requests here. It will be automatically closed. + +You should submit it here: https://github.com/web-token/jwt-framework/pulls diff --git a/vendor/web-token/jwt-signature-algorithm-rsa/LICENSE b/vendor/web-token/jwt-signature-algorithm-rsa/LICENSE new file mode 100644 index 000000000..37cf976b1 --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-rsa/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014-2019 Spomky-Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/web-token/jwt-signature-algorithm-rsa/PS256.php b/vendor/web-token/jwt-signature-algorithm-rsa/PS256.php new file mode 100644 index 000000000..dfaabd58e --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-rsa/PS256.php @@ -0,0 +1,27 @@ +checkKey($key); + $pub = RSAKey::createFromJWK($key->toPublic()); + + return JoseRSA::verify($pub, $input, $signature, $this->getAlgorithm(), $this->getSignatureMethod()); + } + + /** + * @throws InvalidArgumentException if the key is not private + */ + public function sign(JWK $key, string $input): string + { + $this->checkKey($key); + if (!$key->has('d')) { + throw new InvalidArgumentException('The key is not a private key.'); + } + + $priv = RSAKey::createFromJWK($key); + + return JoseRSA::sign($priv, $input, $this->getAlgorithm(), $this->getSignatureMethod()); + } + + abstract protected function getAlgorithm(): string; + + abstract protected function getSignatureMethod(): int; + + /** + * @throws InvalidArgumentException if the key type is not allowed + * @throws InvalidArgumentException if the key is invalid + */ + private function checkKey(JWK $key): void + { + if (!in_array($key->get('kty'), $this->allowedKeyTypes(), true)) { + throw new InvalidArgumentException('Wrong key type.'); + } + foreach (['n', 'e'] as $k) { + if (!$key->has($k)) { + throw new InvalidArgumentException(sprintf('The key parameter "%s" is missing.', $k)); + } + } + } +} diff --git a/vendor/web-token/jwt-signature-algorithm-rsa/RSAPKCS1.php b/vendor/web-token/jwt-signature-algorithm-rsa/RSAPKCS1.php new file mode 100644 index 000000000..da2fd8076 --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-rsa/RSAPKCS1.php @@ -0,0 +1,75 @@ +checkKey($key); + $pub = RSAKey::createFromJWK($key->toPublic()); + + return 1 === openssl_verify($input, $signature, $pub->toPEM(), $this->getAlgorithm()); + } + + /** + * @throws InvalidArgumentException if the key is not private + * @throws InvalidArgumentException if the data cannot be signed + */ + public function sign(JWK $key, string $input): string + { + $this->checkKey($key); + if (!$key->has('d')) { + throw new InvalidArgumentException('The key is not a private key.'); + } + + $priv = RSAKey::createFromJWK($key); + + $result = openssl_sign($input, $signature, $priv->toPEM(), $this->getAlgorithm()); + if (true !== $result) { + throw new RuntimeException('Unable to sign'); + } + + return $signature; + } + + abstract protected function getAlgorithm(): string; + + /** + * @throws InvalidArgumentException if the key type is not allowed + * @throws InvalidArgumentException if the key is not valid + */ + private function checkKey(JWK $key): void + { + if (!in_array($key->get('kty'), $this->allowedKeyTypes(), true)) { + throw new InvalidArgumentException('Wrong key type.'); + } + foreach (['n', 'e'] as $k) { + if (!$key->has($k)) { + throw new InvalidArgumentException(sprintf('The key parameter "%s" is missing.', $k)); + } + } + } +} diff --git a/vendor/web-token/jwt-signature-algorithm-rsa/RSAPSS.php b/vendor/web-token/jwt-signature-algorithm-rsa/RSAPSS.php new file mode 100644 index 000000000..9112c417b --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-rsa/RSAPSS.php @@ -0,0 +1,69 @@ +checkKey($key); + $pub = RSAKey::createFromJWK($key->toPublic()); + + return JoseRSA::verify($pub, $input, $signature, $this->getAlgorithm(), JoseRSA::SIGNATURE_PSS); + } + + /** + * @throws InvalidArgumentException if the key is not private + */ + public function sign(JWK $key, string $input): string + { + $this->checkKey($key); + if (!$key->has('d')) { + throw new InvalidArgumentException('The key is not a private key.'); + } + + $priv = RSAKey::createFromJWK($key); + + return JoseRSA::sign($priv, $input, $this->getAlgorithm(), JoseRSA::SIGNATURE_PSS); + } + + abstract protected function getAlgorithm(): string; + + /** + * @throws InvalidArgumentException if the key type is not allowed + * @throws InvalidArgumentException if the key is not valid + */ + private function checkKey(JWK $key): void + { + if (!in_array($key->get('kty'), $this->allowedKeyTypes(), true)) { + throw new InvalidArgumentException('Wrong key type.'); + } + foreach (['n', 'e'] as $k) { + if (!$key->has($k)) { + throw new InvalidArgumentException(sprintf('The key parameter "%s" is missing.', $k)); + } + } + } +} diff --git a/vendor/web-token/jwt-signature-algorithm-rsa/Tests/RSA15SignatureTest.php b/vendor/web-token/jwt-signature-algorithm-rsa/Tests/RSA15SignatureTest.php new file mode 100644 index 000000000..ebee3f4ac --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-rsa/Tests/RSA15SignatureTest.php @@ -0,0 +1,109 @@ + 'RSA', + 'kid' => 'bilbo.baggins@hobbiton.example', + 'use' => 'sig', + 'n' => 'n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw', + 'e' => 'AQAB', + 'd' => 'bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78eiZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRldY7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-bMwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDjd18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOcOpBrQzwQ', + 'p' => '3Slxg_DwTXJcb6095RoXygQCAZ5RnAvZlno1yhHtnUex_fp7AZ_9nRaO7HX_-SFfGQeutao2TDjDAWU4Vupk8rw9JR0AzZ0N2fvuIAmr_WCsmGpeNqQnev1T7IyEsnh8UMt-n5CafhkikzhEsrmndH6LxOrvRJlsPp6Zv8bUq0k', + 'q' => 'uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc', + 'dp' => 'B8PVvXkvJrj2L-GYQ7v3y9r6Kw5g9SahXBwsWUzp19TVlgI-YV85q1NIb1rxQtD-IsXXR3-TanevuRPRt5OBOdiMGQp8pbt26gljYfKU_E9xn-RULHz0-ed9E9gXLKD4VGngpz-PfQ_q29pk5xWHoJp009Qf1HvChixRX59ehik', + 'dq' => 'CLDmDGduhylc9o7r84rEUVn7pzQ6PF83Y-iBZx5NT-TpnOZKF1pErAMVeKzFEl41DlHHqqBLSM0W1sOFbwTxYWZDm6sI6og5iTbwQGIC3gnJKbi_7k_vJgGHwHxgPaX2PnvP-zyEkDERuf-ry4c_Z11Cq9AqC2yeL6kdKT1cYF8', + 'qi' => '3PiqvXQN0zwMeE-sBvZgi289XP9XCQF3VWqPzMKnIgQp7_Tugo6-NZBKCQsMf3HaEGBjTVJs_jcK8-TRXvaKe-7ZMaQj8VfBdYkssbu0NKDDhjJ-GtiseaDVWt7dcH0cfwxgFUHpQh7FoCrjFJ6h6ZEpMF6xmujs4qMpPz8aaI4', + ]); + + /* + * Header + * @see https://tools.ietf.org/html/rfc7520#section-4.1.2 + */ + $header = [ + 'alg' => 'RS256', + 'kid' => 'bilbo.baggins@hobbiton.example', + ]; + + $jwsBuilder = new JWSBuilder( + new AlgorithmManager([new RS256()]) + ); + $jwsVerifier = new JWSVerifier( + new AlgorithmManager([new RS256()]) + ); + $compactSerializer = new Serializer\CompactSerializer( + ); + $jsonFlattenedSerializer = new Serializer\JSONFlattenedSerializer( + ); + $jsonGeneralSerializer = new Serializer\JSONGeneralSerializer( + ); + $jws = $jwsBuilder + ->create()->withPayload($payload) + ->addSignature($privateKey, $header) + ->build() + ; + + /* + * Header + * @see https://tools.ietf.org/html/rfc7520#section-4.1.3 + */ + $expected_compact_json = 'eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhbXBsZSJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4.MRjdkly7_-oTPTS3AXP41iQIGKa80A0ZmTuV5MEaHoxnW2e5CZ5NlKtainoFmKZopdHM1O2U4mwzJdQx996ivp83xuglII7PNDi84wnB-BDkoBwA78185hX-Es4JIwmDLJK3lfWRa-XtL0RnltuYv746iYTh_qHRD68BNt1uSNCrUCTJDt5aAE6x8wW1Kt9eRo4QPocSadnHXFxnt8Is9UzpERV0ePPQdLuW3IS_de3xyIrDaLGdjluPxUAhb6L2aXic1U12podGU0KLUQSE_oI-ZnmKJ3F4uOZDnd6QZWJushZ41Axf_fcIe8u9ipH84ogoree7vjbU5y18kDquDg'; + $expected_flattened_json = '{"payload":"SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4","protected":"eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhbXBsZSJ9","signature":"MRjdkly7_-oTPTS3AXP41iQIGKa80A0ZmTuV5MEaHoxnW2e5CZ5NlKtainoFmKZopdHM1O2U4mwzJdQx996ivp83xuglII7PNDi84wnB-BDkoBwA78185hX-Es4JIwmDLJK3lfWRa-XtL0RnltuYv746iYTh_qHRD68BNt1uSNCrUCTJDt5aAE6x8wW1Kt9eRo4QPocSadnHXFxnt8Is9UzpERV0ePPQdLuW3IS_de3xyIrDaLGdjluPxUAhb6L2aXic1U12podGU0KLUQSE_oI-ZnmKJ3F4uOZDnd6QZWJushZ41Axf_fcIe8u9ipH84ogoree7vjbU5y18kDquDg"}'; + $expected_json = '{"payload":"SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4","signatures":[{"protected":"eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhbXBsZSJ9","signature":"MRjdkly7_-oTPTS3AXP41iQIGKa80A0ZmTuV5MEaHoxnW2e5CZ5NlKtainoFmKZopdHM1O2U4mwzJdQx996ivp83xuglII7PNDi84wnB-BDkoBwA78185hX-Es4JIwmDLJK3lfWRa-XtL0RnltuYv746iYTh_qHRD68BNt1uSNCrUCTJDt5aAE6x8wW1Kt9eRo4QPocSadnHXFxnt8Is9UzpERV0ePPQdLuW3IS_de3xyIrDaLGdjluPxUAhb6L2aXic1U12podGU0KLUQSE_oI-ZnmKJ3F4uOZDnd6QZWJushZ41Axf_fcIe8u9ipH84ogoree7vjbU5y18kDquDg"}]}'; + + static::assertEquals($expected_compact_json, $compactSerializer->serialize($jws, 0)); + + // We decode the json to compare the 2 arrays otherwise the test may fail as the order may be different + static::assertEquals(json_decode($expected_flattened_json, true), json_decode($jsonFlattenedSerializer->serialize($jws, 0), true)); + static::assertEquals(json_decode($expected_json, true), json_decode($jsonGeneralSerializer->serialize($jws, 0), true)); + + $loaded_compact_json = $compactSerializer->unserialize($expected_compact_json); + $jwsVerifier->verifyWithKey($loaded_compact_json, $privateKey, 0); + + $loaded_flattened_json = $jsonFlattenedSerializer->unserialize($expected_flattened_json); + $jwsVerifier->verifyWithKey($loaded_flattened_json, $privateKey, 0); + + $loaded_json = $jsonGeneralSerializer->unserialize($expected_json); + $jwsVerifier->verifyWithKey($loaded_json, $privateKey, 0); + } +} diff --git a/vendor/web-token/jwt-signature-algorithm-rsa/Tests/RSAKeyWithoutAllPrimesTest.php b/vendor/web-token/jwt-signature-algorithm-rsa/Tests/RSAKeyWithoutAllPrimesTest.php new file mode 100644 index 000000000..956af346e --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-rsa/Tests/RSAKeyWithoutAllPrimesTest.php @@ -0,0 +1,97 @@ +getPrivateKey(); + + $claims = json_encode(['foo' => 'bar']); + + $jwsBuilder = new JWSBuilder( + new AlgorithmManager([$algorithm]) + ); + $jwsVerifier = new JWSVerifier( + new AlgorithmManager([$algorithm]) + ); + $serializer = new CompactSerializer( + ); + $jws = $jwsBuilder + ->create()->withPayload($claims) + ->addSignature($key, ['alg' => $algorithm->name()]) + ->build() + ; + $jws = $serializer->serialize($jws, 0); + + $loaded = $serializer->unserialize($jws); + + static::assertTrue($jwsVerifier->verifyWithKey($loaded, $key, 0)); + } + + public function dataSignatureAlgorithms(): array + { + return [ + [Algorithm\RS256::class], + [Algorithm\RS384::class], + [Algorithm\RS512::class], + [Algorithm\PS256::class], + [Algorithm\PS384::class], + [Algorithm\PS512::class], + ]; + } + + public function dataSignatureAlgorithmsWithSimpleKey(): array + { + return [ + [Algorithm\PS256::class], + [Algorithm\PS384::class], + [Algorithm\PS512::class], + ]; + } + + private function getPrivateKey(): JWK + { + return new JWK([ + 'kty' => 'RSA', + 'kid' => 'private', + 'n' => '2NRPORHXd7wPU6atHqmSfWgEPvsP8HVUkY2AwQQAc8x1J509X5HFxeSXnQym9eAnZHl0JCPbvHoPH4QHlvITYoh0MSgFm2aOPyqOD-XcNdKWtnNX2JIurUCyVlwSwtlmy2ZbCz8YuUmFO0iacahfK1wbWT5QoY-pU3UxnMzDhlBslZN5uL7nRE8Sh_8BthsrMdYeGIMY55kh-P7xTs3MHzpOKhFSrOhdN6aO3HWYUuMAdoMNB-hJvckb2PbCy0_K1Wm3SBHtXn-cuMIUF00W9AR3amp3u3hLa2rcz29jEFXTr2FxKyLH4SdlnFFMJl2vaXuxM4PXgLN33Kj34PfKgc8ljDJ7oaSI9bKt7gunXOLv_o4XWYDq91cvUkOIDAsvqxzzHPZBt0Hru7roW3btkUOiqR6RWy-Cw272yiSEC5QA93m_vklD1KajoFeWN0BW2lWGlfGieZldvKX0sumk1TZuLhlHPHSKYcpeCfahT-jLr1yAeHql6qRN_a0BiHu-SSSjts6InmF1pAELznZ3Jn9-QXX78LsY3xaqOlYqHbCohxXorlYRi4so6eMGILtXjqHOoISb13Ez4YNOQmV4ygmyABRkE0AQG5KLy5cZB7LZn7zqw869UjXxWrmiOaBeDqOkxww6qiWIEDwPIouRLwOfPFtC4LGlb9LmG9Hlhp8', + 'e' => 'AQAB', + 'd' => 'PsMls2VAsz3SSepjDg8Tgg1LvVc6w-WSdxc4f6ZC40H5X2AaVcGCN8f1QtZYta8Od_zX62Ydwq6qFftHnx-vEMRirZ_iD5td7VbKDDwCw-mTCnjUorGdpTSm6mx4WcJICPQ1wkmfRHLNh916JxAPjCN7Hxf0iu9kme3AUJzMs-IvrBQmFZ3cn18sBAWCX0358NEDoSDBYrhmpwZUnvTe8uMToQWmoroX0XX6wEGht8xRY_yHFxTb032U-_ZhaCxOj_uru8bEqKfTm39CBYSg8j0gu8LZqYAmhI9IHxsk16OgRJG2CkBlDv0yYk799dUEY0oUfs7Y4D4SoeKe7ZWMHgKMEqa7ONz18ORznxqKSQhi4hfNVgwMzaM0IoYP4KOfHuaK263zhJU0hMzURJ8KifECeOsDHBR6BhLJ9TYzUe4c9UU55nFNgRBwknKHFFrRAsgVETEzmZWHzWwGQIFtKIAVZ1cjkdMEL3BlbzzXVofXfbbCrPQqcABYx2BZ-J_P8-UFjeMo83VLrR5IHj0_8IhQZUmxZYJcpTIwrf-1A4JGlN2_eLqRymF8tZI6zIPJyo1C0M1CIB3EeHzi-70SbF8xFtGUB7hR234yo_SM-KqVdIk2Sjjta2bQ1KXjSEcvrS_358AMiP0-9JT_fHxTCyzra-SNYoZhdnrEFzoVwQE', + 'p' => '6fWvnj34kJtfMnO1j-qbPjFnaTevREBGAypMvUBU3Fx1Xx0nE7zdc7lln2Qq5-yTQtOQ2lpiE69HkQLR4pMU6V44SjFgVzcTzbFCnNgknEV54S5dyp4KojSWxBi6bt5GwaACkiElDEw9wgc-8JgaEkv4F7e-w44HBwPDECTjE_N0vIawpbD_y6zpifB8ziaAI3xTG4ssA1dt8WZuyQW8SR4FRsYnfkqy0twwHn02gs7XSl4NepkhSO7CY5-YC3U6LazAEZi2NTiUuZSw7F6KaRhsA8CnXTDE5JqFks_fXfLNCbtClON2JtrB1zY-l-2bHyh2a6unDtGn9ZN-Ec7BXw', + 'q' => '7UF_NblAyTxmj7Z2Jz1sZmz-Q3YHOcta00DjmHBhR9ItYRMQFMj-SUGPAtwvN-sk3_ThugaQt46SLT_I3Gy8433cHdW7o3So6HiMYVunyfhqnWznSWs6SvIoEh8rJOXkkIZ-DlRP8XyW5OOvi0cbWEQ1f1jbFyistMmnBClPvf2TKKPvShUl9qmvLxuU87j-_bgQmjVmtwZadnPOyPAxQ4_qqSfIiTOvMSxSycr58rTyu3khHQapGHkS5-2Y_w40GUSfVJ3XP48delYpK-PZP71hn89MJTnnfPOtvJAk1wbEev5wQFTJd-PGOudkGkuEIXryF4TGxRPltl5UeF0CwQ', + ]); + } +} diff --git a/vendor/web-token/jwt-signature-algorithm-rsa/Tests/RSAPSSSignatureTest.php b/vendor/web-token/jwt-signature-algorithm-rsa/Tests/RSAPSSSignatureTest.php new file mode 100644 index 000000000..03a82e4a7 --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-rsa/Tests/RSAPSSSignatureTest.php @@ -0,0 +1,110 @@ + 'RSA', + 'kid' => 'bilbo.baggins@hobbiton.example', + 'use' => 'sig', + 'n' => 'n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw', + 'e' => 'AQAB', + 'd' => 'bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78eiZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRldY7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-bMwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDjd18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOcOpBrQzwQ', + 'p' => '3Slxg_DwTXJcb6095RoXygQCAZ5RnAvZlno1yhHtnUex_fp7AZ_9nRaO7HX_-SFfGQeutao2TDjDAWU4Vupk8rw9JR0AzZ0N2fvuIAmr_WCsmGpeNqQnev1T7IyEsnh8UMt-n5CafhkikzhEsrmndH6LxOrvRJlsPp6Zv8bUq0k', + 'q' => 'uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc', + 'dp' => 'B8PVvXkvJrj2L-GYQ7v3y9r6Kw5g9SahXBwsWUzp19TVlgI-YV85q1NIb1rxQtD-IsXXR3-TanevuRPRt5OBOdiMGQp8pbt26gljYfKU_E9xn-RULHz0-ed9E9gXLKD4VGngpz-PfQ_q29pk5xWHoJp009Qf1HvChixRX59ehik', + 'dq' => 'CLDmDGduhylc9o7r84rEUVn7pzQ6PF83Y-iBZx5NT-TpnOZKF1pErAMVeKzFEl41DlHHqqBLSM0W1sOFbwTxYWZDm6sI6og5iTbwQGIC3gnJKbi_7k_vJgGHwHxgPaX2PnvP-zyEkDERuf-ry4c_Z11Cq9AqC2yeL6kdKT1cYF8', + 'qi' => '3PiqvXQN0zwMeE-sBvZgi289XP9XCQF3VWqPzMKnIgQp7_Tugo6-NZBKCQsMf3HaEGBjTVJs_jcK8-TRXvaKe-7ZMaQj8VfBdYkssbu0NKDDhjJ-GtiseaDVWt7dcH0cfwxgFUHpQh7FoCrjFJ6h6ZEpMF6xmujs4qMpPz8aaI4', + ]); + + /* + * Header + * @see https://tools.ietf.org/html/rfc7520#section-4.2.2 + */ + $header = [ + 'alg' => 'PS384', + 'kid' => 'bilbo.baggins@hobbiton.example', + ]; + + $jwsBuilder = new JWSBuilder( + new AlgorithmManager([new PS384()]) + ); + $jwsVerifier = new JWSVerifier( + new AlgorithmManager([new PS384()]) + ); + $compactSerializer = new Serializer\CompactSerializer( + ); + $jsonFlattenedSerializer = new Serializer\JSONFlattenedSerializer( + ); + $jsonGeneralSerializer = new Serializer\JSONGeneralSerializer( + ); + $jws = $jwsBuilder + ->create()->withPayload($payload) + ->addSignature($privateKey, $header) + ->build() + ; + + $jwsVerifier->verifyWithKey($jws, $privateKey, 0); + + /* + * Header + * @see https://tools.ietf.org/html/rfc7520#section-4.2.3 + */ + $expected_compact_json = 'eyJhbGciOiJQUzM4NCIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhbXBsZSJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4.cu22eBqkYDKgIlTpzDXGvaFfz6WGoz7fUDcfT0kkOy42miAh2qyBzk1xEsnk2IpN6-tPid6VrklHkqsGqDqHCdP6O8TTB5dDDItllVo6_1OLPpcbUrhiUSMxbbXUvdvWXzg-UD8biiReQFlfz28zGWVsdiNAUf8ZnyPEgVFn442ZdNqiVJRmBqrYRXe8P_ijQ7p8Vdz0TTrxUeT3lm8d9shnr2lfJT8ImUjvAA2Xez2Mlp8cBE5awDzT0qI0n6uiP1aCN_2_jLAeQTlqRHtfa64QQSUmFAAjVKPbByi7xho0uTOcbH510a6GYmJUAfmWjwZ6oD4ifKo8DYM-X72Eaw'; + $expected_flattened_json = '{"payload":"SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4","protected":"eyJhbGciOiJQUzM4NCIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhbXBsZSJ9","signature":"cu22eBqkYDKgIlTpzDXGvaFfz6WGoz7fUDcfT0kkOy42miAh2qyBzk1xEsnk2IpN6-tPid6VrklHkqsGqDqHCdP6O8TTB5dDDItllVo6_1OLPpcbUrhiUSMxbbXUvdvWXzg-UD8biiReQFlfz28zGWVsdiNAUf8ZnyPEgVFn442ZdNqiVJRmBqrYRXe8P_ijQ7p8Vdz0TTrxUeT3lm8d9shnr2lfJT8ImUjvAA2Xez2Mlp8cBE5awDzT0qI0n6uiP1aCN_2_jLAeQTlqRHtfa64QQSUmFAAjVKPbByi7xho0uTOcbH510a6GYmJUAfmWjwZ6oD4ifKo8DYM-X72Eaw"}'; + $expected_json = '{"payload":"SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4","signatures":[{"protected":"eyJhbGciOiJQUzM4NCIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhbXBsZSJ9","signature":"cu22eBqkYDKgIlTpzDXGvaFfz6WGoz7fUDcfT0kkOy42miAh2qyBzk1xEsnk2IpN6-tPid6VrklHkqsGqDqHCdP6O8TTB5dDDItllVo6_1OLPpcbUrhiUSMxbbXUvdvWXzg-UD8biiReQFlfz28zGWVsdiNAUf8ZnyPEgVFn442ZdNqiVJRmBqrYRXe8P_ijQ7p8Vdz0TTrxUeT3lm8d9shnr2lfJT8ImUjvAA2Xez2Mlp8cBE5awDzT0qI0n6uiP1aCN_2_jLAeQTlqRHtfa64QQSUmFAAjVKPbByi7xho0uTOcbH510a6GYmJUAfmWjwZ6oD4ifKo8DYM-X72Eaw"}]}'; + + $loaded_compact_json = $compactSerializer->unserialize($expected_compact_json); + static::assertTrue($jwsVerifier->verifyWithKey($loaded_compact_json, $privateKey, 0)); + + $loaded_flattened_json = $jsonFlattenedSerializer->unserialize($expected_flattened_json); + static::assertTrue($jwsVerifier->verifyWithKey($loaded_flattened_json, $privateKey, 0)); + + $loaded_json = $jsonGeneralSerializer->unserialize($expected_json); + static::assertTrue($jwsVerifier->verifyWithKey($loaded_json, $privateKey, 0)); + } +} diff --git a/vendor/web-token/jwt-signature-algorithm-rsa/Tests/RSASignatureTest.php b/vendor/web-token/jwt-signature-algorithm-rsa/Tests/RSASignatureTest.php new file mode 100644 index 000000000..e91dbe47b --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-rsa/Tests/RSASignatureTest.php @@ -0,0 +1,599 @@ + 'RSA', + 'n' => 'tpS1ZmfVKVP5KofIhMBP0tSWc4qlh6fm2lrZSkuKxUjEaWjzZSzs72gEIGxraWusMdoRuV54xsWRyf5KeZT0S-I5Prle3Idi3gICiO4NwvMk6JwSBcJWwmSLFEKyUSnB2CtfiGc0_5rQCpcEt_Dn5iM-BNn7fqpoLIbks8rXKUIj8-qMVqkTXsEKeKinE23t1ykMldsNaaOH-hvGti5Jt2DMnH1JjoXdDXfxvSP_0gjUYb0ektudYFXoA6wekmQyJeImvgx4Myz1I4iHtkY_Cp7J4Mn1ejZ6HNmyvoTE_4OuY1uCeYv4UyXFc1s1uUyYtj4z57qsHGsS4dQ3A2MJsw', + 'e' => 'AQAB', + 'p' => '5BGU1c7af_5sFyfsa-onIJgo5BZu8uHvz3Uyb8OA0a-G9UPO1ShLYjX0wUfhZcFB7fwPtgmmYAN6wKGVce9eMAbX4PliPk3r-BcpZuPKkuLk_wFvgWAQ5Hqw2iEuwXLV0_e8c2gaUt_hyMC5-nFc4v0Bmv6NT6Pfry-UrK3BKWc', + 'd' => 'Kp0KuZwCZGL1BLgsVM-N0edMNitl9wN5Hf2WOYDoIqOZNAEKzdJuenIMhITJjRFUX05GVL138uyp2js_pqDdY9ipA7rAKThwGuDdNphZHech9ih3DGEPXs-YpmHqvIbCd3GoGm38MKwxYkddEpFnjo8rKna1_BpJthrFxjDRhw9DxJBycOdH2yWTyp62ZENPvneK40H2a57W4QScTgfecZqD59m2fGUaWaX5uUmIxaEmtGoJnd9RE4oywKhgN7_TK7wXRlqA4UoRPiH2ACrdU-_cLQL9Jc0u0GqZJK31LDbOeN95QgtSCc72k3Vtzy3CrVpp5TAA67s1Gj9Skn-CAQ', + 'q' => 'zPD-B-nrngwF-O99BHvb47XGKR7ON8JCI6JxavzIkusMXCB8rMyYW8zLs68L8JLAzWZ34oMq0FPUnysBxc5nTF8Nb4BZxTZ5-9cHfoKrYTI3YWsmVW2FpCJFEjMs4NXZ28PBkS9b4zjfS2KhNdkmCeOYU0tJpNfwmOTI90qeUdU', + 'dp' => 'aJrzw_kjWK9uDlTeaES2e4muv6bWbopYfrPHVWG7NPGoGdhnBnd70-jhgMEiTZSNU8VXw2u7prAR3kZ-kAp1DdwlqedYOzFsOJcPA0UZhbORyrBy30kbll_7u6CanFm6X4VyJxCpejd7jKNw6cCTFP1sfhWg5NVJ5EUTkPwE66M', + 'dq' => 'Swz1-m_vmTFN_pu1bK7vF7S5nNVrL4A0OFiEsGliCmuJWzOKdL14DiYxctvnw3H6qT2dKZZfV2tbse5N9-JecdldUjfuqAoLIe7dD7dKi42YOlTC9QXmqvTh1ohnJu8pmRFXEZQGUm_BVhoIb2_WPkjav6YSkguCUHt4HRd2YwE', + 'qi' => 'BocuCOEOq-oyLDALwzMXU8gOf3IL1Q1_BWwsdoANoh6i179psxgE4JXToWcpXZQQqub8ngwE6uR9fpd3m6N_PL4T55vbDDyjPKmrL2ttC2gOtx9KrpPh-Z7LQRo4BE48nHJJrystKHfFlaH2G7JxHNgMBYVADyttN09qEoav8Os', + ]); + + $jwsBuilder = new JWSBuilder( + new AlgorithmManager([new Algorithm\RS256()]) + ); + $serializer = new CompactSerializer( + ); + $jws = $jwsBuilder + ->create()->withPayload('Live long and Prosper.') + ->addSignature( + $key, + [ + 'alg' => 'RS256', + 'jwk' => [ + 'kty' => 'RSA', + 'n' => 'tpS1ZmfVKVP5KofIhMBP0tSWc4qlh6fm2lrZSkuKxUjEaWjzZSzs72gEIGxraWusMdoRuV54xsWRyf5KeZT0S-I5Prle3Idi3gICiO4NwvMk6JwSBcJWwmSLFEKyUSnB2CtfiGc0_5rQCpcEt_Dn5iM-BNn7fqpoLIbks8rXKUIj8-qMVqkTXsEKeKinE23t1ykMldsNaaOH-hvGti5Jt2DMnH1JjoXdDXfxvSP_0gjUYb0ektudYFXoA6wekmQyJeImvgx4Myz1I4iHtkY_Cp7J4Mn1ejZ6HNmyvoTE_4OuY1uCeYv4UyXFc1s1uUyYtj4z57qsHGsS4dQ3A2MJsw', + 'e' => 'AQAB', + ], + ] + ) + ->build() + ; + + static::assertEquals('eyJhbGciOiJSUzI1NiIsImp3ayI6eyJrdHkiOiJSU0EiLCJuIjoidHBTMVptZlZLVlA1S29mSWhNQlAwdFNXYzRxbGg2Zm0ybHJaU2t1S3hVakVhV2p6WlN6czcyZ0VJR3hyYVd1c01kb1J1VjU0eHNXUnlmNUtlWlQwUy1JNVBybGUzSWRpM2dJQ2lPNE53dk1rNkp3U0JjSld3bVNMRkVLeVVTbkIyQ3RmaUdjMF81clFDcGNFdF9EbjVpTS1CTm43ZnFwb0xJYmtzOHJYS1VJajgtcU1WcWtUWHNFS2VLaW5FMjN0MXlrTWxkc05hYU9ILWh2R3RpNUp0MkRNbkgxSmpvWGREWGZ4dlNQXzBnalVZYjBla3R1ZFlGWG9BNndla21ReUplSW12Z3g0TXl6MUk0aUh0a1lfQ3A3SjRNbjFlalo2SE5teXZvVEVfNE91WTF1Q2VZdjRVeVhGYzFzMXVVeVl0ajR6NTdxc0hHc1M0ZFEzQTJNSnN3IiwiZSI6IkFRQUIifX0.TGl2ZSBsb25nIGFuZCBQcm9zcGVyLg.QyRlOCcNBMvCEkJRCQA71y2bVX690g0A6wsC2YXf9_VxOYK-g9-xy-1KjghVXkDPe1gDvYSYnL9oWs1PaFKV0_-ijvvJQE6_5pheKTfIVN3Qbkzjxsm4qXTeChBI5MKeBR8z8iWLFT4xPO8NkelwbS2tSUCHrejio6lDDlWhsqSUP8NjHJhqCSZuCDGu3fMMA24cZrYev3tQRc7HHjyi3q_17NZri7feBd7w3NEDkJp7wT_ZclJrYoucHIo1ypaDPJtM-W1-W-lAVREka6Xq4Bg60zdSZ83ODRQTP_IwQrv7hrIcbrRwn1Za_ORZPRPQDP0CMgkb7TkWDZnbPsAzlQ', $serializer->serialize($jws, 0)); + $jws = $serializer->serialize($jws, 0); + $result = $serializer->unserialize($jws); + + static::assertEquals('Live long and Prosper.', $result->getPayload()); + static::assertEquals(1, $result->countSignatures()); + static::assertEquals('RS256', $result->getSignature(0)->getProtectedHeaderParameter('alg')); + } + + /** + * @test + */ + public function completeRS256SignWithDetachedPayload(): void + { + $key = new JWK([ + 'kty' => 'RSA', + 'n' => 'tpS1ZmfVKVP5KofIhMBP0tSWc4qlh6fm2lrZSkuKxUjEaWjzZSzs72gEIGxraWusMdoRuV54xsWRyf5KeZT0S-I5Prle3Idi3gICiO4NwvMk6JwSBcJWwmSLFEKyUSnB2CtfiGc0_5rQCpcEt_Dn5iM-BNn7fqpoLIbks8rXKUIj8-qMVqkTXsEKeKinE23t1ykMldsNaaOH-hvGti5Jt2DMnH1JjoXdDXfxvSP_0gjUYb0ektudYFXoA6wekmQyJeImvgx4Myz1I4iHtkY_Cp7J4Mn1ejZ6HNmyvoTE_4OuY1uCeYv4UyXFc1s1uUyYtj4z57qsHGsS4dQ3A2MJsw', + 'e' => 'AQAB', + 'p' => '5BGU1c7af_5sFyfsa-onIJgo5BZu8uHvz3Uyb8OA0a-G9UPO1ShLYjX0wUfhZcFB7fwPtgmmYAN6wKGVce9eMAbX4PliPk3r-BcpZuPKkuLk_wFvgWAQ5Hqw2iEuwXLV0_e8c2gaUt_hyMC5-nFc4v0Bmv6NT6Pfry-UrK3BKWc', + 'd' => 'Kp0KuZwCZGL1BLgsVM-N0edMNitl9wN5Hf2WOYDoIqOZNAEKzdJuenIMhITJjRFUX05GVL138uyp2js_pqDdY9ipA7rAKThwGuDdNphZHech9ih3DGEPXs-YpmHqvIbCd3GoGm38MKwxYkddEpFnjo8rKna1_BpJthrFxjDRhw9DxJBycOdH2yWTyp62ZENPvneK40H2a57W4QScTgfecZqD59m2fGUaWaX5uUmIxaEmtGoJnd9RE4oywKhgN7_TK7wXRlqA4UoRPiH2ACrdU-_cLQL9Jc0u0GqZJK31LDbOeN95QgtSCc72k3Vtzy3CrVpp5TAA67s1Gj9Skn-CAQ', + 'q' => 'zPD-B-nrngwF-O99BHvb47XGKR7ON8JCI6JxavzIkusMXCB8rMyYW8zLs68L8JLAzWZ34oMq0FPUnysBxc5nTF8Nb4BZxTZ5-9cHfoKrYTI3YWsmVW2FpCJFEjMs4NXZ28PBkS9b4zjfS2KhNdkmCeOYU0tJpNfwmOTI90qeUdU', + 'dp' => 'aJrzw_kjWK9uDlTeaES2e4muv6bWbopYfrPHVWG7NPGoGdhnBnd70-jhgMEiTZSNU8VXw2u7prAR3kZ-kAp1DdwlqedYOzFsOJcPA0UZhbORyrBy30kbll_7u6CanFm6X4VyJxCpejd7jKNw6cCTFP1sfhWg5NVJ5EUTkPwE66M', + 'dq' => 'Swz1-m_vmTFN_pu1bK7vF7S5nNVrL4A0OFiEsGliCmuJWzOKdL14DiYxctvnw3H6qT2dKZZfV2tbse5N9-JecdldUjfuqAoLIe7dD7dKi42YOlTC9QXmqvTh1ohnJu8pmRFXEZQGUm_BVhoIb2_WPkjav6YSkguCUHt4HRd2YwE', + 'qi' => 'BocuCOEOq-oyLDALwzMXU8gOf3IL1Q1_BWwsdoANoh6i179psxgE4JXToWcpXZQQqub8ngwE6uR9fpd3m6N_PL4T55vbDDyjPKmrL2ttC2gOtx9KrpPh-Z7LQRo4BE48nHJJrystKHfFlaH2G7JxHNgMBYVADyttN09qEoav8Os', + ]); + + $jwsBuilder = new JWSBuilder( + new AlgorithmManager([new Algorithm\RS256()]) + ); + $serializer = new CompactSerializer( + ); + $jws = $jwsBuilder + ->create()->withPayload('Live long and Prosper.', true) + ->addSignature( + $key, + [ + 'alg' => 'RS256', + 'jwk' => [ + 'kty' => 'RSA', + 'n' => 'tpS1ZmfVKVP5KofIhMBP0tSWc4qlh6fm2lrZSkuKxUjEaWjzZSzs72gEIGxraWusMdoRuV54xsWRyf5KeZT0S-I5Prle3Idi3gICiO4NwvMk6JwSBcJWwmSLFEKyUSnB2CtfiGc0_5rQCpcEt_Dn5iM-BNn7fqpoLIbks8rXKUIj8-qMVqkTXsEKeKinE23t1ykMldsNaaOH-hvGti5Jt2DMnH1JjoXdDXfxvSP_0gjUYb0ektudYFXoA6wekmQyJeImvgx4Myz1I4iHtkY_Cp7J4Mn1ejZ6HNmyvoTE_4OuY1uCeYv4UyXFc1s1uUyYtj4z57qsHGsS4dQ3A2MJsw', + 'e' => 'AQAB', + ], + ] + ) + ->build() + ; + + static::assertEquals('eyJhbGciOiJSUzI1NiIsImp3ayI6eyJrdHkiOiJSU0EiLCJuIjoidHBTMVptZlZLVlA1S29mSWhNQlAwdFNXYzRxbGg2Zm0ybHJaU2t1S3hVakVhV2p6WlN6czcyZ0VJR3hyYVd1c01kb1J1VjU0eHNXUnlmNUtlWlQwUy1JNVBybGUzSWRpM2dJQ2lPNE53dk1rNkp3U0JjSld3bVNMRkVLeVVTbkIyQ3RmaUdjMF81clFDcGNFdF9EbjVpTS1CTm43ZnFwb0xJYmtzOHJYS1VJajgtcU1WcWtUWHNFS2VLaW5FMjN0MXlrTWxkc05hYU9ILWh2R3RpNUp0MkRNbkgxSmpvWGREWGZ4dlNQXzBnalVZYjBla3R1ZFlGWG9BNndla21ReUplSW12Z3g0TXl6MUk0aUh0a1lfQ3A3SjRNbjFlalo2SE5teXZvVEVfNE91WTF1Q2VZdjRVeVhGYzFzMXVVeVl0ajR6NTdxc0hHc1M0ZFEzQTJNSnN3IiwiZSI6IkFRQUIifX0..QyRlOCcNBMvCEkJRCQA71y2bVX690g0A6wsC2YXf9_VxOYK-g9-xy-1KjghVXkDPe1gDvYSYnL9oWs1PaFKV0_-ijvvJQE6_5pheKTfIVN3Qbkzjxsm4qXTeChBI5MKeBR8z8iWLFT4xPO8NkelwbS2tSUCHrejio6lDDlWhsqSUP8NjHJhqCSZuCDGu3fMMA24cZrYev3tQRc7HHjyi3q_17NZri7feBd7w3NEDkJp7wT_ZclJrYoucHIo1ypaDPJtM-W1-W-lAVREka6Xq4Bg60zdSZ83ODRQTP_IwQrv7hrIcbrRwn1Za_ORZPRPQDP0CMgkb7TkWDZnbPsAzlQ', $serializer->serialize($jws, 0)); + $jws = $serializer->serialize($jws, 0); + $result = $serializer->unserialize($jws); + + static::assertNull($result->getPayload()); + static::assertEquals(1, $result->countSignatures()); + static::assertEquals('RS256', $result->getSignature(0)->getProtectedHeaderParameter('alg')); + } + + /** + * @test + */ + public function completeRS384Sign(): void + { + $key = new JWK([ + 'kty' => 'RSA', + 'n' => 'tpS1ZmfVKVP5KofIhMBP0tSWc4qlh6fm2lrZSkuKxUjEaWjzZSzs72gEIGxraWusMdoRuV54xsWRyf5KeZT0S-I5Prle3Idi3gICiO4NwvMk6JwSBcJWwmSLFEKyUSnB2CtfiGc0_5rQCpcEt_Dn5iM-BNn7fqpoLIbks8rXKUIj8-qMVqkTXsEKeKinE23t1ykMldsNaaOH-hvGti5Jt2DMnH1JjoXdDXfxvSP_0gjUYb0ektudYFXoA6wekmQyJeImvgx4Myz1I4iHtkY_Cp7J4Mn1ejZ6HNmyvoTE_4OuY1uCeYv4UyXFc1s1uUyYtj4z57qsHGsS4dQ3A2MJsw', + 'e' => 'AQAB', + 'p' => '5BGU1c7af_5sFyfsa-onIJgo5BZu8uHvz3Uyb8OA0a-G9UPO1ShLYjX0wUfhZcFB7fwPtgmmYAN6wKGVce9eMAbX4PliPk3r-BcpZuPKkuLk_wFvgWAQ5Hqw2iEuwXLV0_e8c2gaUt_hyMC5-nFc4v0Bmv6NT6Pfry-UrK3BKWc', + 'd' => 'Kp0KuZwCZGL1BLgsVM-N0edMNitl9wN5Hf2WOYDoIqOZNAEKzdJuenIMhITJjRFUX05GVL138uyp2js_pqDdY9ipA7rAKThwGuDdNphZHech9ih3DGEPXs-YpmHqvIbCd3GoGm38MKwxYkddEpFnjo8rKna1_BpJthrFxjDRhw9DxJBycOdH2yWTyp62ZENPvneK40H2a57W4QScTgfecZqD59m2fGUaWaX5uUmIxaEmtGoJnd9RE4oywKhgN7_TK7wXRlqA4UoRPiH2ACrdU-_cLQL9Jc0u0GqZJK31LDbOeN95QgtSCc72k3Vtzy3CrVpp5TAA67s1Gj9Skn-CAQ', + 'q' => 'zPD-B-nrngwF-O99BHvb47XGKR7ON8JCI6JxavzIkusMXCB8rMyYW8zLs68L8JLAzWZ34oMq0FPUnysBxc5nTF8Nb4BZxTZ5-9cHfoKrYTI3YWsmVW2FpCJFEjMs4NXZ28PBkS9b4zjfS2KhNdkmCeOYU0tJpNfwmOTI90qeUdU', + 'dp' => 'aJrzw_kjWK9uDlTeaES2e4muv6bWbopYfrPHVWG7NPGoGdhnBnd70-jhgMEiTZSNU8VXw2u7prAR3kZ-kAp1DdwlqedYOzFsOJcPA0UZhbORyrBy30kbll_7u6CanFm6X4VyJxCpejd7jKNw6cCTFP1sfhWg5NVJ5EUTkPwE66M', + 'dq' => 'Swz1-m_vmTFN_pu1bK7vF7S5nNVrL4A0OFiEsGliCmuJWzOKdL14DiYxctvnw3H6qT2dKZZfV2tbse5N9-JecdldUjfuqAoLIe7dD7dKi42YOlTC9QXmqvTh1ohnJu8pmRFXEZQGUm_BVhoIb2_WPkjav6YSkguCUHt4HRd2YwE', + 'qi' => 'BocuCOEOq-oyLDALwzMXU8gOf3IL1Q1_BWwsdoANoh6i179psxgE4JXToWcpXZQQqub8ngwE6uR9fpd3m6N_PL4T55vbDDyjPKmrL2ttC2gOtx9KrpPh-Z7LQRo4BE48nHJJrystKHfFlaH2G7JxHNgMBYVADyttN09qEoav8Os', + ]); + + $jwsBuilder = new JWSBuilder( + new AlgorithmManager([new Algorithm\RS384()]) + ); + $serializer = new CompactSerializer( + ); + $jws = $jwsBuilder + ->create()->withPayload('Live long and Prosper.') + ->addSignature( + $key, + [ + 'alg' => 'RS384', + 'jwk' => [ + 'kty' => 'RSA', + 'n' => 'tpS1ZmfVKVP5KofIhMBP0tSWc4qlh6fm2lrZSkuKxUjEaWjzZSzs72gEIGxraWusMdoRuV54xsWRyf5KeZT0S-I5Prle3Idi3gICiO4NwvMk6JwSBcJWwmSLFEKyUSnB2CtfiGc0_5rQCpcEt_Dn5iM-BNn7fqpoLIbks8rXKUIj8-qMVqkTXsEKeKinE23t1ykMldsNaaOH-hvGti5Jt2DMnH1JjoXdDXfxvSP_0gjUYb0ektudYFXoA6wekmQyJeImvgx4Myz1I4iHtkY_Cp7J4Mn1ejZ6HNmyvoTE_4OuY1uCeYv4UyXFc1s1uUyYtj4z57qsHGsS4dQ3A2MJsw', + 'e' => 'AQAB', + ], + ] + ) + ->build() + ; + + static::assertEquals('eyJhbGciOiJSUzM4NCIsImp3ayI6eyJrdHkiOiJSU0EiLCJuIjoidHBTMVptZlZLVlA1S29mSWhNQlAwdFNXYzRxbGg2Zm0ybHJaU2t1S3hVakVhV2p6WlN6czcyZ0VJR3hyYVd1c01kb1J1VjU0eHNXUnlmNUtlWlQwUy1JNVBybGUzSWRpM2dJQ2lPNE53dk1rNkp3U0JjSld3bVNMRkVLeVVTbkIyQ3RmaUdjMF81clFDcGNFdF9EbjVpTS1CTm43ZnFwb0xJYmtzOHJYS1VJajgtcU1WcWtUWHNFS2VLaW5FMjN0MXlrTWxkc05hYU9ILWh2R3RpNUp0MkRNbkgxSmpvWGREWGZ4dlNQXzBnalVZYjBla3R1ZFlGWG9BNndla21ReUplSW12Z3g0TXl6MUk0aUh0a1lfQ3A3SjRNbjFlalo2SE5teXZvVEVfNE91WTF1Q2VZdjRVeVhGYzFzMXVVeVl0ajR6NTdxc0hHc1M0ZFEzQTJNSnN3IiwiZSI6IkFRQUIifX0.TGl2ZSBsb25nIGFuZCBQcm9zcGVyLg.gsBhyBOEDPpHBR8OM2Xb5tybKGeijREZN-smEkvI2188pytujFevbDQJ10afbcdjh5LNKO7U_VD3hGPrC7MIkdtJw4c2d0JnVyhiZT5sFnncnCFjll-Y9GkK7a7jWJJTgF_5LmVEeJSFEEgwT1Stxb-TtZCGqc5ExYizLiuQ2IGB6Sq-hTkpWAXJfmHchE_TxV9A4iLWCMTVM6LsLV6NzDtf2a0iu9XvN1MEdzqM7FNdqNCGN43FveTA0hX8OoFfB2ZjYAjbixUCT4VVI2PuuRyu_Lr8cA73eisolBQLQemPyrCo1s560v2tKD7ICS8Teo1PCJ4HnCuO8bvufI2dKA', $serializer->serialize($jws, 0)); + $jws = $serializer->serialize($jws, 0); + $result = $serializer->unserialize($jws); + + static::assertEquals('Live long and Prosper.', $result->getPayload()); + static::assertEquals(1, $result->countSignatures()); + static::assertEquals('RS384', $result->getSignature(0)->getProtectedHeaderParameter('alg')); + } + + /** + * @test + */ + public function completeRS512Sign(): void + { + $key = new JWK([ + 'kty' => 'RSA', + 'n' => 'tpS1ZmfVKVP5KofIhMBP0tSWc4qlh6fm2lrZSkuKxUjEaWjzZSzs72gEIGxraWusMdoRuV54xsWRyf5KeZT0S-I5Prle3Idi3gICiO4NwvMk6JwSBcJWwmSLFEKyUSnB2CtfiGc0_5rQCpcEt_Dn5iM-BNn7fqpoLIbks8rXKUIj8-qMVqkTXsEKeKinE23t1ykMldsNaaOH-hvGti5Jt2DMnH1JjoXdDXfxvSP_0gjUYb0ektudYFXoA6wekmQyJeImvgx4Myz1I4iHtkY_Cp7J4Mn1ejZ6HNmyvoTE_4OuY1uCeYv4UyXFc1s1uUyYtj4z57qsHGsS4dQ3A2MJsw', + 'e' => 'AQAB', + 'p' => '5BGU1c7af_5sFyfsa-onIJgo5BZu8uHvz3Uyb8OA0a-G9UPO1ShLYjX0wUfhZcFB7fwPtgmmYAN6wKGVce9eMAbX4PliPk3r-BcpZuPKkuLk_wFvgWAQ5Hqw2iEuwXLV0_e8c2gaUt_hyMC5-nFc4v0Bmv6NT6Pfry-UrK3BKWc', + 'd' => 'Kp0KuZwCZGL1BLgsVM-N0edMNitl9wN5Hf2WOYDoIqOZNAEKzdJuenIMhITJjRFUX05GVL138uyp2js_pqDdY9ipA7rAKThwGuDdNphZHech9ih3DGEPXs-YpmHqvIbCd3GoGm38MKwxYkddEpFnjo8rKna1_BpJthrFxjDRhw9DxJBycOdH2yWTyp62ZENPvneK40H2a57W4QScTgfecZqD59m2fGUaWaX5uUmIxaEmtGoJnd9RE4oywKhgN7_TK7wXRlqA4UoRPiH2ACrdU-_cLQL9Jc0u0GqZJK31LDbOeN95QgtSCc72k3Vtzy3CrVpp5TAA67s1Gj9Skn-CAQ', + 'q' => 'zPD-B-nrngwF-O99BHvb47XGKR7ON8JCI6JxavzIkusMXCB8rMyYW8zLs68L8JLAzWZ34oMq0FPUnysBxc5nTF8Nb4BZxTZ5-9cHfoKrYTI3YWsmVW2FpCJFEjMs4NXZ28PBkS9b4zjfS2KhNdkmCeOYU0tJpNfwmOTI90qeUdU', + 'dp' => 'aJrzw_kjWK9uDlTeaES2e4muv6bWbopYfrPHVWG7NPGoGdhnBnd70-jhgMEiTZSNU8VXw2u7prAR3kZ-kAp1DdwlqedYOzFsOJcPA0UZhbORyrBy30kbll_7u6CanFm6X4VyJxCpejd7jKNw6cCTFP1sfhWg5NVJ5EUTkPwE66M', + 'dq' => 'Swz1-m_vmTFN_pu1bK7vF7S5nNVrL4A0OFiEsGliCmuJWzOKdL14DiYxctvnw3H6qT2dKZZfV2tbse5N9-JecdldUjfuqAoLIe7dD7dKi42YOlTC9QXmqvTh1ohnJu8pmRFXEZQGUm_BVhoIb2_WPkjav6YSkguCUHt4HRd2YwE', + 'qi' => 'BocuCOEOq-oyLDALwzMXU8gOf3IL1Q1_BWwsdoANoh6i179psxgE4JXToWcpXZQQqub8ngwE6uR9fpd3m6N_PL4T55vbDDyjPKmrL2ttC2gOtx9KrpPh-Z7LQRo4BE48nHJJrystKHfFlaH2G7JxHNgMBYVADyttN09qEoav8Os', + ]); + + $jwsBuilder = new JWSBuilder( + new AlgorithmManager([new Algorithm\RS512()]) + ); + $serializer = new CompactSerializer( + ); + $jws = $jwsBuilder + ->create()->withPayload('Live long and Prosper.') + ->addSignature( + $key, + [ + 'alg' => 'RS512', + 'jwk' => [ + 'kty' => 'RSA', + 'n' => 'tpS1ZmfVKVP5KofIhMBP0tSWc4qlh6fm2lrZSkuKxUjEaWjzZSzs72gEIGxraWusMdoRuV54xsWRyf5KeZT0S-I5Prle3Idi3gICiO4NwvMk6JwSBcJWwmSLFEKyUSnB2CtfiGc0_5rQCpcEt_Dn5iM-BNn7fqpoLIbks8rXKUIj8-qMVqkTXsEKeKinE23t1ykMldsNaaOH-hvGti5Jt2DMnH1JjoXdDXfxvSP_0gjUYb0ektudYFXoA6wekmQyJeImvgx4Myz1I4iHtkY_Cp7J4Mn1ejZ6HNmyvoTE_4OuY1uCeYv4UyXFc1s1uUyYtj4z57qsHGsS4dQ3A2MJsw', + 'e' => 'AQAB', + ], + ] + ) + ->build() + ; + + static::assertEquals('eyJhbGciOiJSUzUxMiIsImp3ayI6eyJrdHkiOiJSU0EiLCJuIjoidHBTMVptZlZLVlA1S29mSWhNQlAwdFNXYzRxbGg2Zm0ybHJaU2t1S3hVakVhV2p6WlN6czcyZ0VJR3hyYVd1c01kb1J1VjU0eHNXUnlmNUtlWlQwUy1JNVBybGUzSWRpM2dJQ2lPNE53dk1rNkp3U0JjSld3bVNMRkVLeVVTbkIyQ3RmaUdjMF81clFDcGNFdF9EbjVpTS1CTm43ZnFwb0xJYmtzOHJYS1VJajgtcU1WcWtUWHNFS2VLaW5FMjN0MXlrTWxkc05hYU9ILWh2R3RpNUp0MkRNbkgxSmpvWGREWGZ4dlNQXzBnalVZYjBla3R1ZFlGWG9BNndla21ReUplSW12Z3g0TXl6MUk0aUh0a1lfQ3A3SjRNbjFlalo2SE5teXZvVEVfNE91WTF1Q2VZdjRVeVhGYzFzMXVVeVl0ajR6NTdxc0hHc1M0ZFEzQTJNSnN3IiwiZSI6IkFRQUIifX0.TGl2ZSBsb25nIGFuZCBQcm9zcGVyLg.OMttEbx9fWoJl4gJwp8m249P87nNENhy5RzH84S1iR8b-upQNy8dqHoIVsQ6qINDjDL5YTl4UWvChIr5AO433LjNUimIeEp2cfiqrszTTwhv-EF3Lp3Ft9NmTb-3ZWvDo1WwwUrD0qro7bynaz5O06DxQfTROcrC6hNX05y6nW_-21exs2_w2OoOWA0Ebx9ev1ayZJh1AQ6q18Ajb0Gk1RST1PFjz0Sk_YiUIYRSVJzgv2Lf7R_Lyi5A5OkIfLOyJmKBi6m0FOLoynq_fT96wCbf5Nkhx-RiuFEcefGhgDav7Wfim3zA3ZAHeNWe58BZOf-8v1kXsV-yd6zQlVa8iw', $serializer->serialize($jws, 0)); + + $jws = $serializer->serialize($jws, 0); + $result = $serializer->unserialize($jws); + + static::assertEquals('Live long and Prosper.', $result->getPayload()); + static::assertEquals(1, $result->countSignatures()); + static::assertEquals('RS512', $result->getSignature(0)->getProtectedHeaderParameter('alg')); + } + + /** + * @test + */ + public function completePS256Sign(): void + { + $key = new JWK([ + 'kty' => 'RSA', + 'key_ops' => ['sign'], + 'n' => 'tpS1ZmfVKVP5KofIhMBP0tSWc4qlh6fm2lrZSkuKxUjEaWjzZSzs72gEIGxraWusMdoRuV54xsWRyf5KeZT0S-I5Prle3Idi3gICiO4NwvMk6JwSBcJWwmSLFEKyUSnB2CtfiGc0_5rQCpcEt_Dn5iM-BNn7fqpoLIbks8rXKUIj8-qMVqkTXsEKeKinE23t1ykMldsNaaOH-hvGti5Jt2DMnH1JjoXdDXfxvSP_0gjUYb0ektudYFXoA6wekmQyJeImvgx4Myz1I4iHtkY_Cp7J4Mn1ejZ6HNmyvoTE_4OuY1uCeYv4UyXFc1s1uUyYtj4z57qsHGsS4dQ3A2MJsw', + 'e' => 'AQAB', + 'p' => '5BGU1c7af_5sFyfsa-onIJgo5BZu8uHvz3Uyb8OA0a-G9UPO1ShLYjX0wUfhZcFB7fwPtgmmYAN6wKGVce9eMAbX4PliPk3r-BcpZuPKkuLk_wFvgWAQ5Hqw2iEuwXLV0_e8c2gaUt_hyMC5-nFc4v0Bmv6NT6Pfry-UrK3BKWc', + 'd' => 'Kp0KuZwCZGL1BLgsVM-N0edMNitl9wN5Hf2WOYDoIqOZNAEKzdJuenIMhITJjRFUX05GVL138uyp2js_pqDdY9ipA7rAKThwGuDdNphZHech9ih3DGEPXs-YpmHqvIbCd3GoGm38MKwxYkddEpFnjo8rKna1_BpJthrFxjDRhw9DxJBycOdH2yWTyp62ZENPvneK40H2a57W4QScTgfecZqD59m2fGUaWaX5uUmIxaEmtGoJnd9RE4oywKhgN7_TK7wXRlqA4UoRPiH2ACrdU-_cLQL9Jc0u0GqZJK31LDbOeN95QgtSCc72k3Vtzy3CrVpp5TAA67s1Gj9Skn-CAQ', + 'q' => 'zPD-B-nrngwF-O99BHvb47XGKR7ON8JCI6JxavzIkusMXCB8rMyYW8zLs68L8JLAzWZ34oMq0FPUnysBxc5nTF8Nb4BZxTZ5-9cHfoKrYTI3YWsmVW2FpCJFEjMs4NXZ28PBkS9b4zjfS2KhNdkmCeOYU0tJpNfwmOTI90qeUdU', + 'dp' => 'aJrzw_kjWK9uDlTeaES2e4muv6bWbopYfrPHVWG7NPGoGdhnBnd70-jhgMEiTZSNU8VXw2u7prAR3kZ-kAp1DdwlqedYOzFsOJcPA0UZhbORyrBy30kbll_7u6CanFm6X4VyJxCpejd7jKNw6cCTFP1sfhWg5NVJ5EUTkPwE66M', + 'dq' => 'Swz1-m_vmTFN_pu1bK7vF7S5nNVrL4A0OFiEsGliCmuJWzOKdL14DiYxctvnw3H6qT2dKZZfV2tbse5N9-JecdldUjfuqAoLIe7dD7dKi42YOlTC9QXmqvTh1ohnJu8pmRFXEZQGUm_BVhoIb2_WPkjav6YSkguCUHt4HRd2YwE', + 'qi' => 'BocuCOEOq-oyLDALwzMXU8gOf3IL1Q1_BWwsdoANoh6i179psxgE4JXToWcpXZQQqub8ngwE6uR9fpd3m6N_PL4T55vbDDyjPKmrL2ttC2gOtx9KrpPh-Z7LQRo4BE48nHJJrystKHfFlaH2G7JxHNgMBYVADyttN09qEoav8Os', + ]); + + $jwsBuilder = new JWSBuilder( + new AlgorithmManager([new Algorithm\PS256()]) + ); + $serializer = new CompactSerializer( + ); + $jws = $jwsBuilder + ->create()->withPayload('Live long and Prosper.') + ->addSignature( + $key, + [ + 'alg' => 'PS256', + 'jwk' => [ + 'kty' => 'RSA', + 'key_ops' => ['verify'], + 'n' => 'tpS1ZmfVKVP5KofIhMBP0tSWc4qlh6fm2lrZSkuKxUjEaWjzZSzs72gEIGxraWusMdoRuV54xsWRyf5KeZT0S-I5Prle3Idi3gICiO4NwvMk6JwSBcJWwmSLFEKyUSnB2CtfiGc0_5rQCpcEt_Dn5iM-BNn7fqpoLIbks8rXKUIj8-qMVqkTXsEKeKinE23t1ykMldsNaaOH-hvGti5Jt2DMnH1JjoXdDXfxvSP_0gjUYb0ektudYFXoA6wekmQyJeImvgx4Myz1I4iHtkY_Cp7J4Mn1ejZ6HNmyvoTE_4OuY1uCeYv4UyXFc1s1uUyYtj4z57qsHGsS4dQ3A2MJsw', + 'e' => 'AQAB', + ], + ] + ) + ->build() + ; + + $jws = $serializer->serialize($jws, 0); + $result = $serializer->unserialize($jws); + + static::assertEquals('Live long and Prosper.', $result->getPayload()); + static::assertEquals(1, $result->countSignatures()); + static::assertEquals('PS256', $result->getSignature(0)->getProtectedHeaderParameter('alg')); + } + + /** + * @test + */ + public function completePS384Sign(): void + { + $key = new JWK([ + 'kty' => 'RSA', + 'n' => 'tpS1ZmfVKVP5KofIhMBP0tSWc4qlh6fm2lrZSkuKxUjEaWjzZSzs72gEIGxraWusMdoRuV54xsWRyf5KeZT0S-I5Prle3Idi3gICiO4NwvMk6JwSBcJWwmSLFEKyUSnB2CtfiGc0_5rQCpcEt_Dn5iM-BNn7fqpoLIbks8rXKUIj8-qMVqkTXsEKeKinE23t1ykMldsNaaOH-hvGti5Jt2DMnH1JjoXdDXfxvSP_0gjUYb0ektudYFXoA6wekmQyJeImvgx4Myz1I4iHtkY_Cp7J4Mn1ejZ6HNmyvoTE_4OuY1uCeYv4UyXFc1s1uUyYtj4z57qsHGsS4dQ3A2MJsw', + 'e' => 'AQAB', + 'p' => '5BGU1c7af_5sFyfsa-onIJgo5BZu8uHvz3Uyb8OA0a-G9UPO1ShLYjX0wUfhZcFB7fwPtgmmYAN6wKGVce9eMAbX4PliPk3r-BcpZuPKkuLk_wFvgWAQ5Hqw2iEuwXLV0_e8c2gaUt_hyMC5-nFc4v0Bmv6NT6Pfry-UrK3BKWc', + 'd' => 'Kp0KuZwCZGL1BLgsVM-N0edMNitl9wN5Hf2WOYDoIqOZNAEKzdJuenIMhITJjRFUX05GVL138uyp2js_pqDdY9ipA7rAKThwGuDdNphZHech9ih3DGEPXs-YpmHqvIbCd3GoGm38MKwxYkddEpFnjo8rKna1_BpJthrFxjDRhw9DxJBycOdH2yWTyp62ZENPvneK40H2a57W4QScTgfecZqD59m2fGUaWaX5uUmIxaEmtGoJnd9RE4oywKhgN7_TK7wXRlqA4UoRPiH2ACrdU-_cLQL9Jc0u0GqZJK31LDbOeN95QgtSCc72k3Vtzy3CrVpp5TAA67s1Gj9Skn-CAQ', + 'q' => 'zPD-B-nrngwF-O99BHvb47XGKR7ON8JCI6JxavzIkusMXCB8rMyYW8zLs68L8JLAzWZ34oMq0FPUnysBxc5nTF8Nb4BZxTZ5-9cHfoKrYTI3YWsmVW2FpCJFEjMs4NXZ28PBkS9b4zjfS2KhNdkmCeOYU0tJpNfwmOTI90qeUdU', + 'dp' => 'aJrzw_kjWK9uDlTeaES2e4muv6bWbopYfrPHVWG7NPGoGdhnBnd70-jhgMEiTZSNU8VXw2u7prAR3kZ-kAp1DdwlqedYOzFsOJcPA0UZhbORyrBy30kbll_7u6CanFm6X4VyJxCpejd7jKNw6cCTFP1sfhWg5NVJ5EUTkPwE66M', + 'dq' => 'Swz1-m_vmTFN_pu1bK7vF7S5nNVrL4A0OFiEsGliCmuJWzOKdL14DiYxctvnw3H6qT2dKZZfV2tbse5N9-JecdldUjfuqAoLIe7dD7dKi42YOlTC9QXmqvTh1ohnJu8pmRFXEZQGUm_BVhoIb2_WPkjav6YSkguCUHt4HRd2YwE', + 'qi' => 'BocuCOEOq-oyLDALwzMXU8gOf3IL1Q1_BWwsdoANoh6i179psxgE4JXToWcpXZQQqub8ngwE6uR9fpd3m6N_PL4T55vbDDyjPKmrL2ttC2gOtx9KrpPh-Z7LQRo4BE48nHJJrystKHfFlaH2G7JxHNgMBYVADyttN09qEoav8Os', + ]); + + $jwsBuilder = new JWSBuilder( + new AlgorithmManager([new Algorithm\PS384()]) + ); + $serializer = new CompactSerializer( + ); + $jws = $jwsBuilder + ->create()->withPayload('Live long and Prosper.') + ->addSignature( + $key, + [ + 'alg' => 'PS384', + 'jwk' => [ + 'kty' => 'RSA', + 'n' => 'tpS1ZmfVKVP5KofIhMBP0tSWc4qlh6fm2lrZSkuKxUjEaWjzZSzs72gEIGxraWusMdoRuV54xsWRyf5KeZT0S-I5Prle3Idi3gICiO4NwvMk6JwSBcJWwmSLFEKyUSnB2CtfiGc0_5rQCpcEt_Dn5iM-BNn7fqpoLIbks8rXKUIj8-qMVqkTXsEKeKinE23t1ykMldsNaaOH-hvGti5Jt2DMnH1JjoXdDXfxvSP_0gjUYb0ektudYFXoA6wekmQyJeImvgx4Myz1I4iHtkY_Cp7J4Mn1ejZ6HNmyvoTE_4OuY1uCeYv4UyXFc1s1uUyYtj4z57qsHGsS4dQ3A2MJsw', + 'e' => 'AQAB', + ], + ] + ) + ->build() + ; + + $jws = $serializer->serialize($jws, 0); + $result = $serializer->unserialize($jws); + + static::assertEquals('Live long and Prosper.', $result->getPayload()); + static::assertEquals(1, $result->countSignatures()); + static::assertEquals('PS384', $result->getSignature(0)->getProtectedHeaderParameter('alg')); + } + + /** + * @test + */ + public function completePS512Sign(): void + { + $key = new JWK([ + 'kty' => 'RSA', + 'n' => 'tpS1ZmfVKVP5KofIhMBP0tSWc4qlh6fm2lrZSkuKxUjEaWjzZSzs72gEIGxraWusMdoRuV54xsWRyf5KeZT0S-I5Prle3Idi3gICiO4NwvMk6JwSBcJWwmSLFEKyUSnB2CtfiGc0_5rQCpcEt_Dn5iM-BNn7fqpoLIbks8rXKUIj8-qMVqkTXsEKeKinE23t1ykMldsNaaOH-hvGti5Jt2DMnH1JjoXdDXfxvSP_0gjUYb0ektudYFXoA6wekmQyJeImvgx4Myz1I4iHtkY_Cp7J4Mn1ejZ6HNmyvoTE_4OuY1uCeYv4UyXFc1s1uUyYtj4z57qsHGsS4dQ3A2MJsw', + 'e' => 'AQAB', + 'p' => '5BGU1c7af_5sFyfsa-onIJgo5BZu8uHvz3Uyb8OA0a-G9UPO1ShLYjX0wUfhZcFB7fwPtgmmYAN6wKGVce9eMAbX4PliPk3r-BcpZuPKkuLk_wFvgWAQ5Hqw2iEuwXLV0_e8c2gaUt_hyMC5-nFc4v0Bmv6NT6Pfry-UrK3BKWc', + 'd' => 'Kp0KuZwCZGL1BLgsVM-N0edMNitl9wN5Hf2WOYDoIqOZNAEKzdJuenIMhITJjRFUX05GVL138uyp2js_pqDdY9ipA7rAKThwGuDdNphZHech9ih3DGEPXs-YpmHqvIbCd3GoGm38MKwxYkddEpFnjo8rKna1_BpJthrFxjDRhw9DxJBycOdH2yWTyp62ZENPvneK40H2a57W4QScTgfecZqD59m2fGUaWaX5uUmIxaEmtGoJnd9RE4oywKhgN7_TK7wXRlqA4UoRPiH2ACrdU-_cLQL9Jc0u0GqZJK31LDbOeN95QgtSCc72k3Vtzy3CrVpp5TAA67s1Gj9Skn-CAQ', + 'q' => 'zPD-B-nrngwF-O99BHvb47XGKR7ON8JCI6JxavzIkusMXCB8rMyYW8zLs68L8JLAzWZ34oMq0FPUnysBxc5nTF8Nb4BZxTZ5-9cHfoKrYTI3YWsmVW2FpCJFEjMs4NXZ28PBkS9b4zjfS2KhNdkmCeOYU0tJpNfwmOTI90qeUdU', + 'dp' => 'aJrzw_kjWK9uDlTeaES2e4muv6bWbopYfrPHVWG7NPGoGdhnBnd70-jhgMEiTZSNU8VXw2u7prAR3kZ-kAp1DdwlqedYOzFsOJcPA0UZhbORyrBy30kbll_7u6CanFm6X4VyJxCpejd7jKNw6cCTFP1sfhWg5NVJ5EUTkPwE66M', + 'dq' => 'Swz1-m_vmTFN_pu1bK7vF7S5nNVrL4A0OFiEsGliCmuJWzOKdL14DiYxctvnw3H6qT2dKZZfV2tbse5N9-JecdldUjfuqAoLIe7dD7dKi42YOlTC9QXmqvTh1ohnJu8pmRFXEZQGUm_BVhoIb2_WPkjav6YSkguCUHt4HRd2YwE', + 'qi' => 'BocuCOEOq-oyLDALwzMXU8gOf3IL1Q1_BWwsdoANoh6i179psxgE4JXToWcpXZQQqub8ngwE6uR9fpd3m6N_PL4T55vbDDyjPKmrL2ttC2gOtx9KrpPh-Z7LQRo4BE48nHJJrystKHfFlaH2G7JxHNgMBYVADyttN09qEoav8Os', + ]); + + $jwsBuilder = new JWSBuilder( + new AlgorithmManager([new Algorithm\PS512()]) + ); + $serializer = new CompactSerializer( + ); + $jws = $jwsBuilder + ->create()->withPayload('Live long and Prosper.') + ->addSignature( + $key, + [ + 'alg' => 'PS512', + 'jwk' => [ + 'kty' => 'RSA', + 'n' => 'tpS1ZmfVKVP5KofIhMBP0tSWc4qlh6fm2lrZSkuKxUjEaWjzZSzs72gEIGxraWusMdoRuV54xsWRyf5KeZT0S-I5Prle3Idi3gICiO4NwvMk6JwSBcJWwmSLFEKyUSnB2CtfiGc0_5rQCpcEt_Dn5iM-BNn7fqpoLIbks8rXKUIj8-qMVqkTXsEKeKinE23t1ykMldsNaaOH-hvGti5Jt2DMnH1JjoXdDXfxvSP_0gjUYb0ektudYFXoA6wekmQyJeImvgx4Myz1I4iHtkY_Cp7J4Mn1ejZ6HNmyvoTE_4OuY1uCeYv4UyXFc1s1uUyYtj4z57qsHGsS4dQ3A2MJsw', + 'e' => 'AQAB', + ], + ] + ) + ->build() + ; + + $jws = $serializer->serialize($jws, 0); + $result = $serializer->unserialize($jws); + + static::assertEquals('Live long and Prosper.', $result->getPayload()); + static::assertEquals(1, $result->countSignatures()); + static::assertEquals('PS512', $result->getSignature(0)->getProtectedHeaderParameter('alg')); + } + + /** + * @see https://tools.ietf.org/html/rfc7516#appendix-A.4 + * + * @test + */ + public function loadJWSJSONSerialization(): void + { + $serializer = new JSONGeneralSerializer( + ); + $result = $serializer->unserialize('{"payload":"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ","signatures":[{"protected":"eyJhbGciOiJSUzI1NiJ9","header":{"kid":"2010-12-29"},"signature":"cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw"},{"protected":"eyJhbGciOiJFUzI1NiJ9","header":{"kid":"e9bc097a-ce51-4036-9562-d2ade882db0d"},"signature":"DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q"}]}'); + + $claims = json_decode($result->getPayload(), true); + static::assertEquals(['iss' => 'joe', 'exp' => 1300819380, 'http://example.com/is_root' => true], $claims); + static::assertEquals(2, $result->countSignatures()); + static::assertEquals('RS256', $result->getSignature(0)->getProtectedHeaderParameter('alg')); + static::assertEquals('ES256', $result->getSignature(1)->getProtectedHeaderParameter('alg')); + } + + /** + * @see https://tools.ietf.org/html/rfc7516#appendix-A.4 + * + * @test + */ + public function loadJWSJSONSerializationWithDetachedPayload(): void + { + $jwsVerifier = new JWSVerifier( + new AlgorithmManager([new Algorithm\RS256()]) + ); + $serializer = new JSONGeneralSerializer( + ); + $result = $serializer->unserialize('{"signatures":[{"protected":"eyJhbGciOiJSUzI1NiJ9","header":{"kid":"2010-12-29"},"signature":"cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw"},{"protected":"eyJhbGciOiJFUzI1NiJ9","header":{"kid":"e9bc097a-ce51-4036-9562-d2ade882db0d"},"signature":"DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q"}]}'); + + static::assertEquals(2, $result->countSignatures()); + static::assertEquals('RS256', $result->getSignature(0)->getProtectedHeaderParameter('alg')); + static::assertEquals('ES256', $result->getSignature(1)->getProtectedHeaderParameter('alg')); + $jwsVerifier->verifyWithKeySet($result, $this->getPrivateKeySet(), 0, Base64Url::decode('eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ')); + } + + /** + * @test + */ + public function loadJWSJSONSerializationWithDetachedPayloadAndPayloadInJWS(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('A detached payload is set, but the JWS already has a payload'); + $jwsVerifier = new JWSVerifier( + new AlgorithmManager([new Algorithm\RS256()]) + ); + $serializer = new JSONGeneralSerializer( + ); + $result = $serializer->unserialize('{"payload":"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ","signatures":[{"protected":"eyJhbGciOiJSUzI1NiJ9","header":{"kid":"2010-12-29"},"signature":"cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw"},{"protected":"eyJhbGciOiJFUzI1NiJ9","header":{"kid":"e9bc097a-ce51-4036-9562-d2ade882db0d"},"signature":"DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q"}]}'); + + static::assertEquals(2, $result->countSignatures()); + static::assertEquals('RS256', $result->getSignature(0)->getProtectedHeaderParameter('alg')); + + $jwsVerifier->verifyWithKeySet($result, $this->getPrivateKeySet(), 0, 'eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ'); + } + + /** + * @test + */ + public function loadInvalidInput(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Unsupported input'); + $serializer = new CompactSerializer( + ); + $serializer->unserialize('DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q'); + } + + /** + * @test + */ + public function loadInvalidInput2(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Unsupported input'); + $serializer = new CompactSerializer( + ); + $serializer->unserialize('DtEhU3ljb.Eg8L.38VWAf.UAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q'); + } + + /** + * @see https://tools.ietf.org/html/rfc7515#appendix-A.1 + * + * @test + */ + public function loadIETFExample1(): void + { + $serializer = new CompactSerializer( + ); + $result = $serializer->unserialize('eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk'); + + static::assertEquals(['iss' => 'joe', 'exp' => 1300819380, 'http://example.com/is_root' => true], json_decode($result->getPayload(), true)); + static::assertEquals(1, $result->countSignatures()); + static::assertEquals('HS256', $result->getSignature(0)->getProtectedHeaderParameter('alg')); + } + + /** + * @see https://tools.ietf.org/html/rfc7515#appendix-A.2 + * + * @test + */ + public function loadIETFExample2(): void + { + $serializer = new CompactSerializer( + ); + $result = $serializer->unserialize('eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw'); + + static::assertEquals(['iss' => 'joe', 'exp' => 1300819380, 'http://example.com/is_root' => true], json_decode($result->getPayload(), true)); + static::assertEquals(1, $result->countSignatures()); + static::assertEquals('RS256', $result->getSignature(0)->getProtectedHeaderParameter('alg')); + } + + /** + * @see https://tools.ietf.org/html/rfc7515#appendix-A.3 + * + * @test + */ + public function loadIETFExample3(): void + { + $serializer = new CompactSerializer( + ); + $result = $serializer->unserialize('eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q'); + + static::assertEquals(['iss' => 'joe', 'exp' => 1300819380, 'http://example.com/is_root' => true], json_decode($result->getPayload(), true)); + static::assertEquals(1, $result->countSignatures()); + static::assertEquals('ES256', $result->getSignature(0)->getProtectedHeaderParameter('alg')); + } + + /** + * @see https://tools.ietf.org/html/rfc7515#appendix-A.4 + * + * @test + */ + public function loadIETFExample4(): void + { + $serializer = new CompactSerializer( + ); + $result = $serializer->unserialize('eyJhbGciOiJFUzUxMiJ9.UGF5bG9hZA.AdwMgeerwtHoh-l192l60hp9wAHZFVJbLfD_UxMi70cwnZOYaRI1bKPWROc-mZZqwqT2SI-KGDKB34XO0aw_7XdtAG8GaSwFKdCAPZgoXD2YBJZCPEX3xKpRwcdOO8KpEHwJjyqOgzDO7iKvU8vcnwNrmxYbSW9ERBXukOXolLzeO_Jn'); + + static::assertEquals('Payload', $result->getPayload()); + static::assertEquals(1, $result->countSignatures()); + static::assertEquals('ES512', $result->getSignature(0)->getProtectedHeaderParameter('alg')); + } + + private function getPrivateKeySet(): JWKSet + { + $keys = ['keys' => [ + [ + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => 'weNJy2HscCSM6AEDTDg04biOvhFhyyWvOHQfeF_PxMQ', + 'y' => 'e8lnCO-AlStT-NJVX-crhB7QRYhiix03illJOVAOyck', + 'd' => 'VEmDZpDXXK8p8N0Cndsxs924q6nS1RXFASRl6BfUqdw', + ], + [ + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => 'gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0', + 'y' => 'SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps', + 'd' => '0_NxaRPUMQoAJt50Gz8YiTr8gRTwyEaCumd-MToTmIo', + ], + [ + 'kid' => '2010-12-29', + 'kty' => 'RSA', + 'n' => 'ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ', + 'e' => 'AQAB', + 'd' => 'Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97IjlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYTCBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLhBOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ', + ], + [ + 'kid' => 'e9bc097a-ce51-4036-9562-d2ade882db0d', + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => 'f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU', + 'y' => 'x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0', + 'd' => 'jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI', + ], + [ + 'kid' => '123456789', + 'kty' => 'RSA', + 'n' => 'tpS1ZmfVKVP5KofIhMBP0tSWc4qlh6fm2lrZSkuKxUjEaWjzZSzs72gEIGxraWusMdoRuV54xsWRyf5KeZT0S-I5Prle3Idi3gICiO4NwvMk6JwSBcJWwmSLFEKyUSnB2CtfiGc0_5rQCpcEt_Dn5iM-BNn7fqpoLIbks8rXKUIj8-qMVqkTXsEKeKinE23t1ykMldsNaaOH-hvGti5Jt2DMnH1JjoXdDXfxvSP_0gjUYb0ektudYFXoA6wekmQyJeImvgx4Myz1I4iHtkY_Cp7J4Mn1ejZ6HNmyvoTE_4OuY1uCeYv4UyXFc1s1uUyYtj4z57qsHGsS4dQ3A2MJsw', + 'e' => 'AQAB', + 'p' => '5BGU1c7af_5sFyfsa-onIJgo5BZu8uHvz3Uyb8OA0a-G9UPO1ShLYjX0wUfhZcFB7fwPtgmmYAN6wKGVce9eMAbX4PliPk3r-BcpZuPKkuLk_wFvgWAQ5Hqw2iEuwXLV0_e8c2gaUt_hyMC5-nFc4v0Bmv6NT6Pfry-UrK3BKWc', + 'd' => 'Kp0KuZwCZGL1BLgsVM-N0edMNitl9wN5Hf2WOYDoIqOZNAEKzdJuenIMhITJjRFUX05GVL138uyp2js_pqDdY9ipA7rAKThwGuDdNphZHech9ih3DGEPXs-YpmHqvIbCd3GoGm38MKwxYkddEpFnjo8rKna1_BpJthrFxjDRhw9DxJBycOdH2yWTyp62ZENPvneK40H2a57W4QScTgfecZqD59m2fGUaWaX5uUmIxaEmtGoJnd9RE4oywKhgN7_TK7wXRlqA4UoRPiH2ACrdU-_cLQL9Jc0u0GqZJK31LDbOeN95QgtSCc72k3Vtzy3CrVpp5TAA67s1Gj9Skn-CAQ', + 'q' => 'zPD-B-nrngwF-O99BHvb47XGKR7ON8JCI6JxavzIkusMXCB8rMyYW8zLs68L8JLAzWZ34oMq0FPUnysBxc5nTF8Nb4BZxTZ5-9cHfoKrYTI3YWsmVW2FpCJFEjMs4NXZ28PBkS9b4zjfS2KhNdkmCeOYU0tJpNfwmOTI90qeUdU', + 'dp' => 'aJrzw_kjWK9uDlTeaES2e4muv6bWbopYfrPHVWG7NPGoGdhnBnd70-jhgMEiTZSNU8VXw2u7prAR3kZ-kAp1DdwlqedYOzFsOJcPA0UZhbORyrBy30kbll_7u6CanFm6X4VyJxCpejd7jKNw6cCTFP1sfhWg5NVJ5EUTkPwE66M', + 'dq' => 'Swz1-m_vmTFN_pu1bK7vF7S5nNVrL4A0OFiEsGliCmuJWzOKdL14DiYxctvnw3H6qT2dKZZfV2tbse5N9-JecdldUjfuqAoLIe7dD7dKi42YOlTC9QXmqvTh1ohnJu8pmRFXEZQGUm_BVhoIb2_WPkjav6YSkguCUHt4HRd2YwE', + 'qi' => 'BocuCOEOq-oyLDALwzMXU8gOf3IL1Q1_BWwsdoANoh6i179psxgE4JXToWcpXZQQqub8ngwE6uR9fpd3m6N_PL4T55vbDDyjPKmrL2ttC2gOtx9KrpPh-Z7LQRo4BE48nHJJrystKHfFlaH2G7JxHNgMBYVADyttN09qEoav8Os', + ], + [ + 'kty' => 'RSA', + 'n' => 'oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUWcJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3Spsk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2asbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMStPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2djYgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw', + 'e' => 'AQAB', + 'd' => 'kLdtIj6GbDks_ApCSTYQtelcNttlKiOyPzMrXHeI-yk1F7-kpDxY4-WY5NWV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD93Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghkqDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vlt3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSndVTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ', + 'p' => '1r52Xk46c-LsfB5P442p7atdPUrxQSy4mti_tZI3Mgf2EuFVbUoDBvaRQ-SWxkbkmoEzL7JXroSBjSrK3YIQgYdMgyAEPTPjXv_hI2_1eTSPVZfzL0lffNn03IXqWF5MDFuoUYE0hzb2vhrlN_rKrbfDIwUbTrjjgieRbwC6Cl0', + 'q' => 'wLb35x7hmQWZsWJmB_vle87ihgZ19S8lBEROLIsZG4ayZVe9Hi9gDVCOBmUDdaDYVTSNx_8Fyw1YYa9XGrGnDew00J28cRUoeBB_jKI1oma0Orv1T9aXIWxKwd4gvxFImOWr3QRL9KEBRzk2RatUBnmDZJTIAfwTs0g68UZHvtc', + 'dp' => 'ZK-YwE7diUh0qR1tR7w8WHtolDx3MZ_OTowiFvgfeQ3SiresXjm9gZ5KLhMXvo-uz-KUJWDxS5pFQ_M0evdo1dKiRTjVw_x4NyqyXPM5nULPkcpU827rnpZzAJKpdhWAgqrXGKAECQH0Xt4taznjnd_zVpAmZZq60WPMBMfKcuE', + 'dq' => 'Dq0gfgJ1DdFGXiLvQEZnuKEN0UUmsJBxkjydc3j4ZYdBiMRAy86x0vHCjywcMlYYg4yoC4YZa9hNVcsjqA3FeiL19rk8g6Qn29Tt0cj8qqyFpz9vNDBUfCAiJVeESOjJDZPYHdHY8v1b-o-Z2X5tvLx-TCekf7oxyeKDUqKWjis', + 'qi' => 'VIMpMYbPf47dT1w_zDUXfPimsSegnMOA1zTaX7aGk_8urY6R8-ZW1FxU7AlWAyLWybqq6t16VFd7hQd0y6flUK4SlOydB61gwanOsXGOAOv82cHq0E3eL4HrtZkUuKvnPrMnsUUFlfUdybVzxyjz9JF_XyaY14ardLSjf4L_FNY', + ], + [ + 'kty' => 'RSA', + 'n' => 'sXchDaQebHnPiGvyDOAT4saGEUetSyo9MKLOoWFsueri23bOdgWp4Dy1WlUzewbgBHod5pcM9H95GQRV3JDXboIRROSBigeC5yjU1hGzHHyXss8UDprecbAYxknTcQkhslANGRUZmdTOQ5qTRsLAt6BTYuyvVRdhS8exSZEy_c4gs_7svlJJQ4H9_NxsiIoLwAEk7-Q3UXERGYw_75IDrGA84-lA_-Ct4eTlXHBIY2EaV7t7LjJaynVJCpkv4LKjTTAumiGUIuQhrNhZLuF_RJLqHpM2kgWFLU7-VTdL1VbC2tejvcI2BlMkEpk1BzBZI0KQB0GaDWFLN-aEAw3vRw', + 'e' => 'AQAB', + 'd' => 'VFCWOqXr8nvZNyaaJLXdnNPXZKRaWCjkU5Q2egQQpTBMwhprMzWzpR8Sxq1OPThh_J6MUD8Z35wky9b8eEO0pwNS8xlh1lOFRRBoNqDIKVOku0aZb-rynq8cxjDTLZQ6Fz7jSjR1Klop-YKaUHc9GsEofQqYruPhzSA-QgajZGPbE_0ZaVDJHfyd7UUBUKunFMScbflYAAOYJqVIVwaYR5zWEEceUjNnTNo_CVSj-VvXLO5VZfCUAVLgW4dpf1SrtZjSt34YLsRarSb127reG_DUwg9Ch-KyvjT1SkHgUWRVGcyly7uvVGRSDwsXypdrNinPA4jlhoNdizK2zF2CWQ', + 'p' => '9gY2w6I6S6L0juEKsbeDAwpd9WMfgqFoeA9vEyEUuk4kLwBKcoe1x4HG68ik918hdDSE9vDQSccA3xXHOAFOPJ8R9EeIAbTi1VwBYnbTp87X-xcPWlEPkrdoUKW60tgs1aNd_Nnc9LEVVPMS390zbFxt8TN_biaBgelNgbC95sM', + 'q' => 'uKlCKvKv_ZJMVcdIs5vVSU_6cPtYI1ljWytExV_skstvRSNi9r66jdd9-yBhVfuG4shsp2j7rGnIio901RBeHo6TPKWVVykPu1iYhQXw1jIABfw-MVsN-3bQ76WLdt2SDxsHs7q7zPyUyHXmps7ycZ5c72wGkUwNOjYelmkiNS0', + 'dp' => 'w0kZbV63cVRvVX6yk3C8cMxo2qCM4Y8nsq1lmMSYhG4EcL6FWbX5h9yuvngs4iLEFk6eALoUS4vIWEwcL4txw9LsWH_zKI-hwoReoP77cOdSL4AVcraHawlkpyd2TWjE5evgbhWtOxnZee3cXJBkAi64Ik6jZxbvk-RR3pEhnCs', + 'dq' => 'o_8V14SezckO6CNLKs_btPdFiO9_kC1DsuUTd2LAfIIVeMZ7jn1Gus_Ff7B7IVx3p5KuBGOVF8L-qifLb6nQnLysgHDh132NDioZkhH7mI7hPG-PYE_odApKdnqECHWw0J-F0JWnUd6D2B_1TvF9mXA2Qx-iGYn8OVV1Bsmp6qU', + 'qi' => 'eNho5yRBEBxhGBtQRww9QirZsB66TrfFReG_CcteI1aCneT0ELGhYlRlCtUkTRclIfuEPmNsNDPbLoLqqCVznFbvdB7x-Tl-m0l_eFTj2KiqwGqE9PZB9nNTwMVvH3VRRSLWACvPnSiwP8N5Usy-WRXS-V7TbpxIhvepTfE0NNo', + ], + [ + 'kty' => 'RSA', + 'n' => 'ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ', + 'e' => 'AQAB', + 'd' => 'Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97IjlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYTCBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLhBOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ', + 'p' => '4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPGBY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc', + 'q' => 'uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc', + 'dp' => 'BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3QCLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0', + 'dq' => 'h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-kyNlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU', + 'qi' => 'IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2oy26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLUW0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U', + ], + [ + 'kty' => 'EC', + 'crv' => 'P-521', + 'x' => 'AekpBQ8ST8a8VcfVOTNl353vSrDCLLJXmPk06wTjxrrjcBpXp5EOnYG_NjFZ6OvLFV1jSfS9tsz4qUxcWceqwQGk', + 'y' => 'ADSmRA43Z1DSNx_RvcLI87cdL07l6jQyyBXMoxVg_l2Th-x3S1WDhjDly79ajL4Kkd0AZMaZmh9ubmf63e3kyMj2', + 'd' => 'AY5pb7A0UFiB3RELSD64fTLOSV_jazdF7fLYyuTw8lOfRhWg6Y6rUrPAxerEzgdRhajnu0ferB0d53vM9mE15j2C', + ], + ]]; + + return JWKSet::createFromKeyData($keys); + } +} diff --git a/vendor/web-token/jwt-signature-algorithm-rsa/Util/RSA.php b/vendor/web-token/jwt-signature-algorithm-rsa/Util/RSA.php new file mode 100644 index 000000000..f7c2329c0 --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-rsa/Util/RSA.php @@ -0,0 +1,247 @@ +toPEM(), $hash); + if (true !== $result) { + throw new RuntimeException('Unable to sign the data'); + } + + return $signature; + default: + throw new InvalidArgumentException('Unsupported mode.'); + } + } + + /** + * Create a signature. + */ + public static function signWithPSS(RSAKey $key, string $message, string $hash): string + { + $em = self::encodeEMSAPSS($message, 8 * $key->getModulusLength() - 1, Hash::$hash()); + $message = BigInteger::createFromBinaryString($em); + $signature = RSAKey::exponentiate($key, $message); + + return self::convertIntegerToOctetString($signature, $key->getModulusLength()); + } + + /** + * Create a signature. + * + * @deprecated Please use openssl_sign + */ + public static function signWithPKCS15(RSAKey $key, string $message, string $hash): string + { + $em = self::encodeEMSA15($message, $key->getModulusLength(), Hash::$hash()); + $message = BigInteger::createFromBinaryString($em); + $signature = RSAKey::exponentiate($key, $message); + + return self::convertIntegerToOctetString($signature, $key->getModulusLength()); + } + + /** + * @throws InvalidArgumentException if the signature mode is not supported + */ + public static function verify(RSAKey $key, string $message, string $signature, string $hash, int $mode): bool + { + switch ($mode) { + case self::SIGNATURE_PSS: + return self::verifyWithPSS($key, $message, $signature, $hash); + case self::SIGNATURE_PKCS1: + return 1 === openssl_verify($message, $signature, $key->toPEM(), $hash); + default: + throw new InvalidArgumentException('Unsupported mode.'); + } + } + + /** + * Verifies a signature. + * + * @throws RuntimeException if the signature cannot be verified + */ + public static function verifyWithPSS(RSAKey $key, string $message, string $signature, string $hash): bool + { + if (mb_strlen($signature, '8bit') !== $key->getModulusLength()) { + throw new RuntimeException(); + } + $s2 = BigInteger::createFromBinaryString($signature); + $m2 = RSAKey::exponentiate($key, $s2); + $em = self::convertIntegerToOctetString($m2, $key->getModulusLength()); + $modBits = 8 * $key->getModulusLength(); + + return self::verifyEMSAPSS($message, $em, $modBits - 1, Hash::$hash()); + } + + /** + * Verifies a signature. + * + * @deprecated Please use openssl_sign + * + * @throws RuntimeException if the signature cannot be verified + */ + public static function verifyWithPKCS15(RSAKey $key, string $message, string $signature, string $hash): bool + { + if (mb_strlen($signature, '8bit') !== $key->getModulusLength()) { + throw new RuntimeException(); + } + $signature = BigInteger::createFromBinaryString($signature); + $m2 = RSAKey::exponentiate($key, $signature); + $em = self::convertIntegerToOctetString($m2, $key->getModulusLength()); + + return hash_equals($em, self::encodeEMSA15($message, $key->getModulusLength(), Hash::$hash())); + } + + /** + * @throws RuntimeException if the value cannot be converted + */ + private static function convertIntegerToOctetString(BigInteger $x, int $xLen): string + { + $x = $x->toBytes(); + if (mb_strlen($x, '8bit') > $xLen) { + throw new RuntimeException(); + } + + return str_pad($x, $xLen, chr(0), STR_PAD_LEFT); + } + + /** + * MGF1. + */ + private static function getMGF1(string $mgfSeed, int $maskLen, Hash $mgfHash): string + { + $t = ''; + $count = ceil($maskLen / $mgfHash->getLength()); + for ($i = 0; $i < $count; ++$i) { + $c = pack('N', $i); + $t .= $mgfHash->hash($mgfSeed.$c); + } + + return mb_substr($t, 0, $maskLen, '8bit'); + } + + /** + * EMSA-PSS-ENCODE. + * + * @throws RuntimeException if the message length is invalid + */ + private static function encodeEMSAPSS(string $message, int $modulusLength, Hash $hash): string + { + $emLen = ($modulusLength + 1) >> 3; + $sLen = $hash->getLength(); + $mHash = $hash->hash($message); + if ($emLen <= $hash->getLength() + $sLen + 2) { + throw new RuntimeException(); + } + $salt = random_bytes($sLen); + $m2 = "\0\0\0\0\0\0\0\0".$mHash.$salt; + $h = $hash->hash($m2); + $ps = str_repeat(chr(0), $emLen - $sLen - $hash->getLength() - 2); + $db = $ps.chr(1).$salt; + $dbMask = self::getMGF1($h, $emLen - $hash->getLength() - 1, $hash); + $maskedDB = $db ^ $dbMask; + $maskedDB[0] = ~chr(0xFF << ($modulusLength & 7)) & $maskedDB[0]; + $em = $maskedDB.$h.chr(0xBC); + + return $em; + } + + /** + * EMSA-PSS-VERIFY. + * + * @throws InvalidArgumentException if the signature cannot be verified + */ + private static function verifyEMSAPSS(string $m, string $em, int $emBits, Hash $hash): bool + { + $emLen = ($emBits + 1) >> 3; + $sLen = $hash->getLength(); + $mHash = $hash->hash($m); + if ($emLen < $hash->getLength() + $sLen + 2) { + throw new InvalidArgumentException(); + } + if ($em[mb_strlen($em, '8bit') - 1] !== chr(0xBC)) { + throw new InvalidArgumentException(); + } + $maskedDB = mb_substr($em, 0, -$hash->getLength() - 1, '8bit'); + $h = mb_substr($em, -$hash->getLength() - 1, $hash->getLength(), '8bit'); + $temp = chr(0xFF << ($emBits & 7)); + if ((~$maskedDB[0] & $temp) !== $temp) { + throw new InvalidArgumentException(); + } + $dbMask = self::getMGF1($h, $emLen - $hash->getLength() - 1, $hash/*MGF*/); + $db = $maskedDB ^ $dbMask; + $db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0]; + $temp = $emLen - $hash->getLength() - $sLen - 2; + if (mb_substr($db, 0, $temp, '8bit') !== str_repeat(chr(0), $temp)) { + throw new InvalidArgumentException(); + } + if (1 !== ord($db[$temp])) { + throw new InvalidArgumentException(); + } + $salt = mb_substr($db, $temp + 1, null, '8bit'); // should be $sLen long + $m2 = "\0\0\0\0\0\0\0\0".$mHash.$salt; + $h2 = $hash->hash($m2); + + return hash_equals($h, $h2); + } + + /** + * @throws RuntimeException if the value cannot be encoded + */ + private static function encodeEMSA15(string $m, int $emBits, Hash $hash): string + { + $h = $hash->hash($m); + $t = $hash->t(); + $t .= $h; + $tLen = mb_strlen($t, '8bit'); + if ($emBits < $tLen + 11) { + throw new RuntimeException(); + } + $ps = str_repeat(chr(0xFF), $emBits - $tLen - 3); + + return "\0\1{$ps}\0{$t}"; + } +} diff --git a/vendor/web-token/jwt-signature-algorithm-rsa/composer.json b/vendor/web-token/jwt-signature-algorithm-rsa/composer.json new file mode 100644 index 000000000..32b82d727 --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-rsa/composer.json @@ -0,0 +1,47 @@ +{ + "name": "web-token/jwt-signature-algorithm-rsa", + "description": "RSA Based Signature Algorithms the JWT Framework.", + "type": "library", + "license": "MIT", + "keywords": ["JWS", "JWT", "JWE", "JWA", "JWK", "JWKSet", "Jot", "Jose", "RFC7515", "RFC7516", "RFC7517", "RFC7518", "RFC7519", "RFC7520", "Bundle", "Symfony"], + "homepage": "https://github.com/web-token", + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + },{ + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-framework/contributors" + } + ], + "autoload": { + "psr-4": { + "Jose\\Component\\Signature\\Algorithm\\": "" + } + }, + "require": { + "brick/math": "^0.8.17|^0.9", + "ext-openssl": "*", + "web-token/jwt-signature": "^2.1" + }, + "suggest": { + "ext-gmp": "GMP or BCMath is highly recommended to improve the library performance", + "ext-bcmath": "GMP or BCMath is highly recommended to improve the library performance" + }, + "require-dev": { + "phpunit/phpunit": "^8.0" + }, + "extra": { + "branch-alias": { + "v1.0": "1.0.x-dev", + "v1.1": "1.1.x-dev", + "v1.2": "1.2.x-dev", + "v1.3": "1.3.x-dev", + "v2.0": "2.0.x-dev", + "v2.1": "2.1.x-dev" + } + }, + "config": { + "sort-packages": true + } +} diff --git a/vendor/web-token/jwt-signature-algorithm-rsa/phpunit.xml.dist b/vendor/web-token/jwt-signature-algorithm-rsa/phpunit.xml.dist new file mode 100644 index 000000000..c8b3143f3 --- /dev/null +++ b/vendor/web-token/jwt-signature-algorithm-rsa/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + ./Tests/ + + + + + + ./ + + ./vendor + ./Tests + ./src + + + + diff --git a/vendor/web-token/jwt-signature/.github/CONTRIBUTING.md b/vendor/web-token/jwt-signature/.github/CONTRIBUTING.md new file mode 100644 index 000000000..fc360e5d8 --- /dev/null +++ b/vendor/web-token/jwt-signature/.github/CONTRIBUTING.md @@ -0,0 +1,4 @@ +# Contributing + +This repository is a sub repository of [the JWT Framework](https://github.com/web-token/jwt-framework) project and is READ ONLY. +Please do not submit any Pull Requests here. It will be automatically closed. diff --git a/vendor/web-token/jwt-signature/.github/FUNDING.yml b/vendor/web-token/jwt-signature/.github/FUNDING.yml new file mode 100644 index 000000000..7e2ca0e7e --- /dev/null +++ b/vendor/web-token/jwt-signature/.github/FUNDING.yml @@ -0,0 +1 @@ +patreon: FlorentMorselli diff --git a/vendor/web-token/jwt-signature/.github/PULL_REQUEST_TEMPLATE.md b/vendor/web-token/jwt-signature/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..d4ff96cb4 --- /dev/null +++ b/vendor/web-token/jwt-signature/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,3 @@ +Please do not submit any Pull Requests here. It will be automatically closed. + +You should submit it here: https://github.com/web-token/jwt-framework/pulls diff --git a/vendor/web-token/jwt-signature/Algorithm/MacAlgorithm.php b/vendor/web-token/jwt-signature/Algorithm/MacAlgorithm.php new file mode 100644 index 000000000..2304b3679 --- /dev/null +++ b/vendor/web-token/jwt-signature/Algorithm/MacAlgorithm.php @@ -0,0 +1,37 @@ +payload = $payload; + $this->encodedPayload = $encodedPayload; + $this->isPayloadDetached = $isPayloadDetached; + } + + public function getPayload(): ?string + { + return $this->payload; + } + + /** + * Returns true if the payload is detached. + */ + public function isPayloadDetached(): bool + { + return $this->isPayloadDetached; + } + + /** + * Returns the Base64Url encoded payload. + * If the payload is detached, this method returns null. + */ + public function getEncodedPayload(): ?string + { + if (true === $this->isPayloadDetached()) { + return null; + } + + return $this->encodedPayload; + } + + /** + * Returns the signatures associated with the JWS. + * + * @return Signature[] + */ + public function getSignatures(): array + { + return $this->signatures; + } + + /** + * Returns the signature at the given index. + * + * @throws InvalidArgumentException if the signature index does not exist + */ + public function getSignature(int $id): Signature + { + if (isset($this->signatures[$id])) { + return $this->signatures[$id]; + } + + throw new InvalidArgumentException('The signature does not exist.'); + } + + /** + * This method adds a signature to the JWS object. + * Its returns a new JWS object. + * + * @internal + * + * @return JWS + */ + public function addSignature(string $signature, array $protectedHeader, ?string $encodedProtectedHeader, array $header = []): self + { + $jws = clone $this; + $jws->signatures[] = new Signature($signature, $protectedHeader, $encodedProtectedHeader, $header); + + return $jws; + } + + /** + * Returns the number of signature associated with the JWS. + */ + public function countSignatures(): int + { + return count($this->signatures); + } + + /** + * This method splits the JWS into a list of JWSs. + * It is only useful when the JWS contains more than one signature (JSON General Serialization). + * + * @return JWS[] + */ + public function split(): array + { + $result = []; + foreach ($this->signatures as $signature) { + $jws = new self( + $this->payload, + $this->encodedPayload, + $this->isPayloadDetached + ); + $jws = $jws->addSignature( + $signature->getSignature(), + $signature->getProtectedHeader(), + $signature->getEncodedProtectedHeader(), + $signature->getHeader() + ); + + $result[] = $jws; + } + + return $result; + } +} diff --git a/vendor/web-token/jwt-signature/JWSBuilder.php b/vendor/web-token/jwt-signature/JWSBuilder.php new file mode 100644 index 000000000..afffd12f5 --- /dev/null +++ b/vendor/web-token/jwt-signature/JWSBuilder.php @@ -0,0 +1,235 @@ +signatureAlgorithmManager = $signatureAlgorithmManager; + } + + /** + * Returns the algorithm manager associated to the builder. + */ + public function getSignatureAlgorithmManager(): AlgorithmManager + { + return $this->signatureAlgorithmManager; + } + + /** + * Reset the current data. + * + * @return JWSBuilder + */ + public function create(): self + { + $this->payload = null; + $this->isPayloadDetached = false; + $this->signatures = []; + $this->isPayloadEncoded = null; + + return $this; + } + + /** + * Set the payload. + * This method will return a new JWSBuilder object. + * + * @throws InvalidArgumentException if the payload is not UTF-8 encoded + * + * @return JWSBuilder + */ + public function withPayload(string $payload, bool $isPayloadDetached = false): self + { + if (false === mb_detect_encoding($payload, 'UTF-8', true)) { + throw new InvalidArgumentException('The payload must be encoded in UTF-8'); + } + $clone = clone $this; + $clone->payload = $payload; + $clone->isPayloadDetached = $isPayloadDetached; + + return $clone; + } + + /** + * Adds the information needed to compute the signature. + * This method will return a new JWSBuilder object. + * + * @throws InvalidArgumentException if the payload encoding is inconsistent + * + * @return JWSBuilder + */ + public function addSignature(JWK $signatureKey, array $protectedHeader, array $header = []): self + { + $this->checkB64AndCriticalHeader($protectedHeader); + $isPayloadEncoded = $this->checkIfPayloadIsEncoded($protectedHeader); + if (null === $this->isPayloadEncoded) { + $this->isPayloadEncoded = $isPayloadEncoded; + } elseif ($this->isPayloadEncoded !== $isPayloadEncoded) { + throw new InvalidArgumentException('Foreign payload encoding detected.'); + } + $this->checkDuplicatedHeaderParameters($protectedHeader, $header); + KeyChecker::checkKeyUsage($signatureKey, 'signature'); + $algorithm = $this->findSignatureAlgorithm($signatureKey, $protectedHeader, $header); + KeyChecker::checkKeyAlgorithm($signatureKey, $algorithm->name()); + $clone = clone $this; + $clone->signatures[] = [ + 'signature_algorithm' => $algorithm, + 'signature_key' => $signatureKey, + 'protected_header' => $protectedHeader, + 'header' => $header, + ]; + + return $clone; + } + + /** + * Computes all signatures and return the expected JWS object. + * + * @throws RuntimeException if the payload is not set + * @throws RuntimeException if no signature is defined + */ + public function build(): JWS + { + if (null === $this->payload) { + throw new RuntimeException('The payload is not set.'); + } + if (0 === count($this->signatures)) { + throw new RuntimeException('At least one signature must be set.'); + } + + $encodedPayload = false === $this->isPayloadEncoded ? $this->payload : Base64Url::encode($this->payload); + $jws = new JWS($this->payload, $encodedPayload, $this->isPayloadDetached); + foreach ($this->signatures as $signature) { + /** @var MacAlgorithm|SignatureAlgorithm $algorithm */ + $algorithm = $signature['signature_algorithm']; + /** @var JWK $signatureKey */ + $signatureKey = $signature['signature_key']; + /** @var array $protectedHeader */ + $protectedHeader = $signature['protected_header']; + /** @var array $header */ + $header = $signature['header']; + $encodedProtectedHeader = 0 === count($protectedHeader) ? null : Base64Url::encode(JsonConverter::encode($protectedHeader)); + $input = sprintf('%s.%s', $encodedProtectedHeader, $encodedPayload); + if ($algorithm instanceof SignatureAlgorithm) { + $s = $algorithm->sign($signatureKey, $input); + } else { + $s = $algorithm->hash($signatureKey, $input); + } + $jws = $jws->addSignature($s, $protectedHeader, $encodedProtectedHeader, $header); + } + + return $jws; + } + + private function checkIfPayloadIsEncoded(array $protectedHeader): bool + { + return !array_key_exists('b64', $protectedHeader) || true === $protectedHeader['b64']; + } + + /** + * @throws LogicException if the header parameter "crit" is missing, invalid or does not contain "b64" when "b64" is set + */ + private function checkB64AndCriticalHeader(array $protectedHeader): void + { + if (!array_key_exists('b64', $protectedHeader)) { + return; + } + if (!array_key_exists('crit', $protectedHeader)) { + throw new LogicException('The protected header parameter "crit" is mandatory when protected header parameter "b64" is set.'); + } + if (!is_array($protectedHeader['crit'])) { + throw new LogicException('The protected header parameter "crit" must be an array.'); + } + if (!in_array('b64', $protectedHeader['crit'], true)) { + throw new LogicException('The protected header parameter "crit" must contain "b64" when protected header parameter "b64" is set.'); + } + } + + /** + * @throws InvalidArgumentException if the header parameter "alg" is missing or the algorithm is not allowed/not supported + * + * @return MacAlgorithm|SignatureAlgorithm + */ + private function findSignatureAlgorithm(JWK $key, array $protectedHeader, array $header): Algorithm + { + $completeHeader = array_merge($header, $protectedHeader); + if (!array_key_exists('alg', $completeHeader)) { + throw new InvalidArgumentException('No "alg" parameter set in the header.'); + } + if ($key->has('alg') && $key->get('alg') !== $completeHeader['alg']) { + throw new InvalidArgumentException(sprintf('The algorithm "%s" is not allowed with this key.', $completeHeader['alg'])); + } + + $algorithm = $this->signatureAlgorithmManager->get($completeHeader['alg']); + if (!$algorithm instanceof SignatureAlgorithm && !$algorithm instanceof MacAlgorithm) { + throw new InvalidArgumentException(sprintf('The algorithm "%s" is not supported.', $completeHeader['alg'])); + } + + return $algorithm; + } + + /** + * @throws InvalidArgumentException if the header contains duplicated entries + */ + private function checkDuplicatedHeaderParameters(array $header1, array $header2): void + { + $inter = array_intersect_key($header1, $header2); + if (0 !== count($inter)) { + throw new InvalidArgumentException(sprintf('The header contains duplicated entries: %s.', implode(', ', array_keys($inter)))); + } + } +} diff --git a/vendor/web-token/jwt-signature/JWSBuilderFactory.php b/vendor/web-token/jwt-signature/JWSBuilderFactory.php new file mode 100644 index 000000000..dbdf115aa --- /dev/null +++ b/vendor/web-token/jwt-signature/JWSBuilderFactory.php @@ -0,0 +1,41 @@ +signatureAlgorithmManagerFactory = $signatureAlgorithmManagerFactory; + } + + /** + * This method creates a JWSBuilder using the given algorithm aliases. + * + * @param string[] $algorithms + */ + public function create(array $algorithms): JWSBuilder + { + $algorithmManager = $this->signatureAlgorithmManagerFactory->create($algorithms); + + return new JWSBuilder($algorithmManager); + } +} diff --git a/vendor/web-token/jwt-signature/JWSLoader.php b/vendor/web-token/jwt-signature/JWSLoader.php new file mode 100644 index 000000000..f19b449e6 --- /dev/null +++ b/vendor/web-token/jwt-signature/JWSLoader.php @@ -0,0 +1,124 @@ +serializerManager = $serializerManager; + $this->jwsVerifier = $jwsVerifier; + $this->headerCheckerManager = $headerCheckerManager; + } + + /** + * Returns the JWSVerifier associated to the JWSLoader. + */ + public function getJwsVerifier(): JWSVerifier + { + return $this->jwsVerifier; + } + + /** + * Returns the Header Checker Manager associated to the JWSLoader. + */ + public function getHeaderCheckerManager(): ?HeaderCheckerManager + { + return $this->headerCheckerManager; + } + + /** + * Returns the JWSSerializer associated to the JWSLoader. + */ + public function getSerializerManager(): JWSSerializerManager + { + return $this->serializerManager; + } + + /** + * This method will try to load and verify the token using the given key. + * It returns a JWS and will populate the $signature variable in case of success, otherwise an exception is thrown. + * + * @throws Exception if the token cannot be loaded or verified + */ + public function loadAndVerifyWithKey(string $token, JWK $key, ?int &$signature, ?string $payload = null): JWS + { + $keyset = new JWKSet([$key]); + + return $this->loadAndVerifyWithKeySet($token, $keyset, $signature, $payload); + } + + /** + * This method will try to load and verify the token using the given key set. + * It returns a JWS and will populate the $signature variable in case of success, otherwise an exception is thrown. + * + * @throws Exception if the token cannot be loaded or verified + */ + public function loadAndVerifyWithKeySet(string $token, JWKSet $keyset, ?int &$signature, ?string $payload = null): JWS + { + try { + $jws = $this->serializerManager->unserialize($token); + $nbSignatures = $jws->countSignatures(); + for ($i = 0; $i < $nbSignatures; ++$i) { + if ($this->processSignature($jws, $keyset, $i, $payload)) { + $signature = $i; + + return $jws; + } + } + } catch (Throwable $e) { + // Nothing to do. Exception thrown just after + } + + throw new Exception('Unable to load and verify the token.'); + } + + private function processSignature(JWS $jws, JWKSet $keyset, int $signature, ?string $payload): bool + { + try { + if (null !== $this->headerCheckerManager) { + $this->headerCheckerManager->check($jws, $signature); + } + + return $this->jwsVerifier->verifyWithKeySet($jws, $keyset, $signature, $payload); + } catch (Throwable $e) { + return false; + } + } +} diff --git a/vendor/web-token/jwt-signature/JWSLoaderFactory.php b/vendor/web-token/jwt-signature/JWSLoaderFactory.php new file mode 100644 index 000000000..8f29650cf --- /dev/null +++ b/vendor/web-token/jwt-signature/JWSLoaderFactory.php @@ -0,0 +1,59 @@ +jwsSerializerManagerFactory = $jwsSerializerManagerFactory; + $this->jwsVerifierFactory = $jwsVerifierFactory; + $this->headerCheckerManagerFactory = $headerCheckerManagerFactory; + } + + /** + * Creates a JWSLoader using the given serializer aliases, signature algorithm aliases and (optionally) + * the header checker aliases. + */ + public function create(array $serializers, array $algorithms, array $headerCheckers = []): JWSLoader + { + $serializerManager = $this->jwsSerializerManagerFactory->create($serializers); + $jwsVerifier = $this->jwsVerifierFactory->create($algorithms); + if (null !== $this->headerCheckerManagerFactory) { + $headerCheckerManager = $this->headerCheckerManagerFactory->create($headerCheckers); + } else { + $headerCheckerManager = null; + } + + return new JWSLoader($serializerManager, $jwsVerifier, $headerCheckerManager); + } +} diff --git a/vendor/web-token/jwt-signature/JWSTokenSupport.php b/vendor/web-token/jwt-signature/JWSTokenSupport.php new file mode 100644 index 000000000..f2f586f39 --- /dev/null +++ b/vendor/web-token/jwt-signature/JWSTokenSupport.php @@ -0,0 +1,42 @@ + $jwt->countSignatures()) { + throw new InvalidArgumentException('Unknown signature index.'); + } + $protectedHeader = $jwt->getSignature($index)->getProtectedHeader(); + $unprotectedHeader = $jwt->getSignature($index)->getHeader(); + } +} diff --git a/vendor/web-token/jwt-signature/JWSVerifier.php b/vendor/web-token/jwt-signature/JWSVerifier.php new file mode 100644 index 000000000..42d12a66d --- /dev/null +++ b/vendor/web-token/jwt-signature/JWSVerifier.php @@ -0,0 +1,170 @@ +signatureAlgorithmManager = $signatureAlgorithmManager; + } + + /** + * Returns the algorithm manager associated to the JWSVerifier. + */ + public function getSignatureAlgorithmManager(): AlgorithmManager + { + return $this->signatureAlgorithmManager; + } + + /** + * This method will try to verify the JWS object using the given key and for the given signature. + * It returns true if the signature is verified, otherwise false. + * + * @return bool true if the verification of the signature succeeded, else false + */ + public function verifyWithKey(JWS $jws, JWK $jwk, int $signature, ?string $detachedPayload = null): bool + { + $jwkset = new JWKSet([$jwk]); + + return $this->verifyWithKeySet($jws, $jwkset, $signature, $detachedPayload); + } + + /** + * This method will try to verify the JWS object using the given key set and for the given signature. + * It returns true if the signature is verified, otherwise false. + * + * @param JWS $jws A JWS object + * @param JWKSet $jwkset The signature will be verified using keys in the key set + * @param JWK $jwk The key used to verify the signature in case of success + * @param null|string $detachedPayload If not null, the value must be the detached payload encoded in Base64 URL safe. If the input contains a payload, throws an exception. + * + * @throws InvalidArgumentException if there is no key in the keyset + * @throws InvalidArgumentException if the token does not contain any signature + * + * @return bool true if the verification of the signature succeeded, else false + */ + public function verifyWithKeySet(JWS $jws, JWKSet $jwkset, int $signatureIndex, ?string $detachedPayload = null, JWK &$jwk = null): bool + { + if (0 === $jwkset->count()) { + throw new InvalidArgumentException('There is no key in the key set.'); + } + if (0 === $jws->countSignatures()) { + throw new InvalidArgumentException('The JWS does not contain any signature.'); + } + $this->checkPayload($jws, $detachedPayload); + $signature = $jws->getSignature($signatureIndex); + + return $this->verifySignature($jws, $jwkset, $signature, $detachedPayload, $jwk); + } + + private function verifySignature(JWS $jws, JWKSet $jwkset, Signature $signature, ?string $detachedPayload = null, JWK &$successJwk = null): bool + { + $input = $this->getInputToVerify($jws, $signature, $detachedPayload); + $algorithm = $this->getAlgorithm($signature); + foreach ($jwkset->all() as $jwk) { + try { + KeyChecker::checkKeyUsage($jwk, 'verification'); + KeyChecker::checkKeyAlgorithm($jwk, $algorithm->name()); + if (true === $algorithm->verify($jwk, $input, $signature->getSignature())) { + $successJwk = $jwk; + + return true; + } + } catch (Throwable $e) { + //We do nothing, we continue with other keys + continue; + } + } + + return false; + } + + private function getInputToVerify(JWS $jws, Signature $signature, ?string $detachedPayload): string + { + $isPayloadEmpty = $this->isPayloadEmpty($jws->getPayload()); + $encodedProtectedHeader = $signature->getEncodedProtectedHeader(); + if (!$signature->hasProtectedHeaderParameter('b64') || true === $signature->getProtectedHeaderParameter('b64')) { + if (null !== $jws->getEncodedPayload()) { + return sprintf('%s.%s', $encodedProtectedHeader, $jws->getEncodedPayload()); + } + + $payload = $isPayloadEmpty ? $detachedPayload : $jws->getPayload(); + + return sprintf('%s.%s', $encodedProtectedHeader, Base64Url::encode($payload)); + } + + $payload = $isPayloadEmpty ? $detachedPayload : $jws->getPayload(); + + return sprintf('%s.%s', $encodedProtectedHeader, $payload); + } + + /** + * @throws InvalidArgumentException if the payload is set when a detached payload is provided or no payload is defined + */ + private function checkPayload(JWS $jws, ?string $detachedPayload = null): void + { + $isPayloadEmpty = $this->isPayloadEmpty($jws->getPayload()); + if (null !== $detachedPayload && !$isPayloadEmpty) { + throw new InvalidArgumentException('A detached payload is set, but the JWS already has a payload.'); + } + if ($isPayloadEmpty && null === $detachedPayload) { + throw new InvalidArgumentException('The JWS has a detached payload, but no payload is provided.'); + } + } + + /** + * @throws InvalidArgumentException if the header parameter "alg" is missing or invalid + * + * @return MacAlgorithm|SignatureAlgorithm + */ + private function getAlgorithm(Signature $signature): Algorithm + { + $completeHeader = array_merge($signature->getProtectedHeader(), $signature->getHeader()); + if (!isset($completeHeader['alg'])) { + throw new InvalidArgumentException('No "alg" parameter set in the header.'); + } + + $algorithm = $this->signatureAlgorithmManager->get($completeHeader['alg']); + if (!$algorithm instanceof SignatureAlgorithm && !$algorithm instanceof MacAlgorithm) { + throw new InvalidArgumentException(sprintf('The algorithm "%s" is not supported or is not a signature or MAC algorithm.', $completeHeader['alg'])); + } + + return $algorithm; + } + + private function isPayloadEmpty(?string $payload): bool + { + return null === $payload || '' === $payload; + } +} diff --git a/vendor/web-token/jwt-signature/JWSVerifierFactory.php b/vendor/web-token/jwt-signature/JWSVerifierFactory.php new file mode 100644 index 000000000..67a65e842 --- /dev/null +++ b/vendor/web-token/jwt-signature/JWSVerifierFactory.php @@ -0,0 +1,41 @@ +algorithmManagerFactory = $algorithmManagerFactory; + } + + /** + * Creates a JWSVerifier using the given signature algorithm aliases. + * + * @param string[] $algorithms + */ + public function create(array $algorithms): JWSVerifier + { + $algorithmManager = $this->algorithmManagerFactory->create($algorithms); + + return new JWSVerifier($algorithmManager); + } +} diff --git a/vendor/web-token/jwt-signature/LICENSE b/vendor/web-token/jwt-signature/LICENSE new file mode 100644 index 000000000..37cf976b1 --- /dev/null +++ b/vendor/web-token/jwt-signature/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014-2019 Spomky-Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/web-token/jwt-signature/README.md b/vendor/web-token/jwt-signature/README.md new file mode 100644 index 000000000..26f0befcb --- /dev/null +++ b/vendor/web-token/jwt-signature/README.md @@ -0,0 +1,15 @@ +PHP JWT Signature Component +=========================== + +This repository is a sub repository of [the JWT Framework](https://github.com/web-token/jwt-framework) project and is READ ONLY. + +**Please do not submit any Pull Request here.** +You should go to [the main repository](https://github.com/web-token/jwt-framework) instead. + +# Documentation + +The official documentation is available as https://web-token.spomky-labs.com/ + +# Licence + +This software is release under [MIT licence](LICENSE). diff --git a/vendor/web-token/jwt-signature/Serializer/CompactSerializer.php b/vendor/web-token/jwt-signature/Serializer/CompactSerializer.php new file mode 100644 index 000000000..a4bdab26d --- /dev/null +++ b/vendor/web-token/jwt-signature/Serializer/CompactSerializer.php @@ -0,0 +1,96 @@ +getSignature($signatureIndex); + if (0 !== count($signature->getHeader())) { + throw new LogicException('The signature contains unprotected header parameters and cannot be converted into compact JSON.'); + } + $isEmptyPayload = null === $jws->getEncodedPayload() || '' === $jws->getEncodedPayload(); + if (!$this->isPayloadEncoded($signature->getProtectedHeader()) && !$isEmptyPayload) { + if (1 !== preg_match('/^[\x{20}-\x{2d}|\x{2f}-\x{7e}]*$/u', $jws->getPayload())) { + throw new LogicException('Unable to convert the JWS with non-encoded payload.'); + } + } + + return sprintf( + '%s.%s.%s', + $signature->getEncodedProtectedHeader(), + $jws->getEncodedPayload(), + Base64Url::encode($signature->getSignature()) + ); + } + + /** + * @throws InvalidArgumentException if the input is invalid + */ + public function unserialize(string $input): JWS + { + $parts = explode('.', $input); + if (3 !== count($parts)) { + throw new InvalidArgumentException('Unsupported input'); + } + + try { + $encodedProtectedHeader = $parts[0]; + $protectedHeader = JsonConverter::decode(Base64Url::decode($parts[0])); + $hasPayload = '' !== $parts[1]; + if (!$hasPayload) { + $payload = null; + $encodedPayload = null; + } else { + $encodedPayload = $parts[1]; + $payload = $this->isPayloadEncoded($protectedHeader) ? Base64Url::decode($encodedPayload) : $encodedPayload; + } + $signature = Base64Url::decode($parts[2]); + + $jws = new JWS($payload, $encodedPayload, !$hasPayload); + + return $jws->addSignature($signature, $protectedHeader, $encodedProtectedHeader); + } catch (Throwable $throwable) { + throw new InvalidArgumentException('Unsupported input', $throwable->getCode(), $throwable); + } + } +} diff --git a/vendor/web-token/jwt-signature/Serializer/JSONFlattenedSerializer.php b/vendor/web-token/jwt-signature/Serializer/JSONFlattenedSerializer.php new file mode 100644 index 000000000..0d4f54ec6 --- /dev/null +++ b/vendor/web-token/jwt-signature/Serializer/JSONFlattenedSerializer.php @@ -0,0 +1,110 @@ +getSignature($signatureIndex); + + $data = []; + $values = [ + 'payload' => $jws->getEncodedPayload(), + 'protected' => $signature->getEncodedProtectedHeader(), + 'header' => $signature->getHeader(), + ]; + $encodedPayload = $jws->getEncodedPayload(); + if (null !== $encodedPayload && '' !== $encodedPayload) { + $data['payload'] = $encodedPayload; + } + $encodedProtectedHeader = $signature->getEncodedProtectedHeader(); + if (null !== $encodedProtectedHeader && '' !== $encodedProtectedHeader) { + $data['protected'] = $encodedProtectedHeader; + } + $header = $signature->getHeader(); + if (0 !== count($header)) { + $data['header'] = $header; + } + $data['signature'] = Base64Url::encode($signature->getSignature()); + + return JsonConverter::encode($data); + } + + /** + * @throws InvalidArgumentException if the input is not supported + * @throws InvalidArgumentException if the JWS header is invalid + */ + public function unserialize(string $input): JWS + { + $data = JsonConverter::decode($input); + if (!is_array($data)) { + throw new InvalidArgumentException('Unsupported input.'); + } + if (!isset($data['signature'])) { + throw new InvalidArgumentException('Unsupported input.'); + } + $signature = Base64Url::decode($data['signature']); + + if (isset($data['protected'])) { + $encodedProtectedHeader = $data['protected']; + $protectedHeader = JsonConverter::decode(Base64Url::decode($data['protected'])); + } else { + $encodedProtectedHeader = null; + $protectedHeader = []; + } + if (isset($data['header'])) { + if (!is_array($data['header'])) { + throw new InvalidArgumentException('Bad header.'); + } + $header = $data['header']; + } else { + $header = []; + } + + if (isset($data['payload'])) { + $encodedPayload = $data['payload']; + $payload = $this->isPayloadEncoded($protectedHeader) ? Base64Url::decode($encodedPayload) : $encodedPayload; + } else { + $payload = null; + $encodedPayload = null; + } + + $jws = new JWS($payload, $encodedPayload, null === $encodedPayload); + + return $jws->addSignature($signature, $protectedHeader, $encodedProtectedHeader, $header); + } +} diff --git a/vendor/web-token/jwt-signature/Serializer/JSONGeneralSerializer.php b/vendor/web-token/jwt-signature/Serializer/JSONGeneralSerializer.php new file mode 100644 index 000000000..25f603212 --- /dev/null +++ b/vendor/web-token/jwt-signature/Serializer/JSONGeneralSerializer.php @@ -0,0 +1,167 @@ +countSignatures()) { + throw new LogicException('No signature.'); + } + + $data = []; + $this->checkPayloadEncoding($jws); + + if (false === $jws->isPayloadDetached()) { + $data['payload'] = $jws->getEncodedPayload(); + } + + $data['signatures'] = []; + foreach ($jws->getSignatures() as $signature) { + $tmp = ['signature' => Base64Url::encode($signature->getSignature())]; + $values = [ + 'protected' => $signature->getEncodedProtectedHeader(), + 'header' => $signature->getHeader(), + ]; + + foreach ($values as $key => $value) { + if ((is_string($value) && '' !== $value) || (is_array($value) && 0 !== count($value))) { + $tmp[$key] = $value; + } + } + $data['signatures'][] = $tmp; + } + + return JsonConverter::encode($data); + } + + /** + * @throws InvalidArgumentException if the input is not supported + */ + public function unserialize(string $input): JWS + { + $data = JsonConverter::decode($input); + if (!isset($data['signatures'])) { + throw new InvalidArgumentException('Unsupported input.'); + } + + $isPayloadEncoded = null; + $rawPayload = $data['payload'] ?? null; + $signatures = []; + foreach ($data['signatures'] as $signature) { + if (!isset($signature['signature'])) { + throw new InvalidArgumentException('Unsupported input.'); + } + list($encodedProtectedHeader, $protectedHeader, $header) = $this->processHeaders($signature); + $signatures[] = [ + 'signature' => Base64Url::decode($signature['signature']), + 'protected' => $protectedHeader, + 'encoded_protected' => $encodedProtectedHeader, + 'header' => $header, + ]; + $isPayloadEncoded = $this->processIsPayloadEncoded($isPayloadEncoded, $protectedHeader); + } + + $payload = $this->processPayload($rawPayload, $isPayloadEncoded); + $jws = new JWS($payload, $rawPayload); + foreach ($signatures as $signature) { + $jws = $jws->addSignature( + $signature['signature'], + $signature['protected'], + $signature['encoded_protected'], + $signature['header'] + ); + } + + return $jws; + } + + /** + * @throws InvalidArgumentException if the payload encoding is invalid + */ + private function processIsPayloadEncoded(?bool $isPayloadEncoded, array $protectedHeader): bool + { + if (null === $isPayloadEncoded) { + return $this->isPayloadEncoded($protectedHeader); + } + if ($this->isPayloadEncoded($protectedHeader) !== $isPayloadEncoded) { + throw new InvalidArgumentException('Foreign payload encoding detected.'); + } + + return $isPayloadEncoded; + } + + private function processHeaders(array $signature): array + { + $encodedProtectedHeader = $signature['protected'] ?? null; + $protectedHeader = null === $encodedProtectedHeader ? [] : JsonConverter::decode(Base64Url::decode($encodedProtectedHeader)); + $header = array_key_exists('header', $signature) ? $signature['header'] : []; + + return [$encodedProtectedHeader, $protectedHeader, $header]; + } + + private function processPayload(?string $rawPayload, ?bool $isPayloadEncoded): ?string + { + if (null === $rawPayload) { + return null; + } + + return false === $isPayloadEncoded ? $rawPayload : Base64Url::decode($rawPayload); + } + + // @throws LogicException if the payload encoding is invalid + private function checkPayloadEncoding(JWS $jws): void + { + if ($jws->isPayloadDetached()) { + return; + } + $is_encoded = null; + foreach ($jws->getSignatures() as $signature) { + if (null === $is_encoded) { + $is_encoded = $this->isPayloadEncoded($signature->getProtectedHeader()); + } + if (false === $jws->isPayloadDetached()) { + if ($is_encoded !== $this->isPayloadEncoded($signature->getProtectedHeader())) { + throw new LogicException('Foreign payload encoding detected.'); + } + } + } + } +} diff --git a/vendor/web-token/jwt-signature/Serializer/JWSSerializer.php b/vendor/web-token/jwt-signature/Serializer/JWSSerializer.php new file mode 100644 index 000000000..c2edd373f --- /dev/null +++ b/vendor/web-token/jwt-signature/Serializer/JWSSerializer.php @@ -0,0 +1,38 @@ +add($serializer); + } + } + + /** + * @return string[] + */ + public function list(): array + { + return array_keys($this->serializers); + } + + /** + * Converts a JWS into a string. + * + * @throws InvalidArgumentException if the serializer is not supported + */ + public function serialize(string $name, JWS $jws, ?int $signatureIndex = null): string + { + if (!isset($this->serializers[$name])) { + throw new InvalidArgumentException(sprintf('Unsupported serializer "%s".', $name)); + } + + return $this->serializers[$name]->serialize($jws, $signatureIndex); + } + + /** + * Loads data and return a JWS object. + * + * @param string $input A string that represents a JWS + * @param null|string $name the name of the serializer if the input is unserialized + * + * @throws InvalidArgumentException if the input is not supported + */ + public function unserialize(string $input, ?string &$name = null): JWS + { + foreach ($this->serializers as $serializer) { + try { + $jws = $serializer->unserialize($input); + $name = $serializer->name(); + + return $jws; + } catch (InvalidArgumentException $e) { + continue; + } + } + + throw new InvalidArgumentException('Unsupported input.'); + } + + private function add(JWSSerializer $serializer): void + { + $this->serializers[$serializer->name()] = $serializer; + } +} diff --git a/vendor/web-token/jwt-signature/Serializer/JWSSerializerManagerFactory.php b/vendor/web-token/jwt-signature/Serializer/JWSSerializerManagerFactory.php new file mode 100644 index 000000000..7e346eedf --- /dev/null +++ b/vendor/web-token/jwt-signature/Serializer/JWSSerializerManagerFactory.php @@ -0,0 +1,63 @@ +serializers[$name])) { + throw new InvalidArgumentException(sprintf('Unsupported serializer "%s".', $name)); + } + $serializers[] = $this->serializers[$name]; + } + + return new JWSSerializerManager($serializers); + } + + /** + * @return string[] + */ + public function names(): array + { + return array_keys($this->serializers); + } + + /** + * @return JWSSerializer[] + */ + public function all(): array + { + return $this->serializers; + } + + public function add(JWSSerializer $serializer): void + { + $this->serializers[$serializer->name()] = $serializer; + } +} diff --git a/vendor/web-token/jwt-signature/Serializer/Serializer.php b/vendor/web-token/jwt-signature/Serializer/Serializer.php new file mode 100644 index 000000000..48a11477a --- /dev/null +++ b/vendor/web-token/jwt-signature/Serializer/Serializer.php @@ -0,0 +1,24 @@ +protectedHeader = null === $encodedProtectedHeader ? [] : $protectedHeader; + $this->encodedProtectedHeader = $encodedProtectedHeader; + $this->signature = $signature; + $this->header = $header; + } + + /** + * The protected header associated with the signature. + */ + public function getProtectedHeader(): array + { + return $this->protectedHeader; + } + + /** + * The unprotected header associated with the signature. + */ + public function getHeader(): array + { + return $this->header; + } + + /** + * The protected header associated with the signature. + */ + public function getEncodedProtectedHeader(): ?string + { + return $this->encodedProtectedHeader; + } + + /** + * Returns the value of the protected header of the specified key. + * + * @param string $key The key + * + * @throws InvalidArgumentException if the header parameter does not exist + * + * @return null|mixed Header value + */ + public function getProtectedHeaderParameter(string $key) + { + if ($this->hasProtectedHeaderParameter($key)) { + return $this->getProtectedHeader()[$key]; + } + + throw new InvalidArgumentException(sprintf('The protected header "%s" does not exist', $key)); + } + + /** + * Returns true if the protected header has the given parameter. + * + * @param string $key The key + */ + public function hasProtectedHeaderParameter(string $key): bool + { + return array_key_exists($key, $this->getProtectedHeader()); + } + + /** + * Returns the value of the unprotected header of the specified key. + * + * @param string $key The key + * + * @return null|mixed Header value + */ + public function getHeaderParameter(string $key) + { + if ($this->hasHeaderParameter($key)) { + return $this->header[$key]; + } + + throw new InvalidArgumentException(sprintf('The header "%s" does not exist', $key)); + } + + /** + * Returns true if the unprotected header has the given parameter. + * + * @param string $key The key + */ + public function hasHeaderParameter(string $key): bool + { + return array_key_exists($key, $this->header); + } + + /** + * Returns the value of the signature. + */ + public function getSignature(): string + { + return $this->signature; + } +} diff --git a/vendor/web-token/jwt-signature/Tests/ForeignJWTTest.php b/vendor/web-token/jwt-signature/Tests/ForeignJWTTest.php new file mode 100644 index 000000000..e627f5e87 --- /dev/null +++ b/vendor/web-token/jwt-signature/Tests/ForeignJWTTest.php @@ -0,0 +1,56 @@ + 'RSA', + 'kid' => 'public', + 'n' => 'rYYOdiGrtRzCcV179qEI7TN-kkdmL37zJ3qugImaoOKbtw9EUwZGyMkcYp48eVksOwT7bxSn1hzP_n75Jlwl85MMAYIqKiQFHjjUVHBAD6HWFHsriod6-fdJxsXDhJ4lDoWxIQFLEKhGo3QeIYO0b6iwuSSIR2qO8sOCmmEngvq4OfyZz11mTpztl5cObeal8f6lQ5UHFUCXfx_QLnkrrTMuRioFZ1lEn2MhGm9Mx8eATY8OXUsK6L47LYP7aiWFKepesX4Tk16aKoB2GdlDO3-TG0aAYe89Ar7rGaoW39EYAuzxpbMka2Pp83Re4dEzMKMXy-mbGMTh5waqHIE9L9Rwldi2CaRrLgBBuMF_XyrCL4nMbEQ7xbVDxkayZ1sOir3TbrV9Z-bRjNNQhPl_zmfttyTEk18EyXhIwOVxjRmMdbPbP_K93o3h7_-mYTRgpoUM93X_3ec-lnyDHhSX2IrRe9z3eerzu4c7l3XV8eWhqIYWOw_AyArK1XxSlJhcSwWAFBXt7fYHGoT-wOI3lr7mJb8hqIMIOxA3M9-3NK_IPPjBcKQHrpUKQBulaYGCSlbIgUIkMDoxU4RaRAbR_31JLi9ZEgTmKjg7Db6I-omIlBSqdPZIEVQpHgGPlMMfKD05cYfXg82b5M_xuGNHXaFm_MkCJnKDq4NKx4ePUkM', + 'e' => 'AQAB', + ]); + + $challenge = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJmNTIyMjI3OS1kNTQ0LTRjM2QtYWM3Yy1mYzU0NzE1ZDBjMjYiLCJleHAiOjE0Njk2NDkyMDksImp0aSI6IjZhZjExNDk3LTdkNmItNDQ5Ny04NDI1LTc0YmExM2E1ODMxYyIsInJlZGlyIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NDQ0NC9vYXV0aDIvYXV0aD9jbGllbnRfaWQ9ZjUyMjIyNzktZDU0NC00YzNkLWFjN2MtZmM1NDcxNWQwYzI2XHUwMDI2cmVzcG9uc2VfdHlwZT1jb2RlXHUwMDI2c2NvcGU9Y29yZStmb29cdTAwMjZzdGF0ZT1hYmNkZWZnaFx1MDAyNnJlZGlyZWN0X3VyaT1odHRwOi8vbG9jYWxob3N0L2NiIiwic2NwIjpbImNvcmUiLCJmb28iXX0.U3fu5eJVDD5tpOa9O3SejMh78skCj6a3rv5qWqzmME2WF0R9QnTR6TS5g6OOCh86o03AlHi2LhE4GSoUmC8WMPzXopDSdZdEkuifYUOSjRQV3Mp5tn6ozkkB75TtIhM8x5_QV3YKgTy3bcojg-Nx3ix43ENGdnbaCZ6Sxqj4xDknh7pHtsUiHfHZL7jd0I0xP5TYOw0_rxhuK9UZKtt_o05sFnNr0PW1k5d6aU5qJoBNVAendr_evrzXIV0yCC_odj5KySsNaQiXjUX_Tri2_5gSgcr8t3GMRm-HjDJRttwD3vgQG_K3vuToB-JAtHNMDcqmPjLzEFFkRDeh55kHgPJlYzSdwWD52b9sX5fj-VrRLdQzO2VVVkP7a9GoCGS06ypV9R_yGK8HzKJ8uB12dTNmplo03v4vdWxVdsnWxmBJ0m7G7yBCr-iGi87ezowpkMw22rNBkqnaEZIVbmX5E-G3UncE6io3IizEGH4YcGxWSk_D2fCII6X9uncf2rwslhEMiGC6rwlrL8dgl3kJTB4d0s2wIKgWJwEfLkiamJ2CJp6x0tqG7ozWv3k1tNQaZ9OwaulZ7nbmHgalyIOI2k-emMhFZsdnAtCxtcrxleevoiYF-Q54h1BhYInQT6Ejx7CdKOTEjljttB7lcqqpboSblw8Ji7lxUiKHWyGhcPI'; + + $jwsVerifier = $this->getJWSVerifierFactory()->create(['RS256']); + $jwt = $this->getJWSSerializerManager()->unserialize($challenge); + static::assertTrue($jwsVerifier->verifyWithKey($jwt, $jwk, 0)); + + $expectedHeader = [ + 'alg' => 'RS256', + 'typ' => 'JWT', + ]; + + static::assertEquals($expectedHeader, $jwt->getSignature(0)->getProtectedHeader()); + } +} diff --git a/vendor/web-token/jwt-signature/Tests/JWSFlattenedTest.php b/vendor/web-token/jwt-signature/Tests/JWSFlattenedTest.php new file mode 100644 index 000000000..36268e28b --- /dev/null +++ b/vendor/web-token/jwt-signature/Tests/JWSFlattenedTest.php @@ -0,0 +1,35 @@ +getJWSSerializerManager()->unserialize('{"payload":"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ","protected":"eyJhbGciOiJFUzI1NiJ9","header":{"kid":"e9bc097a-ce51-4036-9562-d2ade882db0d"},"signature":"DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q"}'); + + static::assertEquals('ES256', $loaded->getSignature(0)->getProtectedHeaderParameter('alg')); + static::assertEquals(['iss' => 'joe', 'exp' => 1300819380, 'http://example.com/is_root' => true], json_decode($loaded->getPayload(), true)); + } +} diff --git a/vendor/web-token/jwt-signature/Tests/JWSLoaderTest.php b/vendor/web-token/jwt-signature/Tests/JWSLoaderTest.php new file mode 100644 index 000000000..06130b74a --- /dev/null +++ b/vendor/web-token/jwt-signature/Tests/JWSLoaderTest.php @@ -0,0 +1,120 @@ +expectException(Exception::class); + $this->expectExceptionMessage('Unable to load and verify the token.'); + + $token = '{"payload":"SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4","protected":"eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhbXBsZSJ9","signature":"MRjdkly7_-oTPTS3AXP41iQIGKa80A0ZmTuV5MEaHoxnW2e5CZ5NlKtainoFmKZopdHM1O2U4mwzJdQx996ivp83xuglII7PNDi84wnB-BDkoBwA78185hX-Es4JIwmDLJK3lfWRa-XtL0RnltuYv746iYTh_qHRD68BNt1uSNCrUCTJDt5aAE6x8wW1Kt9eRo4QPocSadnHXFxnt8Is9UzpERV0ePPQdLuW3IS_de3xyIrDaLGdjluPxUAhb6L2aXic1U12podGU0KLUQSE_oI-ZnmKJ3F4uOZDnd6QZWJushZ41Axf_fcIe8u9ipH84ogoree7vjbU5y18kDquDg"}'; + $key = new JWK([ + 'kty' => 'RSA', + 'kid' => 'bilbo.baggins@hobbiton.example', + 'use' => 'sig', + 'n' => 'n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw', + 'e' => 'AQAB', + ]); + + $this->getJWSLoader()->loadAndVerifyWithKey($token, $key, $signature); + } + + /** + * @test + */ + public function theTokenCannotBeVerifiedBecauseOfAnUnsupportedAlgorithm(): void + { + $this->expectException(Exception::class); + $this->expectExceptionMessage('Unable to load and verify the token.'); + + $token = 'eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhbXBsZSJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4.MRjdkly7_-oTPTS3AXP41iQIGKa80A0ZmTuV5MEaHoxnW2e5CZ5NlKtainoFmKZopdHM1O2U4mwzJdQx996ivp83xuglII7PNDi84wnB-BDkoBwA78185hX-Es4JIwmDLJK3lfWRa-XtL0RnltuYv746iYTh_qHRD68BNt1uSNCrUCTJDt5aAE6x8wW1Kt9eRo4QPocSadnHXFxnt8Is9UzpERV0ePPQdLuW3IS_de3xyIrDaLGdjluPxUAhb6L2aXic1U12podGU0KLUQSE_oI-ZnmKJ3F4uOZDnd6QZWJushZ41Axf_fcIe8u9ipH84ogoree7vjbU5y18kDquDg'; + $key = new JWK([ + 'kty' => 'RSA', + 'kid' => 'bilbo.baggins@hobbiton.example', + 'use' => 'sig', + 'n' => 'n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw', + 'e' => 'AQAB', + ]); + + $this->getJWSLoader()->loadAndVerifyWithKey($token, $key, $signature); + } + + /** + * @test + */ + public function theTokenCannotBeVerifiedBecauseOfABadKey(): void + { + $this->expectException(Exception::class); + $this->expectExceptionMessage('Unable to load and verify the token.'); + + $token = 'eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LWVlZjMxNGJjNzAzNyJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4.s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0'; + $key = new JWK([ + 'kty' => 'RSA', + 'kid' => 'bilbo.baggins@hobbiton.example', + 'use' => 'sig', + 'n' => 'n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw', + 'e' => 'AQAB', + ]); + + $this->getJWSLoader()->loadAndVerifyWithKey($token, $key, $signature); + } + + /** + * @test + */ + public function theTokenCanBeVerified(): void + { + $token = 'eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LWVlZjMxNGJjNzAzNyJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4.s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0'; + $key = new JWK([ + 'kty' => 'oct', + 'kid' => '018c0ae5-4d9b-471b-bfd6-eef314bc7037', + 'use' => 'sig', + 'alg' => 'HS256', + 'k' => 'hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg', + ]); + + $jws = $this->getJWSLoader()->loadAndVerifyWithKey($token, $key, $signature); + static::assertEquals("It\xe2\x80\x99s a dangerous business, Frodo, going out your door. You step onto the road, and if you don't keep your feet, there\xe2\x80\x99s no knowing where you might be swept off to.", $jws->getPayload()); + static::assertEquals(0, $signature); + } + + private function getJWSLoader(): JWSLoader + { + if (null === $this->jwsLoader) { + $this->jwsLoader = $this->getJWSLoaderFactory()->create(['jws_compact'], ['HS256']); + } + + return $this->jwsLoader; + } +} diff --git a/vendor/web-token/jwt-signature/Tests/JWSSplitTest.php b/vendor/web-token/jwt-signature/Tests/JWSSplitTest.php new file mode 100644 index 000000000..f4ab6a9c7 --- /dev/null +++ b/vendor/web-token/jwt-signature/Tests/JWSSplitTest.php @@ -0,0 +1,55 @@ +unserialize($input); + $split = $jws->split(); + + static::assertEquals(3, $jws->countSignatures()); + static::assertEquals(3, count($jws->split())); + + for ($i = 0; $i < $jws->countSignatures(); ++$i) { + $signature1 = $jws->getSignature($i); + $tempJws = $split[$i]; + static::assertEquals(1, $tempJws->countSignatures()); + static::assertEquals($jws->isPayloadDetached(), $tempJws->isPayloadDetached()); + static::assertEquals($jws->getEncodedPayload(), $tempJws->getEncodedPayload()); + static::assertEquals($jws->getPayload(), $tempJws->getPayload()); + + $signature2 = $tempJws->getSignature(0); + static::assertEquals($signature1->getSignature(), $signature2->getSignature()); + static::assertEquals($signature1->getHeader(), $signature2->getHeader()); + static::assertEquals($signature1->getEncodedProtectedHeader(), $signature2->getEncodedProtectedHeader()); + static::assertEquals($signature1->getProtectedHeader(), $signature2->getProtectedHeader()); + } + } +} diff --git a/vendor/web-token/jwt-signature/Tests/JWSTest.php b/vendor/web-token/jwt-signature/Tests/JWSTest.php new file mode 100644 index 000000000..3c1ab86bd --- /dev/null +++ b/vendor/web-token/jwt-signature/Tests/JWSTest.php @@ -0,0 +1,180 @@ + time(), + 'iat' => time(), + 'exp' => time() + 3600, + 'iss' => 'Me', + 'aud' => 'You', + 'sub' => 'My friend', + ]; + $header = ['alg' => 'none']; + $jws = new JWS(json_encode($claims), json_encode($claims)); + $jws = $jws->addSignature('', $header, Base64Url::encode(json_encode($header))); + + static::assertEquals(json_encode($claims), $jws->getPayload()); + static::assertEquals(1, $jws->countSignatures()); + static::assertTrue($jws->getSignature(0)->hasProtectedHeaderParameter('alg')); + static::assertEquals($header, $jws->getSignature(0)->getProtectedHeader()); + static::assertEquals('none', $jws->getSignature(0)->getProtectedHeaderParameter('alg')); + static::assertEquals([], $jws->getSignature(0)->getHeader()); + } + + /** + * @test + */ + public function toCompactJSONFailed(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The signature does not exist.'); + + $claims = [ + 'nbf' => time(), + 'iat' => time(), + 'exp' => time() + 3600, + 'iss' => 'Me', + 'aud' => 'You', + 'sub' => 'My friend', + ]; + $jws = new JWS(json_encode($claims), json_encode($claims)); + $this->getJWSSerializerManager()->serialize('jws_compact', $jws, 0); + } + + /** + * @test + */ + public function toFlattenedJSONFailed(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The signature does not exist.'); + + $claims = [ + 'nbf' => time(), + 'iat' => time(), + 'exp' => time() + 3600, + 'iss' => 'Me', + 'aud' => 'You', + 'sub' => 'My friend', + ]; + $jws = new JWS(json_encode($claims), json_encode($claims)); + $this->getJWSSerializerManager()->serialize('jws_json_flattened', $jws, 0); + } + + /** + * @test + */ + public function toJSONFailed(): void + { + $this->expectException(LogicException::class); + $this->expectExceptionMessage('No signature.'); + + $claims = [ + 'nbf' => time(), + 'iat' => time(), + 'exp' => time() + 3600, + 'iss' => 'Me', + 'aud' => 'You', + 'sub' => 'My friend', + ]; + $jws = new JWS(json_encode($claims), json_encode($claims)); + $this->getJWSSerializerManager()->serialize('jws_json_general', $jws, 0); + } + + /** + * @test + */ + public function signatureContainsUnprotectedHeader(): void + { + $this->expectException(LogicException::class); + $this->expectExceptionMessage('The signature contains unprotected header parameters and cannot be converted into compact JSON'); + + $claims = [ + 'nbf' => time(), + 'iat' => time(), + 'exp' => time() + 3600, + 'iss' => 'Me', + 'aud' => 'You', + 'sub' => 'My friend', + ]; + $header = ['alg' => 'none']; + $jws = new JWS(json_encode($claims), json_encode($claims)); + $jws = $jws->addSignature('', $header, Base64Url::encode(json_encode($header)), ['foo' => 'bar']); + + $this->getJWSSerializerManager()->serialize('jws_compact', $jws, 0); + } + + /** + * @test + */ + public function signatureDoesNotContainHeader(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The header "foo" does not exist'); + + $claims = [ + 'nbf' => time(), + 'iat' => time(), + 'exp' => time() + 3600, + 'iss' => 'Me', + 'aud' => 'You', + 'sub' => 'My friend', + ]; + $header = ['alg' => 'none']; + $jws = new JWS(json_encode($claims), json_encode($claims)); + $jws = $jws->addSignature('', $header, Base64Url::encode(json_encode($header))); + $jws->getSignature(0)->getHeaderParameter('foo'); + } + + /** + * @test + */ + public function signatureDoesNotContainProtectedHeader(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The protected header "foo" does not exist'); + + $claims = [ + 'nbf' => time(), + 'iat' => time(), + 'exp' => time() + 3600, + 'iss' => 'Me', + 'aud' => 'You', + 'sub' => 'My friend', + ]; + $header = ['alg' => 'none']; + $jws = new JWS(json_encode($claims), json_encode($claims)); + $jws = $jws->addSignature('', $header, Base64Url::encode(json_encode($header))); + $jws->getSignature(0)->getProtectedHeaderParameter('foo'); + } +} diff --git a/vendor/web-token/jwt-signature/Tests/RFC7520/MultipleSignaturesTest.php b/vendor/web-token/jwt-signature/Tests/RFC7520/MultipleSignaturesTest.php new file mode 100644 index 000000000..281d2aeb4 --- /dev/null +++ b/vendor/web-token/jwt-signature/Tests/RFC7520/MultipleSignaturesTest.php @@ -0,0 +1,97 @@ + 'RSA', + 'kid' => 'bilbo.baggins@hobbiton.example', + 'use' => 'sig', + 'n' => 'n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw', + 'e' => 'AQAB', + 'd' => 'bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78eiZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRldY7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-bMwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDjd18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOcOpBrQzwQ', + 'p' => '3Slxg_DwTXJcb6095RoXygQCAZ5RnAvZlno1yhHtnUex_fp7AZ_9nRaO7HX_-SFfGQeutao2TDjDAWU4Vupk8rw9JR0AzZ0N2fvuIAmr_WCsmGpeNqQnev1T7IyEsnh8UMt-n5CafhkikzhEsrmndH6LxOrvRJlsPp6Zv8bUq0k', + 'q' => 'uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc', + 'dp' => 'B8PVvXkvJrj2L-GYQ7v3y9r6Kw5g9SahXBwsWUzp19TVlgI-YV85q1NIb1rxQtD-IsXXR3-TanevuRPRt5OBOdiMGQp8pbt26gljYfKU_E9xn-RULHz0-ed9E9gXLKD4VGngpz-PfQ_q29pk5xWHoJp009Qf1HvChixRX59ehik', + 'dq' => 'CLDmDGduhylc9o7r84rEUVn7pzQ6PF83Y-iBZx5NT-TpnOZKF1pErAMVeKzFEl41DlHHqqBLSM0W1sOFbwTxYWZDm6sI6og5iTbwQGIC3gnJKbi_7k_vJgGHwHxgPaX2PnvP-zyEkDERuf-ry4c_Z11Cq9AqC2yeL6kdKT1cYF8', + 'qi' => '3PiqvXQN0zwMeE-sBvZgi289XP9XCQF3VWqPzMKnIgQp7_Tugo6-NZBKCQsMf3HaEGBjTVJs_jcK8-TRXvaKe-7ZMaQj8VfBdYkssbu0NKDDhjJ-GtiseaDVWt7dcH0cfwxgFUHpQh7FoCrjFJ6h6ZEpMF6xmujs4qMpPz8aaI4', + ]); + + $ecdsa_private_key = new JWK([ + 'kty' => 'EC', + 'kid' => 'bilbo.baggins@hobbiton.example', + 'use' => 'sig', + 'crv' => 'P-521', + 'x' => 'AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt', + 'y' => 'AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVySsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1', + 'd' => 'AAhRON2r9cqXX1hg-RoI6R1tX5p2rUAYdmpHZoC1XNM56KtscrX6zbKipQrCW9CGZH3T4ubpnoTKLDYJ_fF3_rJt', + ]); + + $symmetric_key = new JWK([ + 'kty' => 'oct', + 'kid' => '018c0ae5-4d9b-471b-bfd6-eef314bc7037', + 'use' => 'sig', + 'alg' => 'HS256', + 'k' => 'hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg', + ]); + + $jwsBuilder = $this->getJWSBuilderFactory()->create(['RS256', 'ES512', 'HS256']); + $jwsVerifier = $this->getJWSVerifierFactory()->create(['RS256', 'ES512', 'HS256']); + $jws = $jwsBuilder + ->create()->withPayload($payload) + ->addSignature($ecdsa_private_key, [], ['alg' => 'ES512', 'kid' => 'bilbo.baggins@hobbiton.example']) //@see https://tools.ietf.org/html/rfc7520#section-4.8.2 + ->addSignature($rsa_private_key, ['alg' => 'RS256'], ['kid' => 'bilbo.baggins@hobbiton.example']) //@see https://tools.ietf.org/html/rfc7520#section-4.8.3 + ->addSignature($symmetric_key, ['alg' => 'HS256', 'kid' => '018c0ae5-4d9b-471b-bfd6-eef314bc7037']) //@see https://tools.ietf.org/html/rfc7520#section-4.8.4 + ->build() + ; + + static::assertEquals(3, $jws->countSignatures()); + + static::assertTrue($jwsVerifier->verifyWithKey($jws, $ecdsa_private_key, 0)); + static::assertTrue($jwsVerifier->verifyWithKey($jws, $rsa_private_key, 1)); + static::assertTrue($jwsVerifier->verifyWithKey($jws, $symmetric_key, 2)); + + // @see https://tools.ietf.org/html/rfc7520#section-4.8.5 + $expected_json = '{"payload":"SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4","signatures":[{"protected":"eyJhbGciOiJSUzI1NiJ9","header":{"kid":"bilbo.baggins@hobbiton.example"},"signature":"MIsjqtVlOpa71KE-Mss8_Nq2YH4FGhiocsqrgi5NvyG53uoimic1tcMdSg-qptrzZc7CG6Svw2Y13TDIqHzTUrL_lR2ZFcryNFiHkSw129EghGpwkpxaTn_THJTCglNbADko1MZBCdwzJxwqZc-1RlpO2HibUYyXSwO97BSe0_evZKdjvvKSgsIqjytKSeAMbhMBdMma622_BG5t4sdbuCHtFjp9iJmkio47AIwqkZV1aIZsv33uPUqBBCXbYoQJwt7mxPftHmNlGoOSMxR_3thmXTCm4US-xiNOyhbm8afKK64jU6_TPtQHiJeQJxz9G3Tx-083B745_AfYOnlC9w"},{"header":{"alg":"ES512","kid":"bilbo.baggins@hobbiton.example"},"signature":"ARcVLnaJJaUWG8fG-8t5BREVAuTY8n8YHjwDO1muhcdCoFZFFjfISu0Cdkn9Ybdlmi54ho0x924DUz8sK7ZXkhc7AFM8ObLfTvNCrqcI3Jkl2U5IX3utNhODH6v7xgy1Qahsn0fyb4zSAkje8bAWz4vIfj5pCMYxxm4fgV3q7ZYhm5eD"},{"protected":"eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LWVlZjMxNGJjNzAzNyJ9","signature":"s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0"}]}'; + + $loaded_json = $this->getJWSSerializerManager()->unserialize($expected_json); + static::assertEquals(3, $loaded_json->countSignatures()); + + static::assertTrue($jwsVerifier->verifyWithKey($loaded_json, $rsa_private_key, 0)); + static::assertTrue($jwsVerifier->verifyWithKey($loaded_json, $ecdsa_private_key, 1)); + static::assertTrue($jwsVerifier->verifyWithKey($loaded_json, $symmetric_key, 2)); + } +} diff --git a/vendor/web-token/jwt-signature/Tests/RFC7520/NestingTest.php b/vendor/web-token/jwt-signature/Tests/RFC7520/NestingTest.php new file mode 100644 index 000000000..5178425f0 --- /dev/null +++ b/vendor/web-token/jwt-signature/Tests/RFC7520/NestingTest.php @@ -0,0 +1,67 @@ + 'hobbiton.example', + 'exp' => 1300819380, + 'http://example.com/is_root' => true, + ]; + + $signature_key = new JWK([ + 'kty' => 'RSA', + 'kid' => 'hobbiton.example', + 'use' => 'sig', + 'n' => 'kNrPIBDXMU6fcyv5i-QHQAQ-K8gsC3HJb7FYhYaw8hXbNJa-t8q0lDKwLZgQXYV-ffWxXJv5GGrlZE4GU52lfMEegTDzYTrRQ3tepgKFjMGg6Iy6fkl1ZNsx2gEonsnlShfzA9GJwRTmtKPbk1s-hwx1IU5AT-AIelNqBgcF2vE5W25_SGGBoaROVdUYxqETDggM1z5cKV4ZjDZ8-lh4oVB07bkac6LQdHpJUUySH_Er20DXx30Kyi97PciXKTS-QKXnmm8ivyRCmux22ZoPUind2BKC5OiG4MwALhaL2Z2k8CsRdfy-7dg7z41Rp6D0ZeEvtaUp4bX4aKraL4rTfw', + 'e' => 'AQAB', + 'd' => 'ZLe_TIxpE9-W_n2VBa-HWvuYPtjvxwVXClJFOpJsdea8g9RMx34qEOEtnoYc2un3CZ3LtJi-mju5RAT8YSc76YJds3ZVw0UiO8mMBeG6-iOnvgobobNx7K57-xjTJZU72EjOr9kB7z6ZKwDDq7HFyCDhUEcYcHFVc7iL_6TibVhAhOFONWlqlJgEgwVYd0rybNGKifdnpEbwyHoMwY6HM1qvnEFgP7iZ0YzHUT535x6jj4VKcdA7ZduFkhUauysySEW7mxZM6fj1vdjJIy9LD1fIz30Xv4ckoqhKF5GONU6tNmMmNgAD6gIViyEle1PrIxl1tBhCI14bRW-zrpHgAQ', + 'p' => 'yKWYoNIAqwMRQlgIBOdT1NIcbDNUUs2Rh-pBaxD_mIkweMt4Mg-0-B2iSYvMrs8horhonV7vxCQagcBAATGW-hAafUehWjxWSH-3KccRM8toL4e0q7M-idRDOBXSoe7Z2-CV2x_ZCY3RP8qp642R13WgXqGDIM4MbUkZSjcY9-c', + 'q' => 'uND4o15V30KDzf8vFJw589p1vlQVQ3NEilrinRUPHkkxaAzDzccGgrWMWpGxGFFnNL3w5CqPLeU76-5IVYQq0HwYVl0hVXQHr7sgaGu-483Ad3ENcL23FrOnF45m7_2ooAstJDe49MeLTTQKrSIBl_SKvqpYvfSPTczPcZkh9Kk', + 'dp' => 'jmTnEoq2qqa8ouaymjhJSCnsveUXnMQC2gAneQJRQkFqQu-zV2PKPKNbPvKVyiF5b2-L3tM3OW2d2iNDyRUWXlT7V5l0KwPTABSTOnTqAmYChGi8kXXdlhcrtSvXldBakC6saxwI_TzGGY2MVXzc2ZnCvCXHV4qjSxOrfP3pHFU', + 'dq' => 'R9FUvU88OVzEkTkXl3-5-WusE4DjHmndeZIlu3rifBdfLpq_P-iWPBbGaq9wzQ1c-J7SzCdJqkEJDv5yd2C7rnZ6kpzwBh_nmL8zscAk1qsunnt9CJGAYz7-sGWy1JGShFazfP52ThB4rlCJ0YuEaQMrIzpY77_oLAhpmDA0hLk', + 'qi' => 'S8tC7ZknW6hPITkjcwttQOPLVmRfwirRlFAViuDb8NW9CrV_7F2OqUZCqmzHTYAumwGFHI1WVRep7anleWaJjxC_1b3fq_al4qH3Pe-EKiHg6IMazuRtZLUROcThrExDbF5dYbsciDnfRUWLErZ4N1Be0bnxYuPqxwKd9QZwMo0', + ]); + + $signature_header = [ + 'alg' => 'PS256', + 'typ' => 'JWT', + ]; + + $json_compact = 'eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJob2JiaXRvbi5leGFtcGxlIiwiZXhwIjoxMzAwODE5MzgwLCJodHRwOi8vZXhhbXBsZS5jb20vaXNfcm9vdCI6dHJ1ZX0.dPpMqwRZxFYi1UfcDAaf8M99o7kwUWtiXZ-ByvVuJih4MhJ_aZqciprz0OWaIAkIvn1qskChirjKvY9ESZNUCP4JjvfyPS-nqjJxYoA5ztWOyFk2cZNIPXjcJXSQwXPO9tEe-v4VSqgD0aKHqPxYog4N6Cz1lKph1U1sYDSI67_bLL7elg_vkjfMp5_W5l5LuUYGMeh6hxQIaIUXf9EwV2JmvTMuZ-vBOWy0Sniy1EFo72CRTvmtrIf5AROo5MNliY3KtUxeP-SOmD-LEYwW9SlkohYzMVAZDDOrVbv7KVRHpeYNaK75KEQqdCEEkS_rskZS-Qtt_nlegTWh1mEYaA'; + + $jwsVerifier = $this->getJWSVerifierFactory()->create(['PS256']); + $loaded_compact_json = $this->getJWSSerializerManager()->unserialize($json_compact); + + static::assertTrue($jwsVerifier->verifyWithKey($loaded_compact_json, $signature_key, 0)); + static::assertEquals($signature_header, $loaded_compact_json->getSignature(0)->getProtectedHeader()); + static::assertEquals($payload, json_decode($loaded_compact_json->getPayload(), true)); + } +} diff --git a/vendor/web-token/jwt-signature/Tests/SignatureTest.php b/vendor/web-token/jwt-signature/Tests/SignatureTest.php new file mode 100644 index 000000000..ae9ea879a --- /dev/null +++ b/vendor/web-token/jwt-signature/Tests/SignatureTest.php @@ -0,0 +1,138 @@ +algorithmManagerFactory) { + $this->algorithmManagerFactory = new AlgorithmManagerFactory(); + $this->algorithmManagerFactory->add('HS256', new Algorithm\HS256()); + $this->algorithmManagerFactory->add('HS384', new Algorithm\HS384()); + $this->algorithmManagerFactory->add('HS512', new Algorithm\HS512()); + $this->algorithmManagerFactory->add('ES256', new Algorithm\ES256()); + $this->algorithmManagerFactory->add('ES384', new Algorithm\ES384()); + $this->algorithmManagerFactory->add('ES512', new Algorithm\ES512()); + $this->algorithmManagerFactory->add('RS256', new Algorithm\RS256()); + $this->algorithmManagerFactory->add('RS384', new Algorithm\RS384()); + $this->algorithmManagerFactory->add('RS512', new Algorithm\RS512()); + $this->algorithmManagerFactory->add('PS256', new Algorithm\PS256()); + $this->algorithmManagerFactory->add('PS384', new Algorithm\PS384()); + $this->algorithmManagerFactory->add('PS512', new Algorithm\PS512()); + $this->algorithmManagerFactory->add('none', new Algorithm\None()); + $this->algorithmManagerFactory->add('EdDSA', new Algorithm\EdDSA()); + } + + return $this->algorithmManagerFactory; + } + + protected function getJWSBuilderFactory(): JWSBuilderFactory + { + if (null === $this->jwsBuilderFactory) { + $this->jwsBuilderFactory = new JWSBuilderFactory( + $this->getAlgorithmManagerFactory() + ); + } + + return $this->jwsBuilderFactory; + } + + protected function getJWSVerifierFactory(): JWSVerifierFactory + { + if (null === $this->jwsVerifierFactory) { + $this->jwsVerifierFactory = new JWSVerifierFactory( + $this->getAlgorithmManagerFactory() + ); + } + + return $this->jwsVerifierFactory; + } + + protected function getJWSSerializerManagerFactory(): Serializer\JWSSerializerManagerFactory + { + if (null === $this->jwsSerializerManagerFactory) { + $this->jwsSerializerManagerFactory = new Serializer\JWSSerializerManagerFactory(); + $this->jwsSerializerManagerFactory->add(new Serializer\CompactSerializer()); + $this->jwsSerializerManagerFactory->add(new Serializer\JSONFlattenedSerializer()); + $this->jwsSerializerManagerFactory->add(new Serializer\JSONGeneralSerializer()); + } + + return $this->jwsSerializerManagerFactory; + } + + protected function getJWSSerializerManager(): Serializer\JWSSerializerManager + { + if (null === $this->jwsSerializerManager) { + $this->jwsSerializerManager = new Serializer\JWSSerializerManager([ + new Serializer\CompactSerializer(), + new Serializer\JSONFlattenedSerializer(), + new Serializer\JSONGeneralSerializer(), + ]); + } + + return $this->jwsSerializerManager; + } + + protected function getJWSLoaderFactory(): JWSLoaderFactory + { + if (null === $this->jwsLoaderFactory) { + $this->jwsLoaderFactory = new JWSLoaderFactory( + $this->getJWSSerializerManagerFactory(), + $this->getJWSVerifierFactory(), + null + ); + } + + return $this->jwsLoaderFactory; + } +} diff --git a/vendor/web-token/jwt-signature/Tests/SignerTest.php b/vendor/web-token/jwt-signature/Tests/SignerTest.php new file mode 100644 index 000000000..9744ea4e3 --- /dev/null +++ b/vendor/web-token/jwt-signature/Tests/SignerTest.php @@ -0,0 +1,967 @@ +expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('No "alg" parameter set in the header.'); + + $jwsBuilder = $this->getJWSBuilderFactory()->create([]); + $jwsBuilder + ->create()->withPayload(json_encode($this->getKey3())) + ->addSignature($this->getKey1(), []) + ->build() + ; + } + + /** + * @test + */ + public function algParameterIsNotSupported(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The algorithm "foo" is not supported.'); + + $jwsBuilder = $this->getJWSBuilderFactory()->create([]); + $jwsBuilder + ->create()->withPayload(json_encode($this->getKey3())) + ->addSignature($this->getKey1(), ['alg' => 'foo']) + ->build() + ; + } + + /** + * @test + */ + public function duplicatedHeader(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The header contains duplicated entries: foo.'); + + $jwsBuilder = $this->getJWSBuilderFactory()->create([]); + $jwsBuilder + ->create()->withPayload(json_encode($this->getKey3())) + ->addSignature($this->getKey1(), ['alg' => 'ES256', 'foo' => 'bar'], ['foo' => 'bar']) + ; + } + + /** + * @test + */ + public function signAndLoadCompact(): void + { + $jwsBuilder = $this->getJWSBuilderFactory()->create(['HS512', 'RS512']); + $jws = $jwsBuilder + ->create()->withPayload(json_encode($this->getKey3())) + ->addSignature($this->getKey1(), ['alg' => 'HS512']) + ->addSignature($this->getKey2(), ['alg' => 'RS512']) + ->build() + ; + + static::assertEquals(2, $jws->countSignatures()); + + $loaded = $this->getJWSSerializerManager()->unserialize($this->getJWSSerializerManager()->serialize('jws_json_general', $jws, 0)); + + static::assertEquals('HS512', $loaded->getSignature(0)->getProtectedHeaderParameter('alg')); + static::assertEquals('RS512', $loaded->getSignature(1)->getProtectedHeaderParameter('alg')); + } + + /** + * @test + */ + public function signMultipleInstructionWithCompactRepresentation(): void + { + $jwsBuilder = $this->getJWSBuilderFactory()->create(['HS512', 'RS512']); + $jws = $jwsBuilder + ->create()->withPayload('Live long and Prosper.') + ->addSignature($this->getKey1(), ['alg' => 'HS512']) + ->addSignature($this->getKey2(), ['alg' => 'RS512']) + ->build() + ; + + static::assertEquals(2, $jws->countSignatures()); + static::assertEquals('eyJhbGciOiJIUzUxMiJ9.TGl2ZSBsb25nIGFuZCBQcm9zcGVyLg.TjxvVLKLc1kU5XW1NjZlI6_kQHjeU2orTWBZ7p0KuRzq_9lyPWR04PAUpbYkaLJLsmIJ8Fxi8Gsrc0khPtFxfQ', $this->getJWSSerializerManager()->serialize('jws_compact', $jws, 0)); + static::assertEquals('eyJhbGciOiJSUzUxMiJ9.TGl2ZSBsb25nIGFuZCBQcm9zcGVyLg.cR-npy2oEi275rpeTAKooLRzOhIOFMewpzE38CLx4_CtdkN4Y7EUlca9ryV6yGMH8SswUqosMnmUU8XYg7xkuNAc6mCODJVF2exfb_Mulmr9YolQrLFrFRsMk1rztXMinCMQeCe5ue3Ck4E4aJlIkjf-d0DJktoIhH6d2gZ-iJeLQ32wcBhPcEbj2gr7K_wYKlEXhKFwG59OE-hIi9IHXEKvK-2V5vzZLVC80G4aWYd3D-2eX3LF1K69NP04jGcu1D4l9UV8zTz1gOWe697iZG0JyKhSccUaHZ0TfEa8cT0tm6xTz6tpUGSDdvPQU8JCU8GTOsi9ifxTsI-GlWE3YA', $this->getJWSSerializerManager()->serialize('jws_compact', $jws, 1)); + } + + /** + * @group JWSBuilder + * + * @test + */ + public function signMultipleInstructionWithCompactRepresentationUsingBuilder(): void + { + $jwsBuilder = $this->getJWSBuilderFactory()->create(['HS512', 'RS512']); + $jws = $jwsBuilder + ->create()->withPayload('Live long and Prosper.') + ->addSignature($this->getKey1(), ['alg' => 'HS512']) + ->addSignature($this->getKey2(), ['alg' => 'RS512']) + ->build() + ; + + static::assertEquals(2, $jws->countSignatures()); + static::assertEquals('eyJhbGciOiJIUzUxMiJ9.TGl2ZSBsb25nIGFuZCBQcm9zcGVyLg.TjxvVLKLc1kU5XW1NjZlI6_kQHjeU2orTWBZ7p0KuRzq_9lyPWR04PAUpbYkaLJLsmIJ8Fxi8Gsrc0khPtFxfQ', $this->getJWSSerializerManager()->serialize('jws_compact', $jws, 0)); + static::assertEquals('eyJhbGciOiJSUzUxMiJ9.TGl2ZSBsb25nIGFuZCBQcm9zcGVyLg.cR-npy2oEi275rpeTAKooLRzOhIOFMewpzE38CLx4_CtdkN4Y7EUlca9ryV6yGMH8SswUqosMnmUU8XYg7xkuNAc6mCODJVF2exfb_Mulmr9YolQrLFrFRsMk1rztXMinCMQeCe5ue3Ck4E4aJlIkjf-d0DJktoIhH6d2gZ-iJeLQ32wcBhPcEbj2gr7K_wYKlEXhKFwG59OE-hIi9IHXEKvK-2V5vzZLVC80G4aWYd3D-2eX3LF1K69NP04jGcu1D4l9UV8zTz1gOWe697iZG0JyKhSccUaHZ0TfEa8cT0tm6xTz6tpUGSDdvPQU8JCU8GTOsi9ifxTsI-GlWE3YA', $this->getJWSSerializerManager()->serialize('jws_compact', $jws, 1)); + } + + /** + * @group JWSBuilder + * + * @test + */ + public function signMultipleInstructionWithCompactRepresentationUsingBuilderAndDetachedPayload(): void + { + $jwsBuilder = $this->getJWSBuilderFactory()->create(['HS512', 'RS512']); + $jws = $jwsBuilder + ->create()->withPayload('Live long and Prosper.', true) + ->addSignature($this->getKey1(), ['alg' => 'HS512']) + ->addSignature($this->getKey2(), ['alg' => 'RS512']) + ->build() + ; + + static::assertEquals(2, $jws->countSignatures()); + static::assertEquals('eyJhbGciOiJIUzUxMiJ9..TjxvVLKLc1kU5XW1NjZlI6_kQHjeU2orTWBZ7p0KuRzq_9lyPWR04PAUpbYkaLJLsmIJ8Fxi8Gsrc0khPtFxfQ', $this->getJWSSerializerManager()->serialize('jws_compact', $jws, 0)); + static::assertEquals('eyJhbGciOiJSUzUxMiJ9..cR-npy2oEi275rpeTAKooLRzOhIOFMewpzE38CLx4_CtdkN4Y7EUlca9ryV6yGMH8SswUqosMnmUU8XYg7xkuNAc6mCODJVF2exfb_Mulmr9YolQrLFrFRsMk1rztXMinCMQeCe5ue3Ck4E4aJlIkjf-d0DJktoIhH6d2gZ-iJeLQ32wcBhPcEbj2gr7K_wYKlEXhKFwG59OE-hIi9IHXEKvK-2V5vzZLVC80G4aWYd3D-2eX3LF1K69NP04jGcu1D4l9UV8zTz1gOWe697iZG0JyKhSccUaHZ0TfEa8cT0tm6xTz6tpUGSDdvPQU8JCU8GTOsi9ifxTsI-GlWE3YA', $this->getJWSSerializerManager()->serialize('jws_compact', $jws, 1)); + } + + /** + * @test + */ + public function createCompactJWSUsingFactory(): void + { + $jwsBuilder = $this->getJWSBuilderFactory()->create(['HS512', 'RS512']); + $jwsVerifier = $this->getJWSVerifierFactory()->create(['HS512', 'RS512']); + + $jws = $jwsBuilder + ->create()->withPayload('Live long and Prosper.') + ->addSignature($this->getKey1(), ['alg' => 'HS512']) + ->addSignature($this->getKey2(), ['alg' => 'RS512']) + ->build() + ; + $jws0 = $this->getJWSSerializerManager()->serialize('jws_compact', $jws, 0); + $jws1 = $this->getJWSSerializerManager()->serialize('jws_compact', $jws, 1); + + $jws = $jwsBuilder + ->create()->withPayload('Live long and Prosper.', true) + ->addSignature($this->getKey1(), ['alg' => 'HS512']) + ->addSignature($this->getKey2(), ['alg' => 'RS512']) + ->build() + ; + $jws2 = $this->getJWSSerializerManager()->serialize('jws_compact', $jws, 0); + $jws3 = $this->getJWSSerializerManager()->serialize('jws_compact', $jws, 1); + + static::assertEquals('eyJhbGciOiJIUzUxMiJ9.TGl2ZSBsb25nIGFuZCBQcm9zcGVyLg.TjxvVLKLc1kU5XW1NjZlI6_kQHjeU2orTWBZ7p0KuRzq_9lyPWR04PAUpbYkaLJLsmIJ8Fxi8Gsrc0khPtFxfQ', $jws0); + static::assertEquals('eyJhbGciOiJSUzUxMiJ9.TGl2ZSBsb25nIGFuZCBQcm9zcGVyLg.cR-npy2oEi275rpeTAKooLRzOhIOFMewpzE38CLx4_CtdkN4Y7EUlca9ryV6yGMH8SswUqosMnmUU8XYg7xkuNAc6mCODJVF2exfb_Mulmr9YolQrLFrFRsMk1rztXMinCMQeCe5ue3Ck4E4aJlIkjf-d0DJktoIhH6d2gZ-iJeLQ32wcBhPcEbj2gr7K_wYKlEXhKFwG59OE-hIi9IHXEKvK-2V5vzZLVC80G4aWYd3D-2eX3LF1K69NP04jGcu1D4l9UV8zTz1gOWe697iZG0JyKhSccUaHZ0TfEa8cT0tm6xTz6tpUGSDdvPQU8JCU8GTOsi9ifxTsI-GlWE3YA', $jws1); + static::assertEquals('eyJhbGciOiJIUzUxMiJ9..TjxvVLKLc1kU5XW1NjZlI6_kQHjeU2orTWBZ7p0KuRzq_9lyPWR04PAUpbYkaLJLsmIJ8Fxi8Gsrc0khPtFxfQ', $jws2); + static::assertEquals('eyJhbGciOiJSUzUxMiJ9..cR-npy2oEi275rpeTAKooLRzOhIOFMewpzE38CLx4_CtdkN4Y7EUlca9ryV6yGMH8SswUqosMnmUU8XYg7xkuNAc6mCODJVF2exfb_Mulmr9YolQrLFrFRsMk1rztXMinCMQeCe5ue3Ck4E4aJlIkjf-d0DJktoIhH6d2gZ-iJeLQ32wcBhPcEbj2gr7K_wYKlEXhKFwG59OE-hIi9IHXEKvK-2V5vzZLVC80G4aWYd3D-2eX3LF1K69NP04jGcu1D4l9UV8zTz1gOWe697iZG0JyKhSccUaHZ0TfEa8cT0tm6xTz6tpUGSDdvPQU8JCU8GTOsi9ifxTsI-GlWE3YA', $jws3); + + $loaded_0 = $this->getJWSSerializerManager()->unserialize($jws0); + static::assertTrue($jwsVerifier->verifyWithKey($loaded_0, $this->getKey1(), 0)); + + $loaded_1 = $this->getJWSSerializerManager()->unserialize($jws1); + static::assertTrue($jwsVerifier->verifyWithKey($loaded_1, $this->getKey2(), 0)); + + $loaded_2 = $this->getJWSSerializerManager()->unserialize($jws2); + static::assertTrue($jwsVerifier->verifyWithKey($loaded_2, $this->getKey1(), 0, 'Live long and Prosper.')); + + $loaded_3 = $this->getJWSSerializerManager()->unserialize($jws3); + static::assertTrue($jwsVerifier->verifyWithKey($loaded_3, $this->getKey2(), 0, 'Live long and Prosper.')); + } + + /** + * @test + */ + public function signMultipleInstructionWithFlattenedRepresentation(): void + { + $jwsBuilder = $this->getJWSBuilderFactory()->create(['HS512', 'RS512']); + $jws = $jwsBuilder + ->create()->withPayload('Live long and Prosper.') + ->addSignature($this->getKey1(), ['alg' => 'HS512']) + ->addSignature($this->getKey2(), ['alg' => 'RS512']) + ->build() + ; + + static::assertEquals(2, $jws->countSignatures()); + static::assertEquals('{"payload":"TGl2ZSBsb25nIGFuZCBQcm9zcGVyLg","protected":"eyJhbGciOiJIUzUxMiJ9","signature":"TjxvVLKLc1kU5XW1NjZlI6_kQHjeU2orTWBZ7p0KuRzq_9lyPWR04PAUpbYkaLJLsmIJ8Fxi8Gsrc0khPtFxfQ"}', $this->getJWSSerializerManager()->serialize('jws_json_flattened', $jws, 0)); + static::assertEquals('{"payload":"TGl2ZSBsb25nIGFuZCBQcm9zcGVyLg","protected":"eyJhbGciOiJSUzUxMiJ9","signature":"cR-npy2oEi275rpeTAKooLRzOhIOFMewpzE38CLx4_CtdkN4Y7EUlca9ryV6yGMH8SswUqosMnmUU8XYg7xkuNAc6mCODJVF2exfb_Mulmr9YolQrLFrFRsMk1rztXMinCMQeCe5ue3Ck4E4aJlIkjf-d0DJktoIhH6d2gZ-iJeLQ32wcBhPcEbj2gr7K_wYKlEXhKFwG59OE-hIi9IHXEKvK-2V5vzZLVC80G4aWYd3D-2eX3LF1K69NP04jGcu1D4l9UV8zTz1gOWe697iZG0JyKhSccUaHZ0TfEa8cT0tm6xTz6tpUGSDdvPQU8JCU8GTOsi9ifxTsI-GlWE3YA"}', $this->getJWSSerializerManager()->serialize('jws_json_flattened', $jws, 1)); + } + + /** + * @test + */ + public function createFlattenedJWSUsingFactory(): void + { + $jwsBuilder = $this->getJWSBuilderFactory()->create(['HS512', 'RS512']); + $jwsVerifier = $this->getJWSVerifierFactory()->create(['HS512', 'RS512']); + $jws = $jwsBuilder + ->create()->withPayload('Live long and Prosper.') + ->addSignature($this->getKey1(), ['alg' => 'HS512'], ['foo' => 'bar']) + ->addSignature($this->getKey2(), ['alg' => 'RS512'], ['plic' => 'ploc']) + ->build() + ; + $jws0 = $this->getJWSSerializerManager()->serialize('jws_json_flattened', $jws, 0); + $jws1 = $this->getJWSSerializerManager()->serialize('jws_json_flattened', $jws, 1); + + $jws = $jwsBuilder + ->create()->withPayload('Live long and Prosper.', true) + ->addSignature($this->getKey1(), ['alg' => 'HS512'], ['foo' => 'bar']) + ->addSignature($this->getKey2(), ['alg' => 'RS512'], ['plic' => 'ploc']) + ->build() + ; + $jws2 = $this->getJWSSerializerManager()->serialize('jws_json_flattened', $jws, 0); + $jws3 = $this->getJWSSerializerManager()->serialize('jws_json_flattened', $jws, 1); + + static::assertEquals('{"payload":"TGl2ZSBsb25nIGFuZCBQcm9zcGVyLg","protected":"eyJhbGciOiJIUzUxMiJ9","header":{"foo":"bar"},"signature":"TjxvVLKLc1kU5XW1NjZlI6_kQHjeU2orTWBZ7p0KuRzq_9lyPWR04PAUpbYkaLJLsmIJ8Fxi8Gsrc0khPtFxfQ"}', $jws0); + static::assertEquals('{"payload":"TGl2ZSBsb25nIGFuZCBQcm9zcGVyLg","protected":"eyJhbGciOiJSUzUxMiJ9","header":{"plic":"ploc"},"signature":"cR-npy2oEi275rpeTAKooLRzOhIOFMewpzE38CLx4_CtdkN4Y7EUlca9ryV6yGMH8SswUqosMnmUU8XYg7xkuNAc6mCODJVF2exfb_Mulmr9YolQrLFrFRsMk1rztXMinCMQeCe5ue3Ck4E4aJlIkjf-d0DJktoIhH6d2gZ-iJeLQ32wcBhPcEbj2gr7K_wYKlEXhKFwG59OE-hIi9IHXEKvK-2V5vzZLVC80G4aWYd3D-2eX3LF1K69NP04jGcu1D4l9UV8zTz1gOWe697iZG0JyKhSccUaHZ0TfEa8cT0tm6xTz6tpUGSDdvPQU8JCU8GTOsi9ifxTsI-GlWE3YA"}', $jws1); + static::assertEquals('{"protected":"eyJhbGciOiJIUzUxMiJ9","header":{"foo":"bar"},"signature":"TjxvVLKLc1kU5XW1NjZlI6_kQHjeU2orTWBZ7p0KuRzq_9lyPWR04PAUpbYkaLJLsmIJ8Fxi8Gsrc0khPtFxfQ"}', $jws2); + static::assertEquals('{"protected":"eyJhbGciOiJSUzUxMiJ9","header":{"plic":"ploc"},"signature":"cR-npy2oEi275rpeTAKooLRzOhIOFMewpzE38CLx4_CtdkN4Y7EUlca9ryV6yGMH8SswUqosMnmUU8XYg7xkuNAc6mCODJVF2exfb_Mulmr9YolQrLFrFRsMk1rztXMinCMQeCe5ue3Ck4E4aJlIkjf-d0DJktoIhH6d2gZ-iJeLQ32wcBhPcEbj2gr7K_wYKlEXhKFwG59OE-hIi9IHXEKvK-2V5vzZLVC80G4aWYd3D-2eX3LF1K69NP04jGcu1D4l9UV8zTz1gOWe697iZG0JyKhSccUaHZ0TfEa8cT0tm6xTz6tpUGSDdvPQU8JCU8GTOsi9ifxTsI-GlWE3YA"}', $jws3); + + $loaded_0 = $this->getJWSSerializerManager()->unserialize($jws0); + static::assertTrue($jwsVerifier->verifyWithKey($loaded_0, $this->getKey1(), 0)); + + $loaded_1 = $this->getJWSSerializerManager()->unserialize($jws1); + static::assertTrue($jwsVerifier->verifyWithKey($loaded_1, $this->getKey2(), 0)); + + $loaded_2 = $this->getJWSSerializerManager()->unserialize($jws2); + static::assertTrue($jwsVerifier->verifyWithKey($loaded_2, $this->getKey1(), 0, 'Live long and Prosper.')); + + $loaded_3 = $this->getJWSSerializerManager()->unserialize($jws3); + static::assertTrue($jwsVerifier->verifyWithKey($loaded_3, $this->getKey2(), 0, 'Live long and Prosper.')); + } + + /** + * @test + */ + public function algorithmNotAllowedForTheKey(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The algorithm "RS512" is not allowed with this key.'); + + $jwsBuilder = $this->getJWSBuilderFactory()->create([]); + $jwsBuilder + ->create()->withPayload('Live long and Prosper.') + ->addSignature($this->getKey5(), ['alg' => 'RS512']) + ->build() + ; + } + + /** + * @test + */ + public function operationNotAllowedForTheKey(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Key cannot be used to sign'); + + $jwsBuilder = $this->getJWSBuilderFactory()->create(['PS512']); + $jwsBuilder + ->create()->withPayload('Live long and Prosper.') + ->addSignature($this->getKey4(), ['alg' => 'PS512']) + ->build() + ; + } + + /** + * @test + */ + public function signAndLoadFlattened(): void + { + $jwsBuilder = $this->getJWSBuilderFactory()->create(['HS512']); + $jws = $jwsBuilder + ->create()->withPayload(json_encode(['baz', 'ban'])) + ->addSignature($this->getKey1(), ['alg' => 'HS512'], ['foo' => 'bar']) + ->build() + ; + + $loaded = $this->getJWSSerializerManager()->unserialize($this->getJWSSerializerManager()->serialize('jws_json_flattened', $jws, 0)); + + static::assertEquals(1, $loaded->countSignatures()); + static::assertEquals('HS512', $loaded->getSignature(0)->getProtectedHeaderParameter('alg')); + } + + /** + * @test + */ + public function signAndLoad(): void + { + $jwsBuilder = $this->getJWSBuilderFactory()->create(['HS512', 'RS512']); + $jwsVerifier = $this->getJWSVerifierFactory()->create(['HS512', 'RS512']); + $jws = $jwsBuilder + ->create()->withPayload('Live long and Prosper.') + ->addSignature($this->getKey1(), ['alg' => 'HS512'], ['foo' => 'bar']) + ->addSignature($this->getKey2(), ['alg' => 'RS512']) + ->build() + ; + + $loaded = $this->getJWSSerializerManager()->unserialize($this->getJWSSerializerManager()->serialize('jws_json_general', $jws, 0)); + + static::assertEquals(2, $loaded->countSignatures()); + static::assertEquals('Live long and Prosper.', $loaded->getPayload()); + static::assertTrue($jwsVerifier->verifyWithKeySet($loaded, $this->getSymmetricKeySet(), 0)); + static::assertTrue($jwsVerifier->verifyWithKeySet($loaded, $this->getPublicKeySet(), 1)); + + static::assertEquals('HS512', $loaded->getSignature(0)->getProtectedHeaderParameter('alg')); + static::assertEquals('RS512', $loaded->getSignature(1)->getProtectedHeaderParameter('alg')); + } + + /** + * @test + */ + public function signAndLoadWithWrongKeys(): void + { + $jwsBuilder = $this->getJWSBuilderFactory()->create(['RS512']); + $jwsVerifier = $this->getJWSVerifierFactory()->create(['RS512']); + $jws = $jwsBuilder + ->create()->withPayload('Live long and Prosper.') + ->addSignature($this->getKey2(), ['alg' => 'RS512']) + ->build() + ; + + $loaded = $this->getJWSSerializerManager()->unserialize($this->getJWSSerializerManager()->serialize('jws_json_general', $jws, 0)); + + static::assertEquals(1, $loaded->countSignatures()); + static::assertEquals('Live long and Prosper.', $loaded->getPayload()); + + static::assertFalse($jwsVerifier->verifyWithKeySet($loaded, $this->getSymmetricKeySet(), 0)); + } + + /** + * @test + */ + public function signAndLoadWithUnsupportedAlgorithm(): void + { + $jwsBuilder = $this->getJWSBuilderFactory()->create(['RS512']); + $jwsVerifier = $this->getJWSVerifierFactory()->create(['RS512']); + $jws = $jwsBuilder + ->create()->withPayload('Live long and Prosper.') + ->addSignature($this->getKey2(), ['alg' => 'RS512']) + ->build() + ; + + $loaded = $this->getJWSSerializerManager()->unserialize($this->getJWSSerializerManager()->serialize('jws_json_general', $jws, 0)); + + static::assertEquals(1, $loaded->countSignatures()); + static::assertEquals('Live long and Prosper.', $loaded->getPayload()); + + static::assertFalse($jwsVerifier->verifyWithKeySet($loaded, $this->getSymmetricKeySet(), 0)); + } + + /** + * @test + */ + public function signAndLoadWithJWSWithoutSignatures(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The JWS does not contain any signature.'); + + $payload = "It\xe2\x80\x99s a dangerous business, Frodo, going out your door. You step onto the road, and if you don't keep your feet, there\xe2\x80\x99s no knowing where you might be swept off to."; + $jws = '{"payload":"SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4","signatures":[]}'; + + $jwsVerifier = $this->getJWSVerifierFactory()->create([]); + $loaded = $this->getJWSSerializerManager()->unserialize($jws); + + static::assertEquals(0, $loaded->countSignatures()); + static::assertEquals($payload, $loaded->getPayload()); + + static::assertTrue($jwsVerifier->verifyWithKeySet($loaded, $this->getSymmetricKeySet(), 0)); + } + + /** + * @see https://tools.ietf.org/html/rfc7797#section-4 + * @see https://tools.ietf.org/html/rfc7797#section-4.2 + * + * @test + */ + public function compactJSONWithUnencodedPayloadFailsBecauseOfForbiddenCharacters(): void + { + $this->expectException(LogicException::class); + $this->expectExceptionMessage('Unable to convert the JWS with non-encoded payload.'); + + $protectedHeader = [ + 'alg' => 'HS256', + 'b64' => false, + 'crit' => ['b64'], + ]; + + $key = new JWK([ + 'kty' => 'oct', + 'k' => 'AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow', + ]); + + $jwsBuilder = $this->getJWSBuilderFactory()->create(['HS256']); + $jws = $jwsBuilder + ->create()->withPayload('Live long and Prosper.') + ->addSignature($key, $protectedHeader) + ->build() + ; + + $this->getJWSSerializerManager()->serialize('jws_compact', $jws, 0); + } + + /** + * @see https://tools.ietf.org/html/rfc7797#section-4 + * @see https://tools.ietf.org/html/rfc7797#section-4.2 + * + * @test + */ + public function compactJSONWithUnencodedPayloadSucceeded(): void + { + $protectedHeader = [ + 'alg' => 'HS256', + 'b64' => false, + 'crit' => ['b64'], + ]; + + $key = new JWK([ + 'kty' => 'oct', + 'k' => 'AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow', + ]); + + $jwsBuilder = $this->getJWSBuilderFactory()->create(['HS256']); + $jws = $jwsBuilder + ->create()->withPayload('Live long and Prosper~') + ->addSignature($key, $protectedHeader) + ->build() + ; + + $compact = $this->getJWSSerializerManager()->serialize('jws_compact', $jws, 0); + static::assertEquals('eyJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19.Live long and Prosper~.nUNenbjNAEH2nNIXyQYmutiHRPnT17HcaMr5Lsho4BE', $compact); + + $loaded = $this->getJWSSerializerManager()->unserialize($compact, $serializer); + static::assertEquals(CompactSerializer::NAME, $serializer); + static::assertEquals('Live long and Prosper~', $loaded->getPayload()); + static::assertEquals('Live long and Prosper~', $loaded->getEncodedPayload()); + static::assertEquals($protectedHeader, $loaded->getSignature(0)->getProtectedHeader()); + } + + /** + * @see https://tools.ietf.org/html/rfc7797#section-4 + * @see https://tools.ietf.org/html/rfc7797#section-4.2 + * + * @test + */ + public function compactJSONWithUnencodedDetachedPayload(): void + { + $payload = '$.02'; + $protectedHeader = [ + 'alg' => 'HS256', + 'b64' => false, + 'crit' => ['b64'], + ]; + + $key = new JWK([ + 'kty' => 'oct', + 'k' => 'AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow', + ]); + + $jwsBuilder = $this->getJWSBuilderFactory()->create(['HS256']); + $jwsVerifier = $this->getJWSVerifierFactory()->create(['HS256']); + $jws = $jwsBuilder + ->create()->withPayload($payload, true) + ->addSignature($key, $protectedHeader) + ->build() + ; + $jws = $this->getJWSSerializerManager()->serialize('jws_compact', $jws, 0); + static::assertEquals('eyJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..A5dxf2s96_n5FLueVuW1Z_vh161FwXZC4YLPff6dmDY', $jws); + + $loaded = $this->getJWSSerializerManager()->unserialize($jws); + static::assertTrue($jwsVerifier->verifyWithKey($loaded, $key, 0, $payload)); + + static::assertEquals($protectedHeader, $loaded->getSignature(0)->getProtectedHeader()); + } + + /** + * The library is able to support multiple payload encoding and conversion in JSON if payload is detached. + * + * @test + */ + public function compactJSONWithUnencodedDetachedPayloadAndMultipleSignatures(): void + { + $payload = '$.02'; + $protectedHeader1 = [ + 'alg' => 'HS256', + 'b64' => false, + 'crit' => ['b64'], + ]; + $protectedHeader2 = [ + 'alg' => 'HS512', + 'b64' => false, + 'crit' => ['b64'], + ]; + + $key = new JWK([ + 'kty' => 'oct', + 'k' => 'AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow', + ]); + + $jwsBuilder = $this->getJWSBuilderFactory()->create(['HS256', 'HS512']); + $jwsVerifier = $this->getJWSVerifierFactory()->create(['HS256', 'HS512']); + $jws = $jwsBuilder + ->create()->withPayload($payload, true) + ->addSignature($key, $protectedHeader1) + ->addSignature($key, $protectedHeader2) + ->build() + ; + + $expected_result = '{"signatures":[{"signature":"A5dxf2s96_n5FLueVuW1Z_vh161FwXZC4YLPff6dmDY","protected":"eyJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19"},{"signature":"Mp-m-Vyst0zYCNkpg2RiIN8W9GO4nLU3FKsFtHzEcP4tgR4QcMys1_2m9HrDwszi0Cp2gv_Lioe6UPCcTNn6tQ","protected":"eyJhbGciOiJIUzUxMiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19"}]}'; + + static::assertEquals($expected_result, $this->getJWSSerializerManager()->serialize('jws_json_general', $jws, 0)); + + $loaded = $this->getJWSSerializerManager()->unserialize($expected_result); + static::assertTrue($jwsVerifier->verifyWithKey($loaded, $key, 0, $payload)); + static::assertEquals($protectedHeader1, $loaded->getSignature(0)->getProtectedHeader()); + } + + /** + * The library is able to support multiple payload encoding and conversion in JSON is not available if payload is not detached. + * + * @test + */ + public function compactJSONWithUnencodedPayloadAndMultipleSignatures(): void + { + $this->expectException(LogicException::class); + $this->expectExceptionMessage('Foreign payload encoding detected.'); + + $payload = '$.02'; + $protectedHeader1 = [ + 'alg' => 'HS256', + 'b64' => false, + 'crit' => ['b64'], + ]; + $protectedHeader2 = [ + 'alg' => 'HS256', + ]; + + $key = new JWK([ + 'kty' => 'oct', + 'k' => 'AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow', + ]); + + $jwsBuilder = $this->getJWSBuilderFactory()->create(['HS256']); + $jws = $jwsBuilder + ->create()->withPayload($payload) + ->addSignature($key, $protectedHeader1) + ->addSignature($key, $protectedHeader2) + ->build() + ; + + $this->getJWSSerializerManager()->serialize('jws_json_general', $jws, 0); + } + + /** + * @test + */ + public function jWSWithUnencodedPayloadButNoCritHeader(): void + { + $this->expectException(LogicException::class); + $this->expectExceptionMessage('The protected header parameter "crit" is mandatory when protected header parameter "b64" is set.'); + + $payload = '$.02'; + $protectedHeader = [ + 'alg' => 'HS256', + 'b64' => false, + ]; + + $key = new JWK([ + 'kty' => 'oct', + 'k' => 'AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow', + ]); + + $jwsBuilder = $this->getJWSBuilderFactory()->create(['HS256']); + $jws = $jwsBuilder + ->create()->withPayload($payload, true) + ->addSignature($key, $protectedHeader) + ->build() + ; + $this->getJWSSerializerManager()->serialize('jws_compact', $jws, 0); + } + + /** + * @test + */ + public function jWSWithUnencodedPayloadButCritHeaderIsNotAnArray(): void + { + $this->expectException(LogicException::class); + $this->expectExceptionMessage('The protected header parameter "crit" must be an array.'); + + $payload = '$.02'; + $protectedHeader = [ + 'alg' => 'HS256', + 'b64' => false, + 'crit' => 'foo', + ]; + + $key = new JWK([ + 'kty' => 'oct', + 'k' => 'AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow', + ]); + + $jwsBuilder = $this->getJWSBuilderFactory()->create(['HS256']); + $jws = $jwsBuilder + ->create()->withPayload($payload, true) + ->addSignature($key, $protectedHeader) + ->build() + ; + $this->getJWSSerializerManager()->serialize('jws_compact', $jws, 0); + } + + /** + * @test + */ + public function jWSWithUnencodedPayloadButCritHeaderDoesNotContainB64(): void + { + $this->expectException(LogicException::class); + $this->expectExceptionMessage('The protected header parameter "crit" must contain "b64" when protected header parameter "b64" is set.'); + + $payload = '$.02'; + $protectedHeader = [ + 'alg' => 'HS256', + 'b64' => false, + 'crit' => ['foo'], + ]; + + $key = new JWK([ + 'kty' => 'oct', + 'k' => 'AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow', + ]); + + $jwsBuilder = $this->getJWSBuilderFactory()->create(['HS256']); + $jws = $jwsBuilder + ->create()->withPayload($payload, true) + ->addSignature($key, $protectedHeader) + ->build() + ; + $this->getJWSSerializerManager()->serialize('jws_compact', $jws, 0); + } + + /** + * @see https://tools.ietf.org/html/rfc7797#section-4 + * @see https://tools.ietf.org/html/rfc7797#section-4.2 + * + * @test + */ + public function flattenedJSONWithUnencodedPayload(): void + { + $payload = '$.02'; + $protectedHeader = [ + 'alg' => 'HS256', + 'b64' => false, + 'crit' => ['b64'], + ]; + + $key = new JWK([ + 'kty' => 'oct', + 'k' => 'AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow', + ]); + + $expected_result = [ + 'protected' => 'eyJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19', + 'payload' => '$.02', + 'signature' => 'A5dxf2s96_n5FLueVuW1Z_vh161FwXZC4YLPff6dmDY', + ]; + + $jwsBuilder = $this->getJWSBuilderFactory()->create(['HS256']); + $jwsVerifier = $this->getJWSVerifierFactory()->create(['HS256']); + $jws = $jwsBuilder + ->create()->withPayload($payload) + ->addSignature($key, $protectedHeader) + ->build() + ; + $jws = $this->getJWSSerializerManager()->serialize('jws_json_flattened', $jws, 0); + + static::assertEquals($expected_result, json_decode($jws, true)); + + $loaded = $this->getJWSSerializerManager()->unserialize($jws); + static::assertTrue($jwsVerifier->verifyWithKey($loaded, $key, 0)); + + static::assertEquals($payload, $loaded->getPayload()); + static::assertEquals($protectedHeader, $loaded->getSignature(0)->getProtectedHeader()); + } + + /** + * @see https://tools.ietf.org/html/rfc7797#section-4 + * @see https://tools.ietf.org/html/rfc7797#section-4.2 + * + * @test + */ + public function flattenedJSONWithUnencodedDetachedPayload(): void + { + $payload = '$.02'; + $protectedHeader = [ + 'alg' => 'HS256', + 'b64' => false, + 'crit' => ['b64'], + ]; + + $key = new JWK([ + 'kty' => 'oct', + 'k' => 'AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow', + ]); + + $expected_result = [ + 'protected' => 'eyJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19', + 'signature' => 'A5dxf2s96_n5FLueVuW1Z_vh161FwXZC4YLPff6dmDY', + ]; + + $jwsBuilder = $this->getJWSBuilderFactory()->create(['HS256']); + $jws = $jwsBuilder + ->create()->withPayload($payload, true) + ->addSignature($key, $protectedHeader) + ->build() + ; + $jws = $this->getJWSSerializerManager()->serialize('jws_json_flattened', $jws, 0); + + static::assertEquals($expected_result, json_decode($jws, true)); + } + + /** + * @test + */ + public function signAndLoadWithoutAlgParameterInTheHeader(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('No "alg" parameter set in the header.'); + + $payload = "It\xe2\x80\x99s a dangerous business, Frodo, going out your door. You step onto the road, and if you don't keep your feet, there\xe2\x80\x99s no knowing where you might be swept off to."; + $jws = 'eyJraWQiOiJiaWxiby5iYWdnaW5zQGhvYmJpdG9uLmV4YW1wbGUifQ.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4.MRjdkly7_-oTPTS3AXP41iQIGKa80A0ZmTuV5MEaHoxnW2e5CZ5NlKtainoFmKZopdHM1O2U4mwzJdQx996ivp83xuglII7PNDi84wnB-BDkoBwA78185hX-Es4JIwmDLJK3lfWRa-XtL0RnltuYv746iYTh_qHRD68BNt1uSNCrUCTJDt5aAE6x8wW1Kt9eRo4QPocSadnHXFxnt8Is9UzpERV0ePPQdLuW3IS_de3xyIrDaLGdjluPxUAhb6L2aXic1U12podGU0KLUQSE_oI-ZnmKJ3F4uOZDnd6QZWJushZ41Axf_fcIe8u9ipH84ogoree7vjbU5y18kDquDg'; + + $jwsVerifier = $this->getJWSVerifierFactory()->create([]); + $loaded = $this->getJWSSerializerManager()->unserialize($jws); + + static::assertEquals(1, $loaded->countSignatures()); + static::assertEquals($payload, $loaded->getPayload()); + + static::assertTrue($jwsVerifier->verifyWithKeySet($loaded, $this->getSymmetricKeySet(), 0)); + } + + /** + * @test + */ + public function signAndLoadJWKSet(): void + { + $jwsBuilder = $this->getJWSBuilderFactory()->create(['HS512', 'RS512']); + $jwsVerifier = $this->getJWSVerifierFactory()->create(['HS512', 'RS512']); + $jws = $jwsBuilder + ->create()->withPayload(json_encode($this->getKeyset())) + ->addSignature($this->getKey1(), ['alg' => 'HS512'], ['foo' => 'bar']) + ->addSignature($this->getKey2(), ['alg' => 'RS512']) + ->build() + ; + + $loaded = $this->getJWSSerializerManager()->unserialize($this->getJWSSerializerManager()->serialize('jws_json_general', $jws, 0)); + static::assertEquals(2, $loaded->countSignatures()); + static::assertEquals($this->getKeyset(), JWKSet::createFromKeyData(json_decode($loaded->getPayload(), true))); + static::assertTrue($jwsVerifier->verifyWithKeySet($loaded, $this->getSymmetricKeySet(), 0)); + static::assertTrue($jwsVerifier->verifyWithKeySet($loaded, $this->getPublicKeySet(), 1)); + + static::assertEquals('HS512', $loaded->getSignature(0)->getProtectedHeaderParameter('alg')); + static::assertEquals('RS512', $loaded->getSignature(1)->getProtectedHeaderParameter('alg')); + } + + /** + * @test + */ + public function keySetIsEmpty(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('There is no key in the key set.'); + + $jwsBuilder = $this->getJWSBuilderFactory()->create(['HS512', 'RS512']); + $jwsVerifier = $this->getJWSVerifierFactory()->create(['HS512', 'RS512']); + $jws = $jwsBuilder + ->create()->withPayload(json_encode($this->getKeyset())) + ->addSignature($this->getKey1(), ['alg' => 'HS512', ['foo' => 'bar']]) + ->addSignature($this->getKey2(), ['alg' => 'RS512']) + ->build() + ; + + $loaded = $this->getJWSSerializerManager()->unserialize($this->getJWSSerializerManager()->serialize('jws_json_general', $jws, 0)); + static::assertEquals(2, $loaded->countSignatures()); + static::assertEquals($this->getKeyset(), JWKSet::createFromKeyData(json_decode($loaded->getPayload(), true))); + static::assertTrue($jwsVerifier->verifyWithKeySet($loaded, new JWKSet([]), 0)); + static::assertTrue($jwsVerifier->verifyWithKey($loaded, new JWK(['kty' => 'EC']), 1)); + } + + private function getKey1(): JWK + { + return new JWK([ + 'kty' => 'oct', + 'k' => 'AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow', + ]); + } + + private function getKey2(): JWK + { + return new JWK([ + 'kty' => 'RSA', + 'use' => 'sig', + 'key_ops' => ['sign', 'verify'], + 'n' => 'ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ', + 'e' => 'AQAB', + 'd' => 'Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97IjlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYTCBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLhBOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ', + 'p' => '4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPGBY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc', + 'q' => 'uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc', + 'dp' => 'BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3QCLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0', + 'dq' => 'h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-kyNlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU', + 'qi' => 'IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2oy26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLUW0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U', + ]); + } + + private function getKey3(): JWK + { + return new JWK([ + 'kty' => 'EC', + 'crv' => 'P-256', + 'use' => 'sig', + 'key_ops' => ['sign'], + 'x' => 'f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU', + 'y' => 'x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0', + 'd' => 'jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI', + ]); + } + + private function getKey4(): JWK + { + return new JWK([ + 'kty' => 'RSA', + 'alg' => 'PS512', + 'key_ops' => ['encrypt', 'decrypt'], + 'n' => 'ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ', + 'e' => 'AQAB', + 'd' => 'Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97IjlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYTCBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLhBOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ', + 'p' => '4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPGBY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc', + 'q' => 'uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc', + 'dp' => 'BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3QCLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0', + 'dq' => 'h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-kyNlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU', + 'qi' => 'IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2oy26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLUW0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U', + ]); + } + + private function getKey5(): JWK + { + return new JWK([ + 'kty' => 'RSA', + 'alg' => 'PS512', + 'use' => 'sig', + 'n' => 'ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ', + 'e' => 'AQAB', + 'd' => 'Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97IjlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYTCBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLhBOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ', + 'p' => '4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPGBY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc', + 'q' => 'uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc', + 'dp' => 'BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3QCLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0', + 'dq' => 'h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-kyNlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU', + 'qi' => 'IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2oy26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLUW0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U', + ]); + } + + private function getKeyset(): JWKSet + { + return new JWKSet([$this->getKey1(), $this->getKey2()]); + } + + private function getPublicKeySet(): JWKSet + { + $keys = ['keys' => [ + [ + 'kid' => '71ee230371d19630bc17fb90ccf20ae632ad8cf8', + 'kty' => 'RSA', + 'alg' => 'RS256', + 'use' => 'sig', + 'n' => 'vnMTRCMvsS04M1yaKR112aB8RxOkWHFixZO68wCRlVLxK4ugckXVD_Ebcq-kms1T2XpoWntVfBuX40r2GvcD9UsTFt_MZlgd1xyGwGV6U_tfQUll5mKxCPjr60h83LXKJ_zmLXIqkV8tAoIg78a5VRWoms_0Bn09DKT3-RBWFjk=', + 'e' => 'AQAB', + ], + [ + 'kid' => '02491f945c951adf156f370788e8ccdabf8877a8', + 'kty' => 'RSA', + 'alg' => 'RS256', + 'use' => 'sig', + 'n' => 'rI67uHIDWDgCy_Ut-FhhjTCkEcqzoO80IRgdpk_fJHlDmXhMTJKPizxbIEMs0wRHRZpwH-4D20thpnQB5Mgx6-XM9kOvcYpHSdcYME77BwX6uQG-hw2w77NOhYiCSZCLzx-5ld5Wjy0dympL-ExqQw-wrWipMX7NQhIbJqVbZ18=', + 'e' => 'AQAB', + ], + [ + 'kty' => 'RSA', + 'n' => 'oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUWcJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3Spsk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2asbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMStPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2djYgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw', + 'e' => 'AQAB', + ], + [ + 'kty' => 'RSA', + 'n' => 'sXchDaQebHnPiGvyDOAT4saGEUetSyo9MKLOoWFsueri23bOdgWp4Dy1WlUzewbgBHod5pcM9H95GQRV3JDXboIRROSBigeC5yjU1hGzHHyXss8UDprecbAYxknTcQkhslANGRUZmdTOQ5qTRsLAt6BTYuyvVRdhS8exSZEy_c4gs_7svlJJQ4H9_NxsiIoLwAEk7-Q3UXERGYw_75IDrGA84-lA_-Ct4eTlXHBIY2EaV7t7LjJaynVJCpkv4LKjTTAumiGUIuQhrNhZLuF_RJLqHpM2kgWFLU7-VTdL1VbC2tejvcI2BlMkEpk1BzBZI0KQB0GaDWFLN-aEAw3vRw', + 'e' => 'AQAB', + ], + [ + 'kty' => 'RSA', + 'n' => 'ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ', + 'e' => 'AQAB', + ], + [ + 'kty' => 'EC', + 'crv' => 'P-256', + 'x' => 'f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU', + 'y' => 'x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0', + ], + [ + 'kty' => 'EC', + 'crv' => 'P-521', + 'x' => 'AekpBQ8ST8a8VcfVOTNl353vSrDCLLJXmPk06wTjxrrjcBpXp5EOnYG_NjFZ6OvLFV1jSfS9tsz4qUxcWceqwQGk', + 'y' => 'ADSmRA43Z1DSNx_RvcLI87cdL07l6jQyyBXMoxVg_l2Th-x3S1WDhjDly79ajL4Kkd0AZMaZmh9ubmf63e3kyMj2', + ], + ]]; + + return JWKSet::createFromKeyData($keys); + } + + private function getSymmetricKeySet(): JWKSet + { + $keys = ['keys' => [ + [ + 'kid' => 'DIR_1', + 'kty' => 'oct', + 'k' => Base64Url::encode(hex2bin('00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F')), + ], + [ + 'kty' => 'oct', + 'k' => 'f5aN5V6iihwQVqP-tPNNtkIJNCwUb9-JukCIKkF0rNfxqxA771RJynYAT2xtzAP0MYaR7U5fMP_wvbRQq5l38Q', + ], + [ + 'kty' => 'oct', + 'k' => 'GawgguFyGrWKav7AX4VKUg', + ], + [ + 'kty' => 'oct', + 'k' => 'AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow', + ], + ]]; + + return JWKSet::createFromKeyData($keys); + } +} diff --git a/vendor/web-token/jwt-signature/composer.json b/vendor/web-token/jwt-signature/composer.json new file mode 100644 index 000000000..0a0f84c06 --- /dev/null +++ b/vendor/web-token/jwt-signature/composer.json @@ -0,0 +1,49 @@ +{ + "name": "web-token/jwt-signature", + "description": "Signature component of the JWT Framework.", + "type": "library", + "license": "MIT", + "keywords": ["JWS", "JWT", "JWE", "JWA", "JWK", "JWKSet", "Jot", "Jose", "RFC7515", "RFC7516", "RFC7517", "RFC7518", "RFC7519", "RFC7520", "Bundle", "Symfony"], + "homepage": "https://github.com/web-token", + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + },{ + "name": "All contributors", + "homepage": "https://github.com/web-token/jwt-signature/contributors" + } + ], + "autoload": { + "psr-4": { + "Jose\\Component\\Signature\\": "" + } + }, + "require": { + "web-token/jwt-core": "^2.1" + }, + "require-dev": { + "phpunit/phpunit": "^8.0" + }, + "suggest": { + "web-token/jwt-signature-algorithm-ecdsa": "ECDSA Based Signature Algorithms", + "web-token/jwt-signature-algorithm-eddsa": "EdDSA Based Signature Algorithms", + "web-token/jwt-signature-algorithm-hmac": "HMAC Based Signature Algorithms", + "web-token/jwt-signature-algorithm-none": "None Signature Algorithm", + "web-token/jwt-signature-algorithm-rsa": "RSA Based Signature Algorithms", + "web-token/jwt-signature-algorithm-experimental": "Experimental Signature Algorithms" + }, + "extra": { + "branch-alias": { + "v1.0": "1.0.x-dev", + "v1.1": "1.1.x-dev", + "v1.2": "1.2.x-dev", + "v1.3": "1.3.x-dev", + "v2.0": "2.0.x-dev", + "v2.1": "2.1.x-dev" + } + }, + "config": { + "sort-packages": true + } +} diff --git a/vendor/web-token/jwt-signature/phpunit.xml.dist b/vendor/web-token/jwt-signature/phpunit.xml.dist new file mode 100644 index 000000000..c8b3143f3 --- /dev/null +++ b/vendor/web-token/jwt-signature/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + ./Tests/ + + + + + + ./ + + ./vendor + ./Tests + ./src + + + +