diff --git a/.gitignore b/.gitignore index 57872d0..ba3a105 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /vendor/ +.composer.lock diff --git a/.travis.yml b/.travis.yml index dd6aaec..5197f82 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,9 @@ language: php php: - - 5.3 - - 5.4 - - 5.5 - - 5.6 - - 7 + - 8.1 + - 8.2 + - 8.3 - hhvm cache: @@ -22,8 +20,7 @@ before_script: - composer install --dev --prefer-source script: - - ./vendor/bin/phpunit --coverage-clover=coverage.clover - - ./vendor/bin/phpcs --standard=PSR2 src + - composer check after_script: - wget https://scrutinizer-ci.com/ocular.phar diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..5980ae0 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,18 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 2.0.0 + +### Breaking Changes + +- PHP 8.1 is now required +- All class properties and function parameters are now typed +- All functions now are properly return typed + +### Fixed + +- Fixed deprecation warnings of `Creation of dynamic property ... deprecated` diff --git a/README.md b/README.md index 23763dc..56b9f46 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ without setting anything to get sane defaults. build() ``` @@ -37,7 +37,7 @@ $client = Dflydev\Hawk\Client\ClientBuilder::create() setCrypto($crypto) ->setTimeProvider($timeProvider) ->setNonceProvider($nonceProvider) @@ -142,14 +142,14 @@ $isAuthenticatedResponse = $client->authenticate( build(); // Create a Hawk request based on making a POST request to a specific URL @@ -239,7 +239,7 @@ without setting anything but the credentials provider to get sane defaults. $credentialsProvider = function ($id) { if ('12345' === $id) { - return new Dflydev\Hawk\Credentials\Credentials( + return new \Dflydev\Hawk\Credentials\Credentials( 'afe89a3x', // shared key 'sha256', // default: sha256 '12345' // identifier, default: null @@ -248,7 +248,7 @@ $credentialsProvider = function ($id) { }; // Simple example -$server = Dflydev\Hawk\Server\ServerBuilder::create($credentialsProvider) +$server = \Dflydev\Hawk\Server\ServerBuilder::create($credentialsProvider) ->build() ``` @@ -259,7 +259,7 @@ $server = Dflydev\Hawk\Server\ServerBuilder::create($credentialsProvider) $credentialsProvider = function ($id) { if ('12345' === $id) { - return new Dflydev\Hawk\Credentials\Credentials( + return new \Dflydev\Hawk\Credentials\Credentials( 'afe89a3x', // shared key 'sha256', // default: sha256 '12345' // identifier, default: null @@ -268,7 +268,7 @@ $credentialsProvider = function ($id) { }; // A complete example -$server = Dflydev\Hawk\Server\ServerBuilder::create($credentialsProvider) +$server = \Dflydev\Hawk\Server\ServerBuilder::create($credentialsProvider) ->setCrypto($crypto) ->setTimeProvider($timeProvider) ->setNonceValidator($nonceValidator) @@ -307,7 +307,7 @@ try { 'hello world!' $authorization ); -} catch(Dflydev\Hawk\Server\UnauthorizedException $e) { +} catch(\Dflydev\Hawk\Server\UnauthorizedException $e) { // If authorization is incorrect (invalid mac, etc.) we can catch an // unauthorized exception. throw $e; @@ -367,7 +367,7 @@ header(sprintf("%s: %s", $header->fieldName(), $header->fieldValue())); // Create a simple credentials provider $credentialsProvider = function ($id) { if ('12345' === $id) { - return new Dflydev\Hawk\Credentials\Credentials( + return new \Dflydev\Hawk\Credentials\Credentials( 'afe89a3x', // shared key 'sha256', // default: sha256 '12345' // identifier, default: null @@ -376,7 +376,7 @@ $credentialsProvider = function ($id) { }; // Create a Hawk server -$server = Dflydev\Hawk\Server\ServerBuilder::create($credentialsProvider) +$server = \Dflydev\Hawk\Server\ServerBuilder::create($credentialsProvider) ->build() // Get the authorization header for the request; it should be in the form @@ -393,7 +393,7 @@ try { 'hello world!' $authorization ); -} catch(Dflydev\Hawk\Server\UnauthorizedException $e) { +} catch(\Dflydev\Hawk\Server\UnauthorizedException $e) { // If authorization is incorrect (invalid mac, etc.) we can catch an // unauthorized exception. throw $e; @@ -487,7 +487,7 @@ A simple implementation of `CredentialsInterface`. ```php =5.3.2" - }, - "require-dev": { - "mikey179/vfsstream": "1.1.*", - "phpunit/phpunit": "3.7.*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-0": { - "RandomLib": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Anthony Ferrara", - "email": "ircmaxell@ircmaxell.com", - "homepage": "http://blog.ircmaxell.com" - } - ], - "description": "A Library For Generating Secure Random Numbers", - "homepage": "https://github.com/ircmaxell/RandomLib", - "keywords": [ - "cryptography", - "random", - "random-numbers", - "random-strings" - ], - "time": "2015-04-08 13:58:58" - }, - { - "name": "ircmaxell/security-lib", - "version": "v1.1.0", - "source": { - "type": "git", - "url": "https://github.com/ircmaxell/SecurityLib.git", - "reference": "f3db6de12c20c9bcd1aa3db4353a1bbe0e44e1b5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ircmaxell/SecurityLib/zipball/f3db6de12c20c9bcd1aa3db4353a1bbe0e44e1b5", - "reference": "f3db6de12c20c9bcd1aa3db4353a1bbe0e44e1b5", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "require-dev": { - "mikey179/vfsstream": "1.1.*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-0": { - "SecurityLib": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Anthony Ferrara", - "email": "ircmaxell@ircmaxell.com", - "homepage": "http://blog.ircmaxell.com" - } - ], - "description": "A Base Security Library", - "homepage": "https://github.com/ircmaxell/SecurityLib", - "time": "2015-03-20 14:31:23" - } - ], - "packages-dev": [ - { - "name": "codeclimate/php-test-reporter", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/codeclimate/php-test-reporter.git", - "reference": "418ae782307841ac50fe26daa4cfe04520b0de9c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/codeclimate/php-test-reporter/zipball/418ae782307841ac50fe26daa4cfe04520b0de9c", - "reference": "418ae782307841ac50fe26daa4cfe04520b0de9c", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "php": ">=5.3", - "satooshi/php-coveralls": "0.6.*", - "symfony/console": ">=2.0" - }, - "require-dev": { - "ext-xdebug": "*", - "phpunit/phpunit": "3.7.*@stable" - }, - "bin": [ - "composer/bin/test-reporter" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.1.x-dev" - } - }, - "autoload": { - "psr-0": { - "CodeClimate\\Component": "src/", - "CodeClimate\\Bundle": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Code Climate", - "email": "hello@codeclimate.com", - "homepage": "https://codeclimate.com" - } - ], - "description": "PHP client for reporting test coverage to Code Climate", - "homepage": "https://github.com/codeclimate/php-test-reporter", - "keywords": [ - "codeclimate", - "coverage" - ], - "time": "2015-04-18 14:43:54" - }, - { - "name": "doctrine/instantiator", - "version": "1.0.4", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "f976e5de371104877ebc89bd8fecb0019ed9c119" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/f976e5de371104877ebc89bd8fecb0019ed9c119", - "reference": "f976e5de371104877ebc89bd8fecb0019ed9c119", - "shasum": "" - }, - "require": { - "php": ">=5.3,<8.0-DEV" - }, - "require-dev": { - "athletic/athletic": "~0.1.8", - "ext-pdo": "*", - "ext-phar": "*", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "2.0.*@ALPHA" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-0": { - "Doctrine\\Instantiator\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.com/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://github.com/doctrine/instantiator", - "keywords": [ - "constructor", - "instantiate" - ], - "time": "2014-10-13 12:58:55" - }, - { - "name": "guzzle/guzzle", - "version": "v3.9.3", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle3.git", - "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle3/zipball/0645b70d953bc1c067bbc8d5bc53194706b628d9", - "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "php": ">=5.3.3", - "symfony/event-dispatcher": "~2.1" - }, - "replace": { - "guzzle/batch": "self.version", - "guzzle/cache": "self.version", - "guzzle/common": "self.version", - "guzzle/http": "self.version", - "guzzle/inflection": "self.version", - "guzzle/iterator": "self.version", - "guzzle/log": "self.version", - "guzzle/parser": "self.version", - "guzzle/plugin": "self.version", - "guzzle/plugin-async": "self.version", - "guzzle/plugin-backoff": "self.version", - "guzzle/plugin-cache": "self.version", - "guzzle/plugin-cookie": "self.version", - "guzzle/plugin-curlauth": "self.version", - "guzzle/plugin-error-response": "self.version", - "guzzle/plugin-history": "self.version", - "guzzle/plugin-log": "self.version", - "guzzle/plugin-md5": "self.version", - "guzzle/plugin-mock": "self.version", - "guzzle/plugin-oauth": "self.version", - "guzzle/service": "self.version", - "guzzle/stream": "self.version" - }, - "require-dev": { - "doctrine/cache": "~1.3", - "monolog/monolog": "~1.0", - "phpunit/phpunit": "3.7.*", - "psr/log": "~1.0", - "symfony/class-loader": "~2.1", - "zendframework/zend-cache": "2.*,<2.3", - "zendframework/zend-log": "2.*,<2.3" - }, - "suggest": { - "guzzlehttp/guzzle": "Guzzle 5 has moved to a new package name. The package you have installed, Guzzle 3, is deprecated." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.9-dev" - } - }, - "autoload": { - "psr-0": { - "Guzzle": "src/", - "Guzzle\\Tests": "tests/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Guzzle Community", - "homepage": "https://github.com/guzzle/guzzle/contributors" - } - ], - "description": "PHP HTTP client. This library is deprecated in favor of https://packagist.org/packages/guzzlehttp/guzzle", - "homepage": "http://guzzlephp.org/", - "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "rest", - "web service" - ], - "time": "2015-03-18 18:23:50" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8", - "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.0" - }, - "suggest": { - "dflydev/markdown": "~1.0", - "erusev/parsedown": "~1.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-0": { - "phpDocumentor": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "mike.vanriel@naenius.com" - } - ], - "time": "2015-02-03 12:10:50" - }, - { - "name": "phpspec/prophecy", - "version": "1.4.0", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "8724cd239f8ef4c046f55a3b18b4d91cc7f3e4c5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/8724cd239f8ef4c046f55a3b18b4d91cc7f3e4c5", - "reference": "8724cd239f8ef4c046f55a3b18b4d91cc7f3e4c5", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.2", - "phpdocumentor/reflection-docblock": "~2.0", - "sebastian/comparator": "~1.1" - }, - "require-dev": { - "phpspec/phpspec": "~2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4.x-dev" - } - }, - "autoload": { - "psr-0": { - "Prophecy\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "time": "2015-03-27 19:31:25" - }, - { - "name": "phpunit/php-code-coverage", - "version": "2.0.16", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "934fd03eb6840508231a7f73eb8940cf32c3b66c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/934fd03eb6840508231a7f73eb8940cf32c3b66c", - "reference": "934fd03eb6840508231a7f73eb8940cf32c3b66c", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "phpunit/php-file-iterator": "~1.3", - "phpunit/php-text-template": "~1.2", - "phpunit/php-token-stream": "~1.3", - "sebastian/environment": "~1.0", - "sebastian/version": "~1.0" - }, - "require-dev": { - "ext-xdebug": ">=2.1.4", - "phpunit/phpunit": "~4" - }, - "suggest": { - "ext-dom": "*", - "ext-xdebug": ">=2.2.1", - "ext-xmlwriter": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "time": "2015-04-11 04:35:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "1.4.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "a923bb15680d0089e2316f7a4af8f437046e96bb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a923bb15680d0089e2316f7a4af8f437046e96bb", - "reference": "a923bb15680d0089e2316f7a4af8f437046e96bb", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "time": "2015-04-02 05:19:05" - }, - { - "name": "phpunit/php-text-template", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", - "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "Text/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "time": "2014-01-30 17:20:04" - }, - { - "name": "phpunit/php-timer", - "version": "1.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/19689d4354b295ee3d8c54b4f42c3efb69cbc17c", - "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "PHP/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "time": "2013-08-02 07:42:54" - }, - { - "name": "phpunit/php-token-stream", - "version": "1.4.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "eab81d02569310739373308137284e0158424330" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/eab81d02569310739373308137284e0158424330", - "reference": "eab81d02569310739373308137284e0158424330", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", - "keywords": [ - "tokenizer" - ], - "time": "2015-04-08 04:46:07" - }, - { - "name": "phpunit/phpunit", - "version": "4.6.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "163232991e652e6efed2f8470326fffa61e848e2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/163232991e652e6efed2f8470326fffa61e848e2", - "reference": "163232991e652e6efed2f8470326fffa61e848e2", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-spl": "*", - "php": ">=5.3.3", - "phpspec/prophecy": "~1.3,>=1.3.1", - "phpunit/php-code-coverage": "~2.0,>=2.0.11", - "phpunit/php-file-iterator": "~1.4", - "phpunit/php-text-template": "~1.2", - "phpunit/php-timer": "~1.0", - "phpunit/phpunit-mock-objects": "~2.3", - "sebastian/comparator": "~1.1", - "sebastian/diff": "~1.2", - "sebastian/environment": "~1.2", - "sebastian/exporter": "~1.2", - "sebastian/global-state": "~1.0", - "sebastian/version": "~1.0", - "symfony/yaml": "~2.1|~3.0" - }, - "suggest": { - "phpunit/php-invoker": "~1.1" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.6.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "time": "2015-04-11 05:23:21" - }, - { - "name": "phpunit/phpunit-mock-objects", - "version": "2.3.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "74ffb87f527f24616f72460e54b595f508dccb5c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/74ffb87f527f24616f72460e54b595f508dccb5c", - "reference": "74ffb87f527f24616f72460e54b595f508dccb5c", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "~1.0,>=1.0.2", - "php": ">=5.3.3", - "phpunit/php-text-template": "~1.2" - }, - "require-dev": { - "phpunit/phpunit": "~4.4" - }, - "suggest": { - "ext-soap": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.3.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", - "keywords": [ - "mock", - "xunit" - ], - "time": "2015-04-02 05:36:41" - }, - { - "name": "psr/log", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", - "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", - "shasum": "" - }, - "type": "library", - "autoload": { - "psr-0": { - "Psr\\Log\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "time": "2012-12-21 11:40:51" - }, - { - "name": "satooshi/php-coveralls", - "version": "v0.6.1", - "source": { - "type": "git", - "url": "https://github.com/satooshi/php-coveralls.git", - "reference": "dd0df95bd37a7cf5c5c50304dfe260ffe4b50760" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/satooshi/php-coveralls/zipball/dd0df95bd37a7cf5c5c50304dfe260ffe4b50760", - "reference": "dd0df95bd37a7cf5c5c50304dfe260ffe4b50760", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-json": "*", - "ext-simplexml": "*", - "guzzle/guzzle": ">=3.0", - "php": ">=5.3", - "psr/log": "1.0.0", - "symfony/config": ">=2.0", - "symfony/console": ">=2.0", - "symfony/stopwatch": ">=2.2", - "symfony/yaml": ">=2.0" - }, - "require-dev": { - "apigen/apigen": "2.8.*@stable", - "pdepend/pdepend": "dev-master", - "phpmd/phpmd": "dev-master", - "phpunit/php-invoker": ">=1.1.0,<1.2.0", - "phpunit/phpunit": "3.7.*@stable", - "sebastian/finder-facade": "dev-master", - "sebastian/phpcpd": "1.4.*@stable", - "squizlabs/php_codesniffer": "1.4.*@stable", - "theseer/fdomdocument": "dev-master" - }, - "bin": [ - "composer/bin/coveralls" - ], - "type": "library", - "autoload": { - "psr-0": { - "Contrib\\Component": "src/", - "Contrib\\Bundle": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Kitamura Satoshi", - "email": "with.no.parachute@gmail.com", - "homepage": "https://www.facebook.com/satooshi.jp" - } - ], - "description": "PHP client library for Coveralls API", - "homepage": "https://github.com/satooshi/php-coveralls", - "keywords": [ - "ci", - "coverage", - "github", - "test" - ], - "time": "2013-05-04 08:07:33" - }, - { - "name": "sebastian/comparator", - "version": "1.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "1dd8869519a225f7f2b9eb663e225298fade819e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1dd8869519a225f7f2b9eb663e225298fade819e", - "reference": "1dd8869519a225f7f2b9eb663e225298fade819e", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "sebastian/diff": "~1.2", - "sebastian/exporter": "~1.2" - }, - "require-dev": { - "phpunit/phpunit": "~4.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "http://www.github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "time": "2015-01-29 16:28:08" - }, - { - "name": "sebastian/diff", - "version": "1.3.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "863df9687835c62aa423a22412d26fa2ebde3fd3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/863df9687835c62aa423a22412d26fa2ebde3fd3", - "reference": "863df9687835c62aa423a22412d26fa2ebde3fd3", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Diff implementation", - "homepage": "http://www.github.com/sebastianbergmann/diff", - "keywords": [ - "diff" - ], - "time": "2015-02-22 15:13:53" - }, - { - "name": "sebastian/environment", - "version": "1.2.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "5a8c7d31914337b69923db26c4221b81ff5a196e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5a8c7d31914337b69923db26c4221b81ff5a196e", - "reference": "5a8c7d31914337b69923db26c4221b81ff5a196e", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "time": "2015-01-01 10:01:08" - }, - { - "name": "sebastian/exporter", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "84839970d05254c73cde183a721c7af13aede943" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/84839970d05254c73cde183a721c7af13aede943", - "reference": "84839970d05254c73cde183a721c7af13aede943", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "sebastian/recursion-context": "~1.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "time": "2015-01-27 07:23:06" - }, - { - "name": "sebastian/global-state", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/c7428acdb62ece0a45e6306f1ae85e1c05b09c01", - "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.2" - }, - "suggest": { - "ext-uopz": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "time": "2014-10-06 09:23:50" - }, - { - "name": "sebastian/recursion-context", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "3989662bbb30a29d20d9faa04a846af79b276252" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/3989662bbb30a29d20d9faa04a846af79b276252", - "reference": "3989662bbb30a29d20d9faa04a846af79b276252", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2015-01-24 09:48:32" - }, - { - "name": "sebastian/version", - "version": "1.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "ab931d46cd0d3204a91e1b9a40c4bc13032b58e4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/ab931d46cd0d3204a91e1b9a40c4bc13032b58e4", - "reference": "ab931d46cd0d3204a91e1b9a40c4bc13032b58e4", - "shasum": "" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "time": "2015-02-24 06:35:25" - }, - { - "name": "squizlabs/php_codesniffer", - "version": "2.3.1", - "source": { - "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "f3100143e94bbeeaa4f1cd7c6389c3733d3d1ce1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/f3100143e94bbeeaa4f1cd7c6389c3733d3d1ce1", - "reference": "f3100143e94bbeeaa4f1cd7c6389c3733d3d1ce1", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.1.2" - }, - "bin": [ - "scripts/phpcs", - "scripts/phpcbf" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "CodeSniffer.php", - "CodeSniffer/CLI.php", - "CodeSniffer/Exception.php", - "CodeSniffer/File.php", - "CodeSniffer/Fixer.php", - "CodeSniffer/Report.php", - "CodeSniffer/Reporting.php", - "CodeSniffer/Sniff.php", - "CodeSniffer/Tokens.php", - "CodeSniffer/Reports/", - "CodeSniffer/Tokenizers/", - "CodeSniffer/DocGenerators/", - "CodeSniffer/Standards/AbstractPatternSniff.php", - "CodeSniffer/Standards/AbstractScopeSniff.php", - "CodeSniffer/Standards/AbstractVariableSniff.php", - "CodeSniffer/Standards/IncorrectPatternException.php", - "CodeSniffer/Standards/Generic/Sniffs/", - "CodeSniffer/Standards/MySource/Sniffs/", - "CodeSniffer/Standards/PEAR/Sniffs/", - "CodeSniffer/Standards/PSR1/Sniffs/", - "CodeSniffer/Standards/PSR2/Sniffs/", - "CodeSniffer/Standards/Squiz/Sniffs/", - "CodeSniffer/Standards/Zend/Sniffs/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Greg Sherwood", - "role": "lead" - } - ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "http://www.squizlabs.com/php-codesniffer", - "keywords": [ - "phpcs", - "standards" - ], - "time": "2015-04-23 03:40:59" - }, - { - "name": "symfony/config", - "version": "v2.6.6", - "target-dir": "Symfony/Component/Config", - "source": { - "type": "git", - "url": "https://github.com/symfony/Config.git", - "reference": "d91be01336605db8da21b79bc771e46a7276d1bc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Config/zipball/d91be01336605db8da21b79bc771e46a7276d1bc", - "reference": "d91be01336605db8da21b79bc771e46a7276d1bc", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "symfony/filesystem": "~2.3" - }, - "require-dev": { - "symfony/phpunit-bridge": "~2.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.6-dev" - } - }, - "autoload": { - "psr-0": { - "Symfony\\Component\\Config\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - }, - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - } - ], - "description": "Symfony Config Component", - "homepage": "http://symfony.com", - "time": "2015-03-30 15:54:10" - }, - { - "name": "symfony/console", - "version": "v2.6.6", - "target-dir": "Symfony/Component/Console", - "source": { - "type": "git", - "url": "https://github.com/symfony/Console.git", - "reference": "5b91dc4ed5eb08553f57f6df04c4730a73992667" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Console/zipball/5b91dc4ed5eb08553f57f6df04c4730a73992667", - "reference": "5b91dc4ed5eb08553f57f6df04c4730a73992667", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/event-dispatcher": "~2.1", - "symfony/phpunit-bridge": "~2.7", - "symfony/process": "~2.1" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/process": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.6-dev" - } - }, - "autoload": { - "psr-0": { - "Symfony\\Component\\Console\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - }, - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - } - ], - "description": "Symfony Console Component", - "homepage": "http://symfony.com", - "time": "2015-03-30 15:54:10" - }, - { - "name": "symfony/event-dispatcher", - "version": "v2.6.6", - "target-dir": "Symfony/Component/EventDispatcher", - "source": { - "type": "git", - "url": "https://github.com/symfony/EventDispatcher.git", - "reference": "70f7c8478739ad21e3deef0d977b38c77f1fb284" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/70f7c8478739ad21e3deef0d977b38c77f1fb284", - "reference": "70f7c8478739ad21e3deef0d977b38c77f1fb284", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~2.0,>=2.0.5", - "symfony/dependency-injection": "~2.6", - "symfony/expression-language": "~2.6", - "symfony/phpunit-bridge": "~2.7", - "symfony/stopwatch": "~2.3" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.6-dev" - } - }, - "autoload": { - "psr-0": { - "Symfony\\Component\\EventDispatcher\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - }, - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - } - ], - "description": "Symfony EventDispatcher Component", - "homepage": "http://symfony.com", - "time": "2015-03-13 17:37:22" - }, - { - "name": "symfony/filesystem", - "version": "v2.6.6", - "target-dir": "Symfony/Component/Filesystem", - "source": { - "type": "git", - "url": "https://github.com/symfony/Filesystem.git", - "reference": "4983964b3693e4f13449cb3800c64a9112c301b4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Filesystem/zipball/4983964b3693e4f13449cb3800c64a9112c301b4", - "reference": "4983964b3693e4f13449cb3800c64a9112c301b4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "symfony/phpunit-bridge": "~2.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.6-dev" - } - }, - "autoload": { - "psr-0": { - "Symfony\\Component\\Filesystem\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - }, - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - } - ], - "description": "Symfony Filesystem Component", - "homepage": "http://symfony.com", - "time": "2015-03-22 16:55:57" - }, - { - "name": "symfony/stopwatch", - "version": "v2.6.6", - "target-dir": "Symfony/Component/Stopwatch", - "source": { - "type": "git", - "url": "https://github.com/symfony/Stopwatch.git", - "reference": "5f196e84b5640424a166d2ce9cca161ce1e9d912" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Stopwatch/zipball/5f196e84b5640424a166d2ce9cca161ce1e9d912", - "reference": "5f196e84b5640424a166d2ce9cca161ce1e9d912", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "symfony/phpunit-bridge": "~2.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.6-dev" - } - }, - "autoload": { - "psr-0": { - "Symfony\\Component\\Stopwatch\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - }, - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - } - ], - "description": "Symfony Stopwatch Component", - "homepage": "http://symfony.com", - "time": "2015-03-22 16:55:57" - }, - { - "name": "symfony/yaml", - "version": "v2.6.6", - "target-dir": "Symfony/Component/Yaml", - "source": { - "type": "git", - "url": "https://github.com/symfony/Yaml.git", - "reference": "174f009ed36379a801109955fc5a71a49fe62dd4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Yaml/zipball/174f009ed36379a801109955fc5a71a49fe62dd4", - "reference": "174f009ed36379a801109955fc5a71a49fe62dd4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "symfony/phpunit-bridge": "~2.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.6-dev" - } - }, - "autoload": { - "psr-0": { - "Symfony\\Component\\Yaml\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - }, - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - } - ], - "description": "Symfony Yaml Component", - "homepage": "http://symfony.com", - "time": "2015-03-30 15:54:10" - } - ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": { - "ircmaxell/random-lib": 20, - "codeclimate/php-test-reporter": 20 - }, - "prefer-stable": false, - "prefer-lowest": false, - "platform": [], - "platform-dev": [] -} diff --git a/data/.gitignore b/data/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/data/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/phpcs.xml.dist b/phpcs.xml.dist new file mode 100644 index 0000000..55bae07 --- /dev/null +++ b/phpcs.xml.dist @@ -0,0 +1,14 @@ + + + + + + + + + + + + src + tests + diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 0000000..51ee5ab --- /dev/null +++ b/phpstan-baseline.neon @@ -0,0 +1,11 @@ +parameters: + ignoreErrors: + - + message: "#^Unsafe usage of new static\\(\\)\\.$#" + count: 1 + path: src/Client/ClientBuilder.php + + - + message: "#^Unsafe usage of new static\\(\\)\\.$#" + count: 1 + path: src/Server/ServerBuilder.php diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 0000000..810a3c2 --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,16 @@ +includes: + - phpstan-baseline.neon + - vendor/phpstan/phpstan-phpunit/extension.neon + - vendor/phpstan/phpstan-phpunit/rules.neon + - vendor/phpstan/phpstan-deprecation-rules/rules.neon + +parameters: + tmpDir: data/phpstan + cache: + nodesByStringCountMax: 0 + level: 8 + fileExtensions: + - php + paths: + - src + - tests diff --git a/phpunit.xml.dist b/phpunit.xml.dist index a2437ec..1f44878 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,30 +1,20 @@ - - + - ./tests/unit/ - - - - - - ./tests/integration/ - - - - - - ./tests/functional/ + ./tests/unit - - - - ./src/ - - + + + ./src + + diff --git a/rector.config.php b/rector.config.php new file mode 100644 index 0000000..93eec1f --- /dev/null +++ b/rector.config.php @@ -0,0 +1,34 @@ +paths( + [ + __DIR__ . '/src', + __DIR__ . '/tests', + ] + ); + + $rectorConfig->sets( + [ + PHPUnitSetList::PHPUNIT_100, + PHPUnitSetList::PHPUNIT_CODE_QUALITY, + LevelSetList::UP_TO_PHP_81, + DowngradeLevelSetList::DOWN_TO_PHP_81, + SetList::CODE_QUALITY, + SetList::TYPE_DECLARATION, + ] + ); + + $rectorConfig->importNames(); +}; diff --git a/src/Dflydev/Hawk/Client/Client.php b/src/Client/Client.php similarity index 67% rename from src/Dflydev/Hawk/Client/Client.php rename to src/Client/Client.php index c085abf..395bebb 100644 --- a/src/Dflydev/Hawk/Client/Client.php +++ b/src/Client/Client.php @@ -5,51 +5,47 @@ use Dflydev\Hawk\Credentials\CredentialsInterface; use Dflydev\Hawk\Crypto\Artifacts; use Dflydev\Hawk\Crypto\Crypto; -use Dflydev\Hawk\Header\Header; use Dflydev\Hawk\Header\HeaderFactory; use Dflydev\Hawk\Nonce\NonceProviderInterface; use Dflydev\Hawk\Time\TimeProviderInterface; +use InvalidArgumentException; class Client implements ClientInterface { - private $crypto; - private $timeProvider; - private $nonceProvider; - private $localtimeOffset; - /** * @param integer $localtimeOffset */ public function __construct( - Crypto $crypto, - TimeProviderInterface $timeProvider, - NonceProviderInterface $nonceProvider, - $localtimeOffset + private readonly Crypto $crypto, + private readonly TimeProviderInterface $timeProvider, + private readonly NonceProviderInterface $nonceProvider, + private int $localtimeOffset ) { - $this->crypto = $crypto; - $this->timeProvider = $timeProvider; - $this->nonceProvider = $nonceProvider; - $this->localtimeOffset = $localtimeOffset; } - public function createRequest(CredentialsInterface $credentials, $uri, $method, array $options = array()) - { - $timestamp = isset($options['timestamp']) ? $options['timestamp'] : $this->timeProvider->createTimestamp(); + public function createRequest( + CredentialsInterface $credentials, + string $uri, + string $method, + array $options = [] + ): Request { + $timestamp = $options['timestamp'] ?? $this->timeProvider->createTimestamp(); if ($this->localtimeOffset) { $timestamp += $this->localtimeOffset; } + /** @var array{host: string, path?: string, query?: string, scheme: string, port?: int} $parsed */ $parsed = parse_url($uri); $host = $parsed['host']; - $resource = isset($parsed['path']) ? $parsed['path'] : ''; + $resource = $parsed['path'] ?? ''; if (isset($parsed['query'])) { - $resource .= '?'.$parsed['query']; + $resource .= '?' . $parsed['query']; } - $port = isset($parsed['port']) ? $parsed['port'] : ($parsed['scheme'] === 'https' ? 443 : 80); + $port = $parsed['port'] ?? ($parsed['scheme'] === 'https' ? 443 : 80); - $nonce = isset($options['nonce']) ? $options['nonce'] : $this->nonceProvider->createNonce(); + $nonce = $options['nonce'] ?? $this->nonceProvider->createNonce(); if (isset($options['payload']) || isset($options['content_type'])) { if (isset($options['payload']) && isset($options['content_type'])) { @@ -57,7 +53,7 @@ public function createRequest(CredentialsInterface $credentials, $uri, $method, $contentType = $options['content_type']; $hash = $this->crypto->calculatePayloadHash($payload, $credentials->algorithm(), $contentType); } else { - throw new \InvalidArgumentException( + throw new InvalidArgumentException( "If one of 'payload' and 'content_type' are specified, both must be specified." ); } @@ -67,9 +63,9 @@ public function createRequest(CredentialsInterface $credentials, $uri, $method, $hash = null; } - $ext = isset($options['ext']) ? $options['ext'] : null; - $app = isset($options['app']) ? $options['app'] : null; - $dlg = isset($options['dlg']) ? $options['dlg'] : null; + $ext = $options['ext'] ?? null; + $app = $options['app'] ?? null; + $dlg = $options['dlg'] ?? null; $artifacts = new Artifacts( $method, @@ -86,11 +82,7 @@ public function createRequest(CredentialsInterface $credentials, $uri, $method, $dlg ); - $attributes = array( - 'id' => $credentials->id(), - 'ts' => $artifacts->timestamp(), - 'nonce' => $artifacts->nonce(), - ); + $attributes = ['id' => $credentials->id(), 'ts' => $artifacts->timestamp(), 'nonce' => $artifacts->nonce()]; if (null !== $hash) { $attributes['hash'] = $hash; @@ -116,14 +108,14 @@ public function createRequest(CredentialsInterface $credentials, $uri, $method, public function authenticate( CredentialsInterface $credentials, Request $request, - $headerObjectOrString, - array $options = array() - ) { + mixed $headerObjectOrString, + array $options = [] + ): bool { $header = HeaderFactory::createFromHeaderObjectOrString( 'Server-Authorization', $headerObjectOrString, - function () { - throw new \InvalidArgumentException( + function (): never { + throw new InvalidArgumentException( 'Header must either be a string or an instance of "Dflydev\Hawk\Header\Header"' ); } @@ -134,7 +126,7 @@ function () { $payload = $options['payload']; $contentType = $options['content_type']; } else { - throw new \InvalidArgumentException( + throw new InvalidArgumentException( 'If one of "payload" and "content_type" are specified, both must be specified.' ); } @@ -179,24 +171,39 @@ function () { return $artifacts->hash() === $hash; } - public function createBewit(CredentialsInterface $credentials, $uri, $ttlSec, array $options = array()) - { - $timestamp = isset($options['timestamp']) ? $options['timestamp'] : $this->timeProvider->createTimestamp(); + /** + * @param CredentialsInterface $credentials + * @param string $uri + * @param int $ttlSec + * @param array{ + * timestamp?: int, + * ext?: string, + * } $options + * @return string + */ + public function createBewit( + CredentialsInterface $credentials, + string $uri, + int $ttlSec, + array $options = [] + ): string { + $timestamp = $options['timestamp'] ?? $this->timeProvider->createTimestamp(); if ($this->localtimeOffset) { $timestamp += $this->localtimeOffset; } + /** @var array{host: string, path?: string, query?: string, scheme: string, port?: int} $parsed */ $parsed = parse_url($uri); $host = $parsed['host']; - $resource = isset($parsed['path']) ? $parsed['path'] : ''; + $resource = $parsed['path'] ?? ''; if (isset($parsed['query'])) { - $resource .= '?'.$parsed['query']; + $resource .= '?' . $parsed['query']; } - $port = isset($parsed['port']) ? $parsed['port'] : ($parsed['scheme'] === 'https' ? 443 : 80); + $port = $parsed['port'] ?? ($parsed['scheme'] === 'https' ? 443 : 80); - $ext = isset($options['ext']) ? $options['ext'] : null; + $ext = $options['ext'] ?? null; $exp = $timestamp + $ttlSec; @@ -210,16 +217,16 @@ public function createBewit(CredentialsInterface $credentials, $uri, $ttlSec, ar $ext ); - $bewit = implode('\\', array( + $bewit = implode('\\', [ $credentials->id(), $exp, $this->crypto->calculateMac('bewit', $credentials, $artifacts), - $ext, - )); + $ext + ]); return str_replace( - array('+', '/', '=', "\n"), - array('-', '_', '', ''), + ['+', '/', '=', "\n"], + ['-', '_', '', ''], base64_encode($bewit) ); } diff --git a/src/Dflydev/Hawk/Client/ClientBuilder.php b/src/Client/ClientBuilder.php similarity index 68% rename from src/Dflydev/Hawk/Client/ClientBuilder.php rename to src/Client/ClientBuilder.php index 82129a9..e21691a 100644 --- a/src/Dflydev/Hawk/Client/ClientBuilder.php +++ b/src/Client/ClientBuilder.php @@ -10,42 +10,42 @@ class ClientBuilder { - private $crypto; - private $timeProvider; - private $nonceProvider; - private $localtimeOffset = 0; + private ?Crypto $crypto = null; + private ?TimeProviderInterface $timeProvider = null; + private ?NonceProviderInterface $nonceProvider = null; + private int $localtimeOffset = 0; - public function setCrypto(Crypto $crypto) + public function setCrypto(Crypto $crypto): static { $this->crypto = $crypto; return $this; } - public function setTimeProvider(TimeProviderInterface $timeProvider) + public function setTimeProvider(TimeProviderInterface $timeProvider): static { $this->timeProvider = $timeProvider; return $this; } - public function setNonceProvider(NonceProviderInterface $nonceProvider) + public function setNonceProvider(NonceProviderInterface $nonceProvider): static { $this->nonceProvider = $nonceProvider; return $this; } - public function setLocaltimeOffset($localtimeOffset = null) + public function setLocaltimeOffset(int $localtimeOffset): static { $this->localtimeOffset = $localtimeOffset; return $this; } - public function build() + public function build(): Client { - $crypto = $this->crypto ?: new Crypto; + $crypto = $this->crypto ?: new Crypto(); $timeProvider = $this->timeProvider ?: DefaultTimeProviderFactory::create(); $nonceProvider = $this->nonceProvider ?: DefaultNonceProviderFactory::create(); @@ -57,8 +57,8 @@ public function build() ); } - public static function create() + public static function create(): static { - return new static; + return new static(); } } diff --git a/src/Client/ClientInterface.php b/src/Client/ClientInterface.php new file mode 100644 index 0000000..8132af6 --- /dev/null +++ b/src/Client/ClientInterface.php @@ -0,0 +1,43 @@ +header; + } + + public function artifacts(): Artifacts + { + return $this->artifacts; + } +} diff --git a/src/Credentials/CallbackCredentialsProvider.php b/src/Credentials/CallbackCredentialsProvider.php new file mode 100644 index 0000000..cdf4231 --- /dev/null +++ b/src/Credentials/CallbackCredentialsProvider.php @@ -0,0 +1,18 @@ +callback, $id); + } +} diff --git a/src/Credentials/Credentials.php b/src/Credentials/Credentials.php new file mode 100644 index 0000000..b4708c5 --- /dev/null +++ b/src/Credentials/Credentials.php @@ -0,0 +1,25 @@ +id; + } + + public function key(): string + { + return $this->key; + } + + public function algorithm(): string + { + return $this->algorithm; + } +} diff --git a/src/Credentials/CredentialsInterface.php b/src/Credentials/CredentialsInterface.php new file mode 100644 index 0000000..5e7000c --- /dev/null +++ b/src/Credentials/CredentialsInterface.php @@ -0,0 +1,10 @@ +timestamp; + } + + public function nonce(): string + { + return $this->nonce; + } + + public function ext(): ?string + { + return $this->ext; + } + + public function payload(): mixed + { + return $this->payload; + } + + public function contentType(): ?string + { + return $this->contentType; + } + + public function hash(): ?string + { + return $this->hash; + } + + public function app(): ?string + { + return $this->app; + } + + public function dlg(): ?string + { + return $this->dlg; + } + + public function resource(): mixed + { + return $this->resource; + } + + public function host(): string + { + return $this->host; + } + + public function port(): int + { + return $this->port; + } + + public function method(): string + { + return $this->method; + } +} diff --git a/src/Crypto/Crypto.php b/src/Crypto/Crypto.php new file mode 100644 index 0000000..d156c7e --- /dev/null +++ b/src/Crypto/Crypto.php @@ -0,0 +1,94 @@ +generateNormalizedString($type, $attributes); + + return base64_encode( + hash_hmac( + $credentials->algorithm(), + $normalized, + $credentials->key(), + true + ) + ); + } + + public function calculateTsMac(int $timestamp, CredentialsInterface $credentials): string + { + $normalized = 'hawk.' . self::HEADER_VERSION . '.ts' . "\n" . + $timestamp . "\n"; + + return base64_encode(hash_hmac( + $credentials->algorithm(), + $normalized, + $credentials->key(), + true + )); + } + + public function fixedTimeComparison(string $a, string $b): bool + { + $mismatch = strlen($a) === strlen($b) ? 0 : 1; + if ($mismatch !== 0) { + $b = $a; + } + + for ($i = 0; $i < strlen($a); $i++) { + $ac = $a[$i]; + $bc = $b[$i]; + $mismatch += $ac === $bc ? 0 : 1; + } + + return (0 === $mismatch); + } + + private function generateNormalizedString(string $type, Artifacts $attributes): string + { + $normalized = 'hawk.' . self::HEADER_VERSION . '.' . $type . "\n" . + $attributes->timestamp() . "\n" . + $attributes->nonce() . "\n" . + strtoupper((string) $attributes->method()) . "\n" . + $attributes->resource() . "\n" . + strtolower((string) $attributes->host()) . "\n" . + $attributes->port() . "\n" . + $attributes->hash() . "\n"; + + if ($attributes->ext()) { + // TODO: escape ext + $normalized .= $attributes->ext(); + } + + $normalized .= "\n"; + + if ($attributes->app()) { + $normalized .= $attributes->app() . "\n" . + $attributes->dlg() . "\n"; + } + + return $normalized; + } +} diff --git a/src/Dflydev/Hawk/Client/ClientInterface.php b/src/Dflydev/Hawk/Client/ClientInterface.php deleted file mode 100644 index 8fe0368..0000000 --- a/src/Dflydev/Hawk/Client/ClientInterface.php +++ /dev/null @@ -1,16 +0,0 @@ -header = $header; - $this->artifacts = $artifacts; - } - - public function header() - { - return $this->header; - } - - public function artifacts() - { - return $this->artifacts; - } -} diff --git a/src/Dflydev/Hawk/Credentials/CallbackCredentialsProvider.php b/src/Dflydev/Hawk/Credentials/CallbackCredentialsProvider.php deleted file mode 100644 index a95eaab..0000000 --- a/src/Dflydev/Hawk/Credentials/CallbackCredentialsProvider.php +++ /dev/null @@ -1,18 +0,0 @@ -callback = $callback; - } - - public function loadCredentialsById($id) - { - return call_user_func($this->callback, $id); - } -} diff --git a/src/Dflydev/Hawk/Credentials/Credentials.php b/src/Dflydev/Hawk/Credentials/Credentials.php deleted file mode 100644 index b8fa9be..0000000 --- a/src/Dflydev/Hawk/Credentials/Credentials.php +++ /dev/null @@ -1,32 +0,0 @@ -key = $key; - $this->algorithm = $algorithm; - $this->id = $id; - } - - public function id() - { - return $this->id; - } - - public function key() - { - return $this->key; - } - - public function algorithm() - { - return $this->algorithm; - } -} diff --git a/src/Dflydev/Hawk/Credentials/CredentialsInterface.php b/src/Dflydev/Hawk/Credentials/CredentialsInterface.php deleted file mode 100644 index a32fe52..0000000 --- a/src/Dflydev/Hawk/Credentials/CredentialsInterface.php +++ /dev/null @@ -1,10 +0,0 @@ -method = $method; - $this->host = $host; - $this->port = $port; - $this->resource = $resource; - $this->timestamp = $timestamp; - $this->nonce = $nonce; - $this->ext = $ext; - $this->payload = $payload; - $this->contentType = $contentType; - $this->hash = $hash; - $this->app = $app; - $this->dlg = $dlg; - } - - public function timestamp() - { - return $this->timestamp; - } - - public function nonce() - { - return $this->nonce; - } - - public function ext() - { - return $this->ext; - } - - public function payload() - { - return $this->payload; - } - - public function contentType() - { - return $this->contentType; - } - - public function hash() - { - return $this->hash; - } - - public function app() - { - return $this->app; - } - - public function dlg() - { - return $this->dlg; - } - - public function resource() - { - return $this->resource; - } - - public function host() - { - return $this->host; - } - - public function port() - { - return $this->port; - } - - public function method() - { - return $this->method; - } -} diff --git a/src/Dflydev/Hawk/Crypto/Crypto.php b/src/Dflydev/Hawk/Crypto/Crypto.php deleted file mode 100644 index 1fa4f32..0000000 --- a/src/Dflydev/Hawk/Crypto/Crypto.php +++ /dev/null @@ -1,84 +0,0 @@ -generateNormalizedString($type, $attributes); - - return base64_encode(hash_hmac($credentials->algorithm(), $normalized, $credentials->key(), true)); - } - - public function calculateTsMac($ts, CredentialsInterface $credentials) - { - $normalized = 'hawk.'.self::HEADER_VERSION.'.ts'."\n". - $ts."\n"; - - return base64_encode(hash_hmac( - $credentials->algorithm(), - $normalized, - $credentials->key(), - true - )); - } - - public function fixedTimeComparison($a, $b) - { - $mismatch = strlen($a) === strlen($b) ? 0 : 1; - if ($mismatch) { - $b = $a; - } - - for ($i = 0; $i < strlen($a); $i++) { - $ac = $a[$i]; - $bc = $b[$i]; - $mismatch += $ac === $bc ? 0 : 1; - } - - return (0 === $mismatch); - } - - private function generateNormalizedString($type, Artifacts $attributes) - { - $normalized = 'hawk.'.self::HEADER_VERSION.'.'.$type."\n". - $attributes->timestamp()."\n". - $attributes->nonce()."\n". - strtoupper($attributes->method())."\n". - $attributes->resource()."\n". - strtolower($attributes->host())."\n". - $attributes->port()."\n". - $attributes->hash()."\n"; - - if ($attributes->ext()) { - // TODO: escape ext - $normalized .= $attributes->ext(); - } - - $normalized .= "\n"; - - if ($attributes->app()) { - $normalized .= $attributes->app()."\n". - $attributes->dlg()."\n"; - } - - return $normalized; - } -} diff --git a/src/Dflydev/Hawk/Header/FieldValueParserException.php b/src/Dflydev/Hawk/Header/FieldValueParserException.php deleted file mode 100644 index 92cff34..0000000 --- a/src/Dflydev/Hawk/Header/FieldValueParserException.php +++ /dev/null @@ -1,7 +0,0 @@ -fieldName = $fieldName; - $this->fieldValue = $fieldValue; - $this->attributes = $attributes ?: array(); - } - - public function fieldName() - { - return $this->fieldName; - } - - public function fieldValue() - { - return $this->fieldValue; - } - - public function attributes(array $keys = null) - { - if (null === $keys) { - return $this->attributes; - } - - $attributes = array(); - foreach ($keys as $key) { - if (isset($this->attributes[$key])) { - $attributes[$key] = $this->attributes[$key]; - } - } - - return $attributes; - } - - public function attribute($key) - { - if (isset($this->attributes[$key])) { - return $this->attributes[$key]; - } - } -} diff --git a/src/Dflydev/Hawk/Header/HeaderFactory.php b/src/Dflydev/Hawk/Header/HeaderFactory.php deleted file mode 100644 index 1e511b0..0000000 --- a/src/Dflydev/Hawk/Header/HeaderFactory.php +++ /dev/null @@ -1,43 +0,0 @@ - $value) { - if ($index++ > 0) { - $fieldValue .= ','; - } - - $fieldValue .= ' '.$key.'="'.$value.'"'; - } - } - - return new Header($fieldName, $fieldValue, $attributes); - } - - public static function createFromString($fieldName, $fieldValue, array $requiredKeys = null) - { - return static::create( - $fieldName, - HeaderParser::parseFieldValue($fieldValue, $requiredKeys) - ); - } - - public static function createFromHeaderObjectOrString($fieldName, $headerObjectOrString, $onError) - { - if (is_string($headerObjectOrString)) { - return static::createFromString($fieldName, $headerObjectOrString); - } elseif ($headerObjectOrString instanceof Header) { - return $headerObjectOrString; - } else { - call_user_func($onError); - } - } -} diff --git a/src/Dflydev/Hawk/Nonce/CallbackNonceValidator.php b/src/Dflydev/Hawk/Nonce/CallbackNonceValidator.php deleted file mode 100644 index 62bb53f..0000000 --- a/src/Dflydev/Hawk/Nonce/CallbackNonceValidator.php +++ /dev/null @@ -1,18 +0,0 @@ -callback = $callback; - } - - public function validateNonce($nonce, $timestamp) - { - return call_user_func_array($this->callback, array($nonce, $timestamp)); - } -} diff --git a/src/Dflydev/Hawk/Server/Response.php b/src/Dflydev/Hawk/Server/Response.php deleted file mode 100644 index 6f15828..0000000 --- a/src/Dflydev/Hawk/Server/Response.php +++ /dev/null @@ -1,28 +0,0 @@ -credentials = $credentials; - $this->artifacts = $artifacts; - } - - public function credentials() - { - return $this->credentials; - } - - public function artifacts() - { - return $this->artifacts; - } -} diff --git a/src/Dflydev/Hawk/Server/ServerInterface.php b/src/Dflydev/Hawk/Server/ServerInterface.php deleted file mode 100644 index c1f4986..0000000 --- a/src/Dflydev/Hawk/Server/ServerInterface.php +++ /dev/null @@ -1,26 +0,0 @@ - $attributes + */ + public function __construct( + private string $fieldName, + private string $fieldValue, + private array $attributes = [] + ) { + } + + public function fieldName(): string + { + return $this->fieldName; + } + + public function fieldValue(): string + { + return $this->fieldValue; + } + + /** + * @param string[]|null $keys + * @return array + */ + public function attributes(array $keys = null): array + { + if (null === $keys) { + return $this->attributes; + } + + $attributes = []; + foreach ($keys as $key) { + if (isset($this->attributes[$key])) { + $attributes[$key] = $this->attributes[$key]; + } + } + + return $attributes; + } + + public function attribute(string $key): mixed + { + return $this->attributes[$key] ?? null; + } +} diff --git a/src/Header/HeaderFactory.php b/src/Header/HeaderFactory.php new file mode 100644 index 0000000..439f2e4 --- /dev/null +++ b/src/Header/HeaderFactory.php @@ -0,0 +1,59 @@ +|null $attributes + */ + public static function create(string $fieldName, array $attributes = null): Header + { + $fieldValue = 'Hawk'; + + if (null !== $attributes) { + $index = 0; + foreach ($attributes as $key => $value) { + if ($index++ > 0) { + $fieldValue .= ','; + } + + $fieldValue .= ' ' . $key . '="' . $value . '"'; + } + } + + return new Header($fieldName, $fieldValue, $attributes ?? []); + } + + /** + * @param string[]|null $requiredKeys + * @throws FieldValueParserException + * @throws NotHawkAuthorizationException + */ + public static function createFromString(string $fieldName, string $fieldValue, array $requiredKeys = null): Header + { + return static::create( + $fieldName, + HeaderParser::parseFieldValue($fieldValue, $requiredKeys) + ); + } + + /** + * @param callable(): never $onError + * @throws FieldValueParserException + * @throws NotHawkAuthorizationException + */ + public static function createFromHeaderObjectOrString( + string $fieldName, + mixed $headerObjectOrString, + callable $onError + ): Header { + if (is_string($headerObjectOrString)) { + return static::createFromString($fieldName, $headerObjectOrString); + } + if ($headerObjectOrString instanceof Header) { + return $headerObjectOrString; + } + call_user_func($onError); + } +} diff --git a/src/Dflydev/Hawk/Header/HeaderParser.php b/src/Header/HeaderParser.php similarity index 56% rename from src/Dflydev/Hawk/Header/HeaderParser.php rename to src/Header/HeaderParser.php index a423a50..36a051b 100644 --- a/src/Dflydev/Hawk/Header/HeaderParser.php +++ b/src/Header/HeaderParser.php @@ -4,32 +4,40 @@ class HeaderParser { - public static function parseFieldValue($fieldValue, array $requiredKeys = null) + /** + * @param string[]|null $requiredKeys + * @return array + * @throws FieldValueParserException + */ + public static function parseFieldValue(string $fieldValue, array $requiredKeys = null): array { - if (0 !== strpos($fieldValue, 'Hawk')) { - throw new NotHawkAuthorizationException; + if (!str_starts_with($fieldValue, 'Hawk')) { + throw new NotHawkAuthorizationException(); } - $attributes = array(); + $attributes = []; $fieldValue = substr($fieldValue, 5); foreach (explode(', ', $fieldValue) as $part) { $equalsPos = strpos($part, '='); + if ($equalsPos === false) { + throw new FieldValueParserException('field did not contain a "="'); + } $key = substr($part, 0, $equalsPos); $value = substr($part, $equalsPos + 1); $attributes[$key] = trim($value, '"'); } if (null !== $requiredKeys) { - $missingKeys = array(); + $missingKeys = []; foreach ($requiredKeys as $requiredKey) { if (!isset($attributes[$requiredKey])) { $missingKeys[] = $requiredKey; } } - if (count($missingKeys)) { + if ($missingKeys !== []) { throw new FieldValueParserException( - "Field value was missing the following required key(s): ".implode(', ', $missingKeys) + "Field value was missing the following required key(s): " . implode(', ', $missingKeys) ); } } diff --git a/src/Dflydev/Hawk/Header/NotHawkAuthorizationException.php b/src/Header/NotHawkAuthorizationException.php similarity index 69% rename from src/Dflydev/Hawk/Header/NotHawkAuthorizationException.php rename to src/Header/NotHawkAuthorizationException.php index 71d33fc..f3c3226 100644 --- a/src/Dflydev/Hawk/Header/NotHawkAuthorizationException.php +++ b/src/Header/NotHawkAuthorizationException.php @@ -2,7 +2,9 @@ namespace Dflydev\Hawk\Header; -class NotHawkAuthorizationException extends \Exception +use Exception; + +class NotHawkAuthorizationException extends Exception { public function __construct() { diff --git a/src/Nonce/CallbackNonceValidator.php b/src/Nonce/CallbackNonceValidator.php new file mode 100644 index 0000000..9d4fcf3 --- /dev/null +++ b/src/Nonce/CallbackNonceValidator.php @@ -0,0 +1,18 @@ +callback, [$nonce, $timestamp]); + } +} diff --git a/src/Dflydev/Hawk/Nonce/DefaultNonceProviderFactory.php b/src/Nonce/DefaultNonceProviderFactory.php similarity index 68% rename from src/Dflydev/Hawk/Nonce/DefaultNonceProviderFactory.php rename to src/Nonce/DefaultNonceProviderFactory.php index f0361db..bddd9c9 100644 --- a/src/Dflydev/Hawk/Nonce/DefaultNonceProviderFactory.php +++ b/src/Nonce/DefaultNonceProviderFactory.php @@ -6,9 +6,9 @@ class DefaultNonceProviderFactory { - public static function create() + public static function create(): NonceProvider { - $factory = new Factory; + $factory = new Factory(); return new NonceProvider($factory->getLowStrengthGenerator()); } diff --git a/src/Dflydev/Hawk/Nonce/NonceProvider.php b/src/Nonce/NonceProvider.php similarity index 66% rename from src/Dflydev/Hawk/Nonce/NonceProvider.php rename to src/Nonce/NonceProvider.php index 3d60a6a..96a172a 100644 --- a/src/Dflydev/Hawk/Nonce/NonceProvider.php +++ b/src/Nonce/NonceProvider.php @@ -6,14 +6,11 @@ class NonceProvider implements NonceProviderInterface { - private $generator; - - public function __construct(Generator $generator) + public function __construct(private readonly Generator $generator) { - $this->generator = $generator; } - public function createNonce() + public function createNonce(): string { return $this->generator->generateString( 32, diff --git a/src/Dflydev/Hawk/Nonce/NonceProviderInterface.php b/src/Nonce/NonceProviderInterface.php similarity index 63% rename from src/Dflydev/Hawk/Nonce/NonceProviderInterface.php rename to src/Nonce/NonceProviderInterface.php index 8a5d789..be51592 100644 --- a/src/Dflydev/Hawk/Nonce/NonceProviderInterface.php +++ b/src/Nonce/NonceProviderInterface.php @@ -4,5 +4,5 @@ interface NonceProviderInterface { - public function createNonce(); + public function createNonce(): string; } diff --git a/src/Dflydev/Hawk/Nonce/NonceValidatorInterface.php b/src/Nonce/NonceValidatorInterface.php similarity index 51% rename from src/Dflydev/Hawk/Nonce/NonceValidatorInterface.php rename to src/Nonce/NonceValidatorInterface.php index 2a8f9c6..7c5beb2 100644 --- a/src/Dflydev/Hawk/Nonce/NonceValidatorInterface.php +++ b/src/Nonce/NonceValidatorInterface.php @@ -4,5 +4,5 @@ interface NonceValidatorInterface { - public function validateNonce($nonce, $timestamp); + public function validateNonce(string $nonce, int $timestamp): bool; } diff --git a/src/Server/Response.php b/src/Server/Response.php new file mode 100644 index 0000000..73a9510 --- /dev/null +++ b/src/Server/Response.php @@ -0,0 +1,25 @@ +credentials; + } + + public function artifacts(): Artifacts + { + return $this->artifacts; + } +} diff --git a/src/Dflydev/Hawk/Server/Server.php b/src/Server/Server.php similarity index 68% rename from src/Dflydev/Hawk/Server/Server.php rename to src/Server/Server.php index 1a2bab1..aa2725b 100644 --- a/src/Dflydev/Hawk/Server/Server.php +++ b/src/Server/Server.php @@ -2,71 +2,40 @@ namespace Dflydev\Hawk\Server; -use Dflydev\Hawk\Credentials\CallbackCredentialsProvider; use Dflydev\Hawk\Credentials\CredentialsInterface; use Dflydev\Hawk\Credentials\CredentialsProviderInterface; use Dflydev\Hawk\Crypto\Artifacts; use Dflydev\Hawk\Crypto\Crypto; use Dflydev\Hawk\Header\Header; use Dflydev\Hawk\Header\HeaderFactory; -use Dflydev\Hawk\Nonce\CallbackNonceValidator; use Dflydev\Hawk\Nonce\NonceValidatorInterface; use Dflydev\Hawk\Time\TimeProviderInterface; +use InvalidArgumentException; +/** + * @see \Dflydev\Hawk\Server\ServerTest + */ class Server implements ServerInterface { - private $crypto; - private $credentialsProvider; - private $timeProvider; - private $nonceValidator; - private $timestampSkewSec; - private $localtimeOffsetSec; - public function __construct( - Crypto $crypto, - $credentialsProvider, - TimeProviderInterface $timeProvider, - $nonceValidator, - $timestampSkewSec, - $localtimeOffsetSec + private readonly Crypto $crypto, + private readonly CredentialsProviderInterface $credentialsProvider, + private readonly TimeProviderInterface $timeProvider, + private readonly NonceValidatorInterface $nonceValidator, + private int $timestampSkewSec, + private int $localtimeOffsetSec ) { - if (!$credentialsProvider instanceof CredentialsProviderInterface) { - if (is_callable($credentialsProvider)) { - $credentialsProvider = new CallbackCredentialsProvider($credentialsProvider); - } else { - throw new \InvalidArgumentException( - "Credentials provider must implement CredentialsProviderInterface or must be callable" - ); - } - } - - if (!$nonceValidator instanceof NonceValidatorInterface) { - if (is_callable($nonceValidator)) { - $nonceValidator = new CallbackNonceValidator($nonceValidator); - } else { - throw new \InvalidArgumentException( - "Nonce validator must implement NonceValidatorInterface or must be callable" - ); - } - } - - $this->crypto = $crypto; - $this->credentialsProvider = $credentialsProvider; - $this->timeProvider = $timeProvider; - $this->nonceValidator = $nonceValidator; - $this->timestampSkewSec = $timestampSkewSec; - $this->localtimeOffsetSec = $localtimeOffsetSec; } public function authenticate( - $method, - $host, - $port, - $resource, - $contentType = null, - $payload = null, - $headerObjectOrString = null - ) { + string $method, + string $host, + mixed $port, + mixed $resource, + string $contentType = null, + mixed $payload = null, + mixed $headerObjectOrString = null + ): Response { if (null === $headerObjectOrString) { throw new UnauthorizedException("Missing Authorization header"); } @@ -74,7 +43,7 @@ public function authenticate( $header = HeaderFactory::createFromHeaderObjectOrString( 'Authorization', $headerObjectOrString, - function () { + function (): never { throw new UnauthorizedException("Invalid Authorization header"); } ); @@ -97,7 +66,7 @@ function () { $header->attribute('dlg') ); - foreach (array('id', 'ts', 'nonce', 'mac') as $requiredAttribute) { + foreach (['id', 'ts', 'nonce', 'mac'] as $requiredAttribute) { if (null === $header->attribute($requiredAttribute)) { throw new UnauthorizedException('Missing attributes'); } @@ -137,13 +106,13 @@ function () { $ts = $this->timeProvider->createTimestamp() + $this->localtimeOffsetSec; $tsm = $this->crypto->calculateTsMac($ts, $credentials); - throw new UnauthorizedException('Stale timestamp', array('ts' => $ts, 'tsm' => $tsm)); + throw new UnauthorizedException('Stale timestamp', ['ts' => $ts, 'tsm' => $tsm]); } return new Response($credentials, $artifacts); } - public function createHeader(CredentialsInterface $credentials, Artifacts $artifacts, array $options = array()) + public function createHeader(CredentialsInterface $credentials, Artifacts $artifacts, array $options = []): Header { if (isset($options['payload']) || isset($options['content_type'])) { if (isset($options['payload']) && isset($options['content_type'])) { @@ -151,7 +120,7 @@ public function createHeader(CredentialsInterface $credentials, Artifacts $artif $contentType = $options['content_type']; $hash = $this->crypto->calculatePayloadHash($payload, $credentials->algorithm(), $contentType); } else { - throw new \InvalidArgumentException( + throw new InvalidArgumentException( "If one of 'payload' and 'content_type' are specified, both must be specified." ); } @@ -161,7 +130,7 @@ public function createHeader(CredentialsInterface $credentials, Artifacts $artif $hash = null; } - $ext = isset($options['ext']) ? $options['ext'] : null; + $ext = $options['ext'] ?? null; $responseArtifacts = new Artifacts( $artifacts->method(), @@ -178,9 +147,7 @@ public function createHeader(CredentialsInterface $credentials, Artifacts $artif $artifacts->dlg() ); - $attributes = array( - 'mac' => $this->crypto->calculateMac('response', $credentials, $responseArtifacts), - ); + $attributes = ['mac' => $this->crypto->calculateMac('response', $credentials, $responseArtifacts)]; if ($hash !== null) { $attributes['hash'] = $hash; @@ -195,47 +162,49 @@ public function createHeader(CredentialsInterface $credentials, Artifacts $artif public function authenticatePayload( CredentialsInterface $credentials, - $payload, - $contentType, - $hash - ) { + mixed $payload, + string $contentType, + string $hash + ): bool { $calculatedHash = $this->crypto->calculatePayloadHash($payload, $credentials->algorithm(), $contentType); return $this->crypto->fixedTimeComparison($calculatedHash, $hash); } public function authenticateBewit( - $host, - $port, - $resource - ) { + string $host, + int $port, + mixed $resource + ): Response { // Measure now before any other processing $now = $this->timeProvider->createTimestamp() + $this->localtimeOffsetSec; - if (!preg_match( - '/^(\/.*)([\?&])bewit\=([^&$]*)(?:&(.+))?$/', - $resource, - $resourceParts - )) { + if ( + !preg_match( + '/^(\/.*)([?&])bewit=([^&$]*)(?:&(.+))?$/', + (string) $resource, + $resourceParts + ) + ) { // TODO: Should this do something else? throw new UnauthorizedException('Malformed resource or does not contan bewit'); } $bewit = base64_decode(str_replace( - array('-', '_', '', ''), - array('+', '/', '=', "\n"), + ['-', '_', '', ''], + ['+', '/', '=', "\n"], $resourceParts[3] )); - list ($id, $exp, $mac, $ext) = explode('\\', $bewit); + [$id, $exp, $mac, $ext] = explode('\\', $bewit); - if ($exp < $now) { + if ((int)$exp < $now) { throw new UnauthorizedException('Access expired'); } $resource = $resourceParts[1]; if (isset($resourceParts[4])) { - $resource .= $resourceParts[2].$resourceParts[4]; + $resource .= $resourceParts[2] . $resourceParts[4]; } $artifacts = new Artifacts( @@ -243,7 +212,7 @@ public function authenticateBewit( $host, $port, $resource, - $exp, + (int)$exp, '', $ext ); diff --git a/src/Dflydev/Hawk/Server/ServerBuilder.php b/src/Server/ServerBuilder.php similarity index 50% rename from src/Dflydev/Hawk/Server/ServerBuilder.php rename to src/Server/ServerBuilder.php index 9c94c41..efd2d4b 100644 --- a/src/Dflydev/Hawk/Server/ServerBuilder.php +++ b/src/Server/ServerBuilder.php @@ -2,66 +2,67 @@ namespace Dflydev\Hawk\Server; +use Dflydev\Hawk\Credentials\CredentialsProviderInterface; use Dflydev\Hawk\Crypto\Crypto; +use Dflydev\Hawk\Nonce\CallbackNonceValidator; +use Dflydev\Hawk\Nonce\NonceValidatorInterface; use Dflydev\Hawk\Time\DefaultTimeProviderFactory; use Dflydev\Hawk\Time\TimeProviderInterface; class ServerBuilder { - private $crypto; - private $credentialsProvider; - private $timeProvider; - private $nonceValidator; - private $timestampSkewSec; - private $localtimeOffsetSec; - - public function __construct($credentialsProvider) + private ?Crypto $crypto = null; + private ?TimeProviderInterface $timeProvider = null; + private ?NonceValidatorInterface $nonceValidator = null; + private ?int $timestampSkewSec = null; + private ?int $localtimeOffsetSec = null; + + public function __construct(private CredentialsProviderInterface $credentialsProvider) { - $this->credentialsProvider = $credentialsProvider; } - public function setCrypto(Crypto $crypto) + public function setCrypto(Crypto $crypto): static { $this->crypto = $crypto; return $this; } - public function setTimeProvider(TimeProviderInterface $timeProvider) + public function setTimeProvider(TimeProviderInterface $timeProvider): static { $this->timeProvider = $timeProvider; return $this; } - public function setNonceValidator($nonceValidator) + public function setNonceValidator(NonceValidatorInterface $nonceValidator): static { $this->nonceValidator = $nonceValidator; return $this; } - public function setTimestampSkewSec($timestampSkewSec = null) + public function setTimestampSkewSec(?int $timestampSkewSec = null): static { $this->timestampSkewSec = $timestampSkewSec; return $this; } - public function setLocaltimeOffsetSec($localtimeOffsetSec = null) + public function setLocaltimeOffsetSec(?int $localtimeOffsetSec = null): static { $this->localtimeOffsetSec = $localtimeOffsetSec; return $this; } - public function build() + public function build(): Server { - $crypto = $this->crypto ?: new Crypto; + $crypto = $this->crypto ?: new Crypto(); $timeProvider = $this->timeProvider ?: DefaultTimeProviderFactory::create(); - $nonceValidator = $this->nonceValidator ?: function($nonce, $timestamp) { - return true; - }; + $nonceValidator = $this->nonceValidator ?: new CallbackNonceValidator( + static fn(string $nonce, int $timestamp): bool => true + ); $timestampSkewSec = $this->timestampSkewSec ?: 60; $localtimeOffsetSec = $this->localtimeOffsetSec ?: 0; @@ -75,7 +76,7 @@ public function build() ); } - public static function create($credentialsProvider) + public static function create(CredentialsProviderInterface $credentialsProvider): static { return new static($credentialsProvider); } diff --git a/src/Server/ServerInterface.php b/src/Server/ServerInterface.php new file mode 100644 index 0000000..e3c35a2 --- /dev/null +++ b/src/Server/ServerInterface.php @@ -0,0 +1,32 @@ + $options + */ + public function createHeader(CredentialsInterface $credentials, Artifacts $artifacts, array $options = []): Header; + + public function authenticatePayload( + CredentialsInterface $credentials, + mixed $payload, + string $contentType, + string $hash + ): bool; +} diff --git a/src/Dflydev/Hawk/Server/UnauthorizedException.php b/src/Server/UnauthorizedException.php similarity index 52% rename from src/Dflydev/Hawk/Server/UnauthorizedException.php rename to src/Server/UnauthorizedException.php index 0324ff0..d1074c6 100644 --- a/src/Dflydev/Hawk/Server/UnauthorizedException.php +++ b/src/Server/UnauthorizedException.php @@ -2,27 +2,30 @@ namespace Dflydev\Hawk\Server; +use Exception; +use Dflydev\Hawk\Header\Header; use Dflydev\Hawk\Header\HeaderFactory; -class UnauthorizedException extends \Exception +class UnauthorizedException extends Exception { - private $attributes; - private $header; + private ?Header $header = null; - public function __construct($message = null, array $attributes = null) + /** + * @param array $attributes + */ + public function __construct(string $message = '', private array $attributes = []) { parent::__construct($message); - $this->attributes = $attributes ?: array(); } - public function getHeader() + public function getHeader(): Header { if (null !== $this->header) { return $this->header; } $attributes = $this->attributes; - if ($this->getMessage()) { + if ($this->getMessage() !== '' && $this->getMessage() !== '0') { $attributes['error'] = $this->getMessage(); } diff --git a/src/Dflydev/Hawk/Time/ConstantTimeProvider.php b/src/Time/ConstantTimeProvider.php similarity index 55% rename from src/Dflydev/Hawk/Time/ConstantTimeProvider.php rename to src/Time/ConstantTimeProvider.php index 45d1a23..f2ebd29 100644 --- a/src/Dflydev/Hawk/Time/ConstantTimeProvider.php +++ b/src/Time/ConstantTimeProvider.php @@ -4,14 +4,11 @@ class ConstantTimeProvider implements TimeProviderInterface { - private $time; - - public function __construct($time) + public function __construct(private int $time) { - $this->time = $time; } - public function createTimestamp() + public function createTimestamp(): int { return $this->time; } diff --git a/src/Dflydev/Hawk/Time/DefaultTimeProviderFactory.php b/src/Time/DefaultTimeProviderFactory.php similarity index 50% rename from src/Dflydev/Hawk/Time/DefaultTimeProviderFactory.php rename to src/Time/DefaultTimeProviderFactory.php index c475bea..df8cc7f 100644 --- a/src/Dflydev/Hawk/Time/DefaultTimeProviderFactory.php +++ b/src/Time/DefaultTimeProviderFactory.php @@ -4,8 +4,8 @@ class DefaultTimeProviderFactory { - public static function create() + public static function create(): TimeProvider { - return new TimeProvider; + return new TimeProvider(); } } diff --git a/src/Dflydev/Hawk/Time/TimeProvider.php b/src/Time/TimeProvider.php similarity index 74% rename from src/Dflydev/Hawk/Time/TimeProvider.php rename to src/Time/TimeProvider.php index a57ce1a..029a449 100644 --- a/src/Dflydev/Hawk/Time/TimeProvider.php +++ b/src/Time/TimeProvider.php @@ -4,7 +4,7 @@ class TimeProvider implements TimeProviderInterface { - public function createTimestamp() + public function createTimestamp(): int { return time(); } diff --git a/src/Dflydev/Hawk/Time/TimeProviderInterface.php b/src/Time/TimeProviderInterface.php similarity index 62% rename from src/Dflydev/Hawk/Time/TimeProviderInterface.php rename to src/Time/TimeProviderInterface.php index 9d48f97..4abc42a 100644 --- a/src/Dflydev/Hawk/Time/TimeProviderInterface.php +++ b/src/Time/TimeProviderInterface.php @@ -4,5 +4,5 @@ interface TimeProviderInterface { - public function createTimestamp(); + public function createTimestamp(): int; } diff --git a/tests/unit/Dflydev/Hawk/Client/ClientTest.php b/tests/Unit/Client/ClientTest.php similarity index 55% rename from tests/unit/Dflydev/Hawk/Client/ClientTest.php rename to tests/Unit/Client/ClientTest.php index 7bd9cf5..167ac62 100644 --- a/tests/unit/Dflydev/Hawk/Client/ClientTest.php +++ b/tests/Unit/Client/ClientTest.php @@ -1,17 +1,16 @@ build(); @@ -21,15 +20,14 @@ public function shouldCreateBewit() 'exqbZWtykFZIh2D7cXi9dA' ); - $this->assertEquals( - 'ZXhxYlpXdHlrRlpJaDJEN2NYaTlkQVwxMzY4OTk2ODAwXE8wbWhwcmdvWHFGNDhEbHc1RldBV3ZWUUlwZ0dZc3FzWDc2dHBvNkt5cUk9XA', + $this->assertSame( + 'ZXhxYlpXdHlrRlpJaDJEN2NYaTlkQVwxMzY4OTk2ODAwXE8wbWhwcmdv' . + 'WHFGNDhEbHc1RldBV3ZWUUlwZ0dZc3FzWDc2dHBvNkt5cUk9XA', $client->createBewit( $tentTestVectorsCredentials, 'https://example.com/posts', 0, - array( - 'timestamp' => 1368996800, - ) + ['timestamp' => 1368996800] ) ); } diff --git a/tests/Unit/Crypto/ArtifactsTest.php b/tests/Unit/Crypto/ArtifactsTest.php new file mode 100644 index 0000000..ba96110 --- /dev/null +++ b/tests/Unit/Crypto/ArtifactsTest.php @@ -0,0 +1,42 @@ +assertSame('testmethod', $artifacts->method()); + $this->assertSame('testhost', $artifacts->host()); + $this->assertSame(80, $artifacts->port()); + $this->assertSame('testresource', $artifacts->resource()); + $this->assertSame($time, $artifacts->timestamp()); + $this->assertSame('testnonce', $artifacts->nonce()); + $this->assertSame('testext', $artifacts->ext()); + $this->assertSame('testpayload', $artifacts->payload()); + $this->assertSame('testcontenttype', $artifacts->contentType()); + $this->assertSame('testhash', $artifacts->hash()); + $this->assertSame('testapp', $artifacts->app()); + $this->assertSame('testdlg', $artifacts->dlg()); + } +} diff --git a/tests/Unit/Crypto/CryptoTest.php b/tests/Unit/Crypto/CryptoTest.php new file mode 100644 index 0000000..bf82ee8 --- /dev/null +++ b/tests/Unit/Crypto/CryptoTest.php @@ -0,0 +1,215 @@ +calculatePayloadHash( + $payload, + $algorithm, + $contentType + ); + + $this->assertSame($expectedHash, $calculatedHash); + } + + public static function payloadDataProvider(): Generator + { + yield [ + 'neQFHgYKl/jFqDINrC21uLS0gkFglTz789rzcSr7HYU=', + '{"type":"https://tent.io/types/status/v0#"}', + 'sha256', + 'application/vnd.tent.post.v0+json' + ]; + } + + #[Test] + #[DataProvider('macDataProvider')] + public function shouldCalculateMac( + string $expectedMac, + string $type, + CredentialsInterface $credentials, + Artifacts $artifacts + ): void { + $crypto = new Crypto(); + + $calculatedMac = $crypto->calculateMac($type, $credentials, $artifacts); + + $this->assertSame($expectedMac, $calculatedMac); + } + + public static function macDataProvider(): Generator + { + $tentTestVectorsCredentials = new Credentials( + 'HX9QcbD-r3ItFEnRcAuOSg', + 'sha256', + 'exqbZWtykFZIh2D7cXi9dA' + ); + + $tentTestVectorsAttributes = [ + 'method' => 'POST', + 'host' => 'example.com', + 'port' => 443, + 'resource' => '/posts', + 'timestamp' => 1368996800, + 'nonce' => '3yuYCD4Z', + 'payload' => '{"type":"https://tent.io/types/status/v0#"}', + 'content_type' => 'application/vnd.tent.post.v0+json', + 'hash' => 'neQFHgYKl/jFqDINrC21uLS0gkFglTz789rzcSr7HYU=' + ]; + yield [ + // + // App request w/hash + // + '2sttHCQJG9ejj1x7eCi35FP23Miu9VtlaUgwk68DTpM=', + 'header', + $tentTestVectorsCredentials, + new Artifacts( + $tentTestVectorsAttributes['method'], + $tentTestVectorsAttributes['host'], + $tentTestVectorsAttributes['port'], + $tentTestVectorsAttributes['resource'], + $tentTestVectorsAttributes['timestamp'], + $tentTestVectorsAttributes['nonce'], + null, + $tentTestVectorsAttributes['payload'], + $tentTestVectorsAttributes['content_type'], + $tentTestVectorsAttributes['hash'], + 'wn6yzHGe5TLaT-fvOPbAyQ' + ), + ]; + yield [ + // + // Server Response (App request w/hash) + // + 'lTG3kTBr33Y97Q4KQSSamu9WY/mOUKnZzq/ho9x+yxw=', + 'response', + $tentTestVectorsCredentials, + new Artifacts( + $tentTestVectorsAttributes['method'], + $tentTestVectorsAttributes['host'], + $tentTestVectorsAttributes['port'], + $tentTestVectorsAttributes['resource'], + $tentTestVectorsAttributes['timestamp'], + $tentTestVectorsAttributes['nonce'], + null, + null, + null, + null, + 'wn6yzHGe5TLaT-fvOPbAyQ' + ), + ]; + yield [ + // + // Relationship Request + // + 'OO2ldBDSw8KmNHlEdTC4BciIl8+uiuCRvCnJ9KkcR3Y=', + 'header', + $tentTestVectorsCredentials, + new Artifacts( + $tentTestVectorsAttributes['method'], + $tentTestVectorsAttributes['host'], + $tentTestVectorsAttributes['port'], + $tentTestVectorsAttributes['resource'], + $tentTestVectorsAttributes['timestamp'], + $tentTestVectorsAttributes['nonce'] + ), + ]; + yield [ + // + // Server Response w/ hash (Relationship Request) + // + 'LvxASIZ2gop5cwE2mNervvz6WXkPmVslwm11MDgEZ5E=', + 'response', + $tentTestVectorsCredentials, + new Artifacts( + $tentTestVectorsAttributes['method'], + $tentTestVectorsAttributes['host'], + $tentTestVectorsAttributes['port'], + $tentTestVectorsAttributes['resource'], + $tentTestVectorsAttributes['timestamp'], + $tentTestVectorsAttributes['nonce'], + null, + $tentTestVectorsAttributes['payload'], + $tentTestVectorsAttributes['content_type'], + $tentTestVectorsAttributes['hash'] + ), + ]; + yield [ + // + // Bewit (GET /posts) + // + 'O0mhprgoXqF48Dlw5FWAWvVQIpgGYsqsX76tpo6KyqI=', + 'bewit', + $tentTestVectorsCredentials, + new Artifacts( + 'GET', + $tentTestVectorsAttributes['host'], + $tentTestVectorsAttributes['port'], + $tentTestVectorsAttributes['resource'], + $tentTestVectorsAttributes['timestamp'], + '' + ), + ]; + } + + #[Test] + #[DataProvider('tsMacDataProvider')] + public function shouldCalculateTsMac( + string $expectedTsMac, + int $ts, + CredentialsInterface $credentials + ): void { + $crypto = new Crypto(); + + $calculatedTsMac = $crypto->calculateTsMac($ts, $credentials); + + $this->assertSame($expectedTsMac, $calculatedTsMac); + } + + public static function tsMacDataProvider(): Generator + { + $tentTestVectorsCredentials = new Credentials( + 'HX9QcbD-r3ItFEnRcAuOSg', + 'sha256', + 'exqbZWtykFZIh2D7cXi9dA' + ); + + $tentTestVectorsAttributes = [ + 'method' => 'POST', + 'host' => 'example.com', + 'port' => 443, + 'resource' => '/posts', + 'timestamp' => 1368996800, + 'nonce' => '3yuYCD4Z', + 'payload' => '{"type":"https://tent.io/types/status/v0#"}', + 'content_type' => 'application/vnd.tent.post.v0+json', + 'hash' => 'neQFHgYKl/jFqDINrC21uLS0gkFglTz789rzcSr7HYU=' + ]; + + yield [ + 'HPDcD5S3Kw7LM/oyoXKcgv2Z30RnOLAI5ebXpYDGfo4=', + $tentTestVectorsAttributes['timestamp'], + $tentTestVectorsCredentials + ]; + } +} diff --git a/tests/Unit/Server/ServerTest.php b/tests/Unit/Server/ServerTest.php new file mode 100644 index 0000000..7f7503d --- /dev/null +++ b/tests/Unit/Server/ServerTest.php @@ -0,0 +1,36 @@ + new Credentials( + 'HX9QcbD-r3ItFEnRcAuOSg', + 'sha256', + 'exqbZWtykFZIh2D7cXi9dA' + ); + + $server = ServerBuilder::create(new CallbackCredentialsProvider($credentialsProvider)) + ->setTimeProvider(new ConstantTimeProvider(1368996800)) + ->build(); + + $response = $server->authenticateBewit( + 'example.com', + 443, + '/posts?bewit=ZXhxYlpXdHlrRlpJaDJEN2NYaTlkQVwxMzY4OTk2' . + 'ODAwXE8wbWhwcmdvWHFGNDhEbHc1RldBV3ZWUUlwZ0dZc3FzWDc2dHBvNkt5cUk9XA' + ); + + $this->assertSame('/posts', $response->artifacts()->resource()); + } +} diff --git a/tests/unit/Dflydev/Hawk/Crypto/ArtifactsTest.php b/tests/unit/Dflydev/Hawk/Crypto/ArtifactsTest.php deleted file mode 100644 index 4b608d7..0000000 --- a/tests/unit/Dflydev/Hawk/Crypto/ArtifactsTest.php +++ /dev/null @@ -1,38 +0,0 @@ -assertEquals('testmethod', $artifacts->method()); - $this->assertEquals('testhost', $artifacts->host()); - $this->assertEquals('testport', $artifacts->port()); - $this->assertEquals('testresource', $artifacts->resource()); - $this->assertEquals('testtimestamp', $artifacts->timestamp()); - $this->assertEquals('testnonce', $artifacts->nonce()); - $this->assertEquals('testext', $artifacts->ext()); - $this->assertEquals('testpayload', $artifacts->payload()); - $this->assertEquals('testcontenttype', $artifacts->contentType()); - $this->assertEquals('testhash', $artifacts->hash()); - $this->assertEquals('testapp', $artifacts->app()); - $this->assertEquals('testdlg', $artifacts->dlg()); - } -} diff --git a/tests/unit/Dflydev/Hawk/Crypto/CryptoTest.php b/tests/unit/Dflydev/Hawk/Crypto/CryptoTest.php deleted file mode 100644 index e4f5ade..0000000 --- a/tests/unit/Dflydev/Hawk/Crypto/CryptoTest.php +++ /dev/null @@ -1,238 +0,0 @@ -calculatePayloadHash( - $payload, - $algorithm, - $contentType - ); - - $this->assertEquals($expectedHash, $calculatedHash); - } - - public function payloadDataProvider() - { - return array( - array( - 'neQFHgYKl/jFqDINrC21uLS0gkFglTz789rzcSr7HYU=', - '{"type":"https://tent.io/types/status/v0#"}', - 'sha256', - 'application/vnd.tent.post.v0+json' - ) - ); - } - - /** - * @test - * @dataProvider macDataProvider - */ - public function shouldCalculateMac( - $expectedMac, - $type, - CredentialsInterface $credentials, - Artifacts $artifacts - ) { - $crypto = new Crypto; - - $calculatedMac = $crypto->calculateMac($type, $credentials, $artifacts); - - $this->assertEquals($expectedMac, $calculatedMac); - } - - public function macDataProvider() - { - $tentTestVectorsCredentials = new Credentials( - 'HX9QcbD-r3ItFEnRcAuOSg', - 'sha256', - 'exqbZWtykFZIh2D7cXi9dA' - ); - - $tentTestVectorsAttributes = array( - 'method' => 'POST', - 'host' => 'example.com', - 'port' => 443, - 'resource' => '/posts', - 'timestamp' => 1368996800, - 'nonce' => '3yuYCD4Z', - 'payload' => '{"type":"https://tent.io/types/status/v0#"}', - 'content_type' => 'application/vnd.tent.post.v0+json', - 'hash' => 'neQFHgYKl/jFqDINrC21uLS0gkFglTz789rzcSr7HYU=', - ); - - return array( - array( - - // - // App request w/hash - // - - '2sttHCQJG9ejj1x7eCi35FP23Miu9VtlaUgwk68DTpM=', - - 'header', - $tentTestVectorsCredentials, - new Artifacts( - $tentTestVectorsAttributes['method'], - $tentTestVectorsAttributes['host'], - $tentTestVectorsAttributes['port'], - $tentTestVectorsAttributes['resource'], - $tentTestVectorsAttributes['timestamp'], - $tentTestVectorsAttributes['nonce'], - null, - $tentTestVectorsAttributes['payload'], - $tentTestVectorsAttributes['content_type'], - $tentTestVectorsAttributes['hash'], - 'wn6yzHGe5TLaT-fvOPbAyQ' - ), - ), - array( - - // - // Server Response (App request w/hash) - // - - 'lTG3kTBr33Y97Q4KQSSamu9WY/mOUKnZzq/ho9x+yxw=', - - 'response', - $tentTestVectorsCredentials, - new Artifacts( - $tentTestVectorsAttributes['method'], - $tentTestVectorsAttributes['host'], - $tentTestVectorsAttributes['port'], - $tentTestVectorsAttributes['resource'], - $tentTestVectorsAttributes['timestamp'], - $tentTestVectorsAttributes['nonce'], - null, - null, - null, - null, - 'wn6yzHGe5TLaT-fvOPbAyQ' - ), - ), - array( - - // - // Relationship Request - // - - 'OO2ldBDSw8KmNHlEdTC4BciIl8+uiuCRvCnJ9KkcR3Y=', - - 'header', - $tentTestVectorsCredentials, - new Artifacts( - $tentTestVectorsAttributes['method'], - $tentTestVectorsAttributes['host'], - $tentTestVectorsAttributes['port'], - $tentTestVectorsAttributes['resource'], - $tentTestVectorsAttributes['timestamp'], - $tentTestVectorsAttributes['nonce'] - ), - ), - array( - - // - // Server Response w/ hash (Relationship Request) - // - - 'LvxASIZ2gop5cwE2mNervvz6WXkPmVslwm11MDgEZ5E=', - - 'response', - $tentTestVectorsCredentials, - new Artifacts( - $tentTestVectorsAttributes['method'], - $tentTestVectorsAttributes['host'], - $tentTestVectorsAttributes['port'], - $tentTestVectorsAttributes['resource'], - $tentTestVectorsAttributes['timestamp'], - $tentTestVectorsAttributes['nonce'], - null, - $tentTestVectorsAttributes['payload'], - $tentTestVectorsAttributes['content_type'], - $tentTestVectorsAttributes['hash'] - ), - ), - array( - - // - // Bewit (GET /posts) - // - - 'O0mhprgoXqF48Dlw5FWAWvVQIpgGYsqsX76tpo6KyqI=', - - 'bewit', - $tentTestVectorsCredentials, - new Artifacts( - 'GET', - $tentTestVectorsAttributes['host'], - $tentTestVectorsAttributes['port'], - $tentTestVectorsAttributes['resource'], - $tentTestVectorsAttributes['timestamp'], - '' - ), - ), - ); - } - - /** - * @test - * @dataProvider tsMacDataProvider - */ - public function shouldCalculateTsMac( - $expectedTsMac, - $ts, - CredentialsInterface $credentials - ) { - $crypto = new Crypto; - - $calculatedTsMac = $crypto->calculateTsMac($ts, $credentials); - - $this->assertEquals($expectedTsMac, $calculatedTsMac); - } - - public function tsMacDataProvider() - { - $tentTestVectorsCredentials = new Credentials( - 'HX9QcbD-r3ItFEnRcAuOSg', - 'sha256', - 'exqbZWtykFZIh2D7cXi9dA' - ); - - $tentTestVectorsAttributes = array( - 'method' => 'POST', - 'host' => 'example.com', - 'port' => 443, - 'resource' => '/posts', - 'timestamp' => 1368996800, - 'nonce' => '3yuYCD4Z', - 'payload' => '{"type":"https://tent.io/types/status/v0#"}', - 'content_type' => 'application/vnd.tent.post.v0+json', - 'hash' => 'neQFHgYKl/jFqDINrC21uLS0gkFglTz789rzcSr7HYU=', - ); - - return array( - array( - 'HPDcD5S3Kw7LM/oyoXKcgv2Z30RnOLAI5ebXpYDGfo4=', - - $tentTestVectorsAttributes['timestamp'], - $tentTestVectorsCredentials, - ), - ); - } -} diff --git a/tests/unit/Dflydev/Hawk/Server/ServerTest.php b/tests/unit/Dflydev/Hawk/Server/ServerTest.php deleted file mode 100644 index b9a6da0..0000000 --- a/tests/unit/Dflydev/Hawk/Server/ServerTest.php +++ /dev/null @@ -1,35 +0,0 @@ -setTimeProvider(new ConstantTimeProvider(1368996800)) - ->build(); - - $response = $server->authenticateBewit( - 'example.com', - 443, - '/posts?bewit=ZXhxYlpXdHlrRlpJaDJEN2NYaTlkQVwxMzY4OTk2ODAwXE8wbWhwcmdvWHFGNDhEbHc1RldBV3ZWUUlwZ0dZc3FzWDc2dHBvNkt5cUk9XA' - ); - - $this->assertEquals('/posts', $response->artifacts()->resource()); - } -}