diff --git a/.github/workflows/run-test.yml b/.github/workflows/run-test.yml index 2d69d18..0b3245e 100644 --- a/.github/workflows/run-test.yml +++ b/.github/workflows/run-test.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php-version: ['8.2', '8.3'] + php-version: ['8.0', '8.1'] steps: - uses: shivammathur/setup-php@v2 with: @@ -35,5 +35,7 @@ jobs: ${{ runner.os }}-php-${{ matrix.php-version }}- - name: Install Dependencies run: composer install --no-scripts --no-ansi --no-interaction --no-progress + - name: Run PHP Code Sniffer + run: vendor/bin/phpcs --extensions=php --warning-severity=0 --standard=PSR12 -p ./src - name: Run PHPStan run: vendor/bin/phpstan analyse --no-progress -c phpstan.neon diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..0bef681 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,16 @@ +language: php +sudo: required +php: + - 7.4 + - 8.0 + - 8.1 + - nightly +install: + - curl -s http://getcomposer.org/installer | php + - php composer.phar install --dev --no-interaction +script: + - vendor/bin/phpcs --report=full --report-file=./report.txt --extensions=php --warning-severity=0 --standard=PSR2 -p ./src + - vendor/bin/phpstan analyse -c phpstan.neon +jobs: + allow_failures: + - php: nightly diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9549c14 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Roadiz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/LICENSE.md b/LICENSE.md deleted file mode 100644 index 8e18fa6..0000000 --- a/LICENSE.md +++ /dev/null @@ -1,9 +0,0 @@ -The MIT License (MIT) - -Copyright © 2024 Ambroise Maupate - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5f4d6d2 --- /dev/null +++ b/Makefile @@ -0,0 +1,3 @@ +test: + vendor/bin/phpcs --report=full --report-file=./report.txt -p ./src + vendor/bin/phpstan analyse -c phpstan.neon diff --git a/composer.json b/composer.json index 3b9d1eb..c556272 100644 --- a/composer.json +++ b/composer.json @@ -12,13 +12,12 @@ } ], "require": { - "php": ">=8.2", - "psr/log": ">=1.1", - "ext-openssl": "*" + "php": ">=8.0", + "psr/log": ">=1.1" }, "require-dev": { "phpstan/phpstan": "^1.5.3", - "phpstan/phpdoc-parser": "<2" + "squizlabs/php_codesniffer": "^3.5" }, "autoload": { "psr-4": { @@ -27,8 +26,8 @@ }, "extra": { "branch-alias": { - "dev-main": "2.4.x-dev", - "dev-develop": "2.5.x-dev" + "dev-main": "2.1.x-dev", + "dev-develop": "2.2.x-dev" } } } diff --git a/phpcs.xml.dist b/phpcs.xml.dist new file mode 100644 index 0000000..da4bfdb --- /dev/null +++ b/phpcs.xml.dist @@ -0,0 +1,14 @@ + + + + + + + + + + + + ./src + diff --git a/phpstan.neon b/phpstan.neon index 6d40c05..855a75d 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,5 +1,5 @@ parameters: - level: 7 + level: max paths: - src excludePaths: @@ -7,6 +7,5 @@ parameters: - */bower_components/* - */static/* reportUnmatchedIgnoredErrors: false - ignoreErrors: - - identifier: missingType.iterableValue - - identifier: missingType.generics + checkGenericClassInNonGenericObjectType: false + checkMissingIterableValueType: false diff --git a/src/PasswordGenerator.php b/src/PasswordGenerator.php index 605e1a9..ef24af0 100644 --- a/src/PasswordGenerator.php +++ b/src/PasswordGenerator.php @@ -11,18 +11,21 @@ class PasswordGenerator extends RandomGenerator implements PasswordGeneratorInte * one uppercase letter, one digit, and one special character. The remaining characters * in the password are chosen at random from those four sets. * - * The available characters in each set are user-friendly - there are no ambiguous + * The available characters in each set are user friendly - there are no ambiguous * characters such as i, l, 1, o, 0, etc. * + * @param int $length + * @return string + * * @see https://gist.github.com/tylerhall/521810 */ - public function generatePassword(int $length = 16): string + public function generatePassword(int $length = 12) { $sets = []; $sets[] = 'abcdefghjkmnpqrstuvwxyz'; $sets[] = 'ABCDEFGHJKMNPQRSTUVWXYZ'; $sets[] = '23456789'; - $sets[] = '!@#$%&*?-'; + $sets[] = '!@#$%&*?'; $all = ''; $password = ''; @@ -32,10 +35,12 @@ public function generatePassword(int $length = 16): string } $all = \mb_str_split($all); - for ($i = 0; $i < $length - count($sets); ++$i) { + for ($i = 0; $i < $length - count($sets); $i++) { $password .= $all[array_rand($all)]; } - return str_shuffle($password); + $password = str_shuffle($password); + + return $password; } } diff --git a/src/PasswordGeneratorInterface.php b/src/PasswordGeneratorInterface.php index 88bbc53..89ebbd5 100644 --- a/src/PasswordGeneratorInterface.php +++ b/src/PasswordGeneratorInterface.php @@ -6,5 +6,9 @@ interface PasswordGeneratorInterface { - public function generatePassword(int $length = 16): string; + /** + * @param int $length + * @return string + */ + public function generatePassword(int $length = 12); } diff --git a/src/RandomGenerator.php b/src/RandomGenerator.php index 7257851..758ff16 100644 --- a/src/RandomGenerator.php +++ b/src/RandomGenerator.php @@ -8,22 +8,47 @@ class RandomGenerator { - public function __construct(protected readonly LoggerInterface $logger) + protected ?LoggerInterface $logger; + protected bool $useOpenSsl; + + /** + * @param LoggerInterface|null $logger + */ + public function __construct(LoggerInterface $logger = null) { - if (!function_exists('openssl_random_pseudo_bytes')) { - throw new \RuntimeException('You must enable the "openssl" extension for secure random number generation.'); + $this->logger = $logger; + // determine whether to use OpenSSL + if (defined('PHP_WINDOWS_VERSION_BUILD') && version_compare(PHP_VERSION, '5.3.4', '<')) { + $this->useOpenSsl = false; + } elseif (!function_exists('openssl_random_pseudo_bytes')) { + if (null !== $this->logger) { + $this->logger->notice('It is recommended that you enable the "openssl" extension for random number generation.'); + } + $this->useOpenSsl = false; + } else { + $this->useOpenSsl = true; } } - public function getRandomNumber(int $nbBytes = 32): string + /** + * @param int $nbBytes + * @return string + */ + protected function getRandomNumber(int $nbBytes = 32): string { // try OpenSSL - $bytes = \openssl_random_pseudo_bytes($nbBytes, $strong); + if ($this->useOpenSsl) { + $bytes = openssl_random_pseudo_bytes($nbBytes, $strong); + + if (false !== $bytes && true === $strong) { + return $bytes; + } - if (false !== $bytes && true === $strong) { - return $bytes; + if (null !== $this->logger) { + $this->logger->info('OpenSSL did not produce a secure random number.'); + } } - throw new \RuntimeException('Unable to generate a secure random number.'); + return hash('sha256', uniqid((string) mt_rand(), true), true); } } diff --git a/src/SaltGenerator.php b/src/SaltGenerator.php index be00380..55dd79d 100644 --- a/src/SaltGenerator.php +++ b/src/SaltGenerator.php @@ -6,7 +6,10 @@ class SaltGenerator extends RandomGenerator implements SaltGeneratorInterface { - public function generateSalt(): string + /** + * @return string + */ + public function generateSalt() { return strtr(base64_encode($this->getRandomNumber(24)), '{}', '-_'); } diff --git a/src/SaltGeneratorInterface.php b/src/SaltGeneratorInterface.php index b117ef2..52f3b97 100644 --- a/src/SaltGeneratorInterface.php +++ b/src/SaltGeneratorInterface.php @@ -6,5 +6,8 @@ interface SaltGeneratorInterface { - public function generateSalt(): string; + /** + * @return string + */ + public function generateSalt(); } diff --git a/src/TokenGenerator.php b/src/TokenGenerator.php index 8535004..98f51e4 100644 --- a/src/TokenGenerator.php +++ b/src/TokenGenerator.php @@ -6,7 +6,10 @@ class TokenGenerator extends RandomGenerator implements TokenGeneratorInterface { - public function generateToken(): string + /** + * @return string + */ + public function generateToken() { return rtrim(strtr(base64_encode($this->getRandomNumber()), '+/', '-_'), '='); } diff --git a/src/TokenGeneratorInterface.php b/src/TokenGeneratorInterface.php index 6e5e278..6e9c53f 100644 --- a/src/TokenGeneratorInterface.php +++ b/src/TokenGeneratorInterface.php @@ -6,5 +6,8 @@ interface TokenGeneratorInterface { - public function generateToken(): string; + /** + * @return string + */ + public function generateToken(); }