Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
Spomky committed Apr 14, 2024
2 parents e256e1b + 3380f38 commit 663814e
Show file tree
Hide file tree
Showing 25 changed files with 290 additions and 244 deletions.
4 changes: 2 additions & 2 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ updates:
open-pull-requests-limit: 20
allow:
- dependency-type: all
labels: [ "Dependencies" ]
labels: [ "dependencies" ]

- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
open-pull-requests-limit: 20
labels: [ "Dependencies" ]
labels: [ "dependencies" ]
4 changes: 2 additions & 2 deletions .github/workflows/dependency-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: 'Checkout Repository'
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: 'Dependency Review'
uses: actions/dependency-review-action@v3
uses: actions/dependency-review-action@v4
53 changes: 41 additions & 12 deletions .github/workflows/integrate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
runs-on: "ubuntu-latest"
steps:
- name: "Checkout code"
uses: "actions/checkout@v3"
uses: "actions/checkout@v4"

- name: "Check file permissions"
run: |
Expand All @@ -35,10 +35,10 @@ jobs:
coverage: "none"

- name: "Checkout code"
uses: "actions/checkout@v3"
uses: "actions/checkout@v4"

- name: "Install dependencies"
uses: "ramsey/composer-install@v2"
uses: "ramsey/composer-install@v3"
with:
dependency-versions: "highest"

Expand Down Expand Up @@ -71,10 +71,10 @@ jobs:
coverage: "xdebug"

- name: "Checkout code"
uses: "actions/checkout@v3"
uses: "actions/checkout@v4"

- name: "Install dependencies"
uses: "ramsey/composer-install@v2"
uses: "ramsey/composer-install@v3"
with:
dependency-versions: "${{ matrix.dependencies }}"
composer-options: "--optimize-autoloader"
Expand All @@ -97,13 +97,13 @@ jobs:
coverage: "none"

- name: "Checkout code"
uses: "actions/checkout@v3"
uses: "actions/checkout@v4"

- name: "Validate Composer configuration"
run: "composer validate --strict"

- name: "Install dependencies"
uses: "ramsey/composer-install@v2"
uses: "ramsey/composer-install@v3"
with:
dependency-versions: "highest"
composer-options: "--optimize-autoloader"
Expand All @@ -129,13 +129,13 @@ jobs:
coverage: "none"

- name: "Checkout code"
uses: "actions/checkout@v3"
uses: "actions/checkout@v4"

- name: "Check adherence to EditorConfig"
uses: "greut/eclint-action@v0"

- name: "Install dependencies"
uses: "ramsey/composer-install@v2"
uses: "ramsey/composer-install@v3"
with:
dependency-versions: "highest"
composer-options: "--optimize-autoloader"
Expand All @@ -147,6 +147,35 @@ jobs:
run: |
vendor/bin/deptrac analyse --fail-on-uncovered --no-cache
mutation_testing:
name: "5️⃣ Mutation Testing"
needs:
- "byte_level"
- "syntax_errors"
runs-on: "ubuntu-latest"
steps:
- name: "Set up PHP"
uses: "shivammathur/setup-php@v2"
with:
php-version: "8.1"
extensions: "mbstring"
coverage: "xdebug"

- name: "Checkout code"
uses: "actions/checkout@v4"

- name: "Fetch Git base reference"
run: "git fetch --depth=1 origin ${GITHUB_BASE_REF}"

- name: "Install dependencies"
uses: "ramsey/composer-install@v3"
with:
dependency-versions: "highest"
composer-options: "--optimize-autoloader"

- name: "Execute Infection"
run: "make ci-mu"

rector_checkstyle:
name: "6️⃣ Rector Checkstyle"
needs:
Expand All @@ -162,13 +191,13 @@ jobs:
coverage: "xdebug"

- name: "Checkout code"
uses: "actions/checkout@v3"
uses: "actions/checkout@v4"

- name: "Fetch Git base reference"
run: "git fetch --depth=1 origin ${GITHUB_BASE_REF}"

- name: "Install dependencies"
uses: "ramsey/composer-install@v2"
uses: "ramsey/composer-install@v3"
with:
dependency-versions: "highest"
composer-options: "--optimize-autoloader"
Expand All @@ -184,7 +213,7 @@ jobs:
runs-on: "ubuntu-20.04"
steps:
- name: "Checkout code"
uses: "actions/checkout@v3"
uses: "actions/checkout@v4"

- name: "Check exported files"
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-on-milestone-closed.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:

steps:
- name: "Checkout"
uses: "actions/checkout@v3"
uses: "actions/checkout@v4"

- name: "Release"
uses: "laminas/[email protected]"
Expand Down
7 changes: 3 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@

.PHONY: mu
mu: vendor ## Mutation tests
vendor/bin/infection -s --threads=$$(nproc) --min-msi=30 --min-covered-msi=50
XDEBUG_MODE=coverage vendor/bin/infection -s --threads=$$(nproc) --min-msi=30 --min-covered-msi=50

.PHONY: tests
tests: vendor ## Run all tests
vendor/bin/phpunit --color
yarn test

.PHONY: cc
cc: vendor ## Show test coverage rates (HTML)
Expand Down Expand Up @@ -42,11 +41,11 @@ st: vendor ## Run static analyse

.PHONY: ci-mu
ci-mu: vendor ## Mutation tests (for CI/CD only)
vendor/bin/infection --logger-github -s --threads=$$(nproc) --min-msi=30 --min-covered-msi=50
XDEBUG_MODE=coverage vendor/bin/infection --logger-github -s --threads=$$(nproc) --min-msi=30 --min-covered-msi=50

.PHONY: ci-cc
ci-cc: vendor ## Show test coverage rates (for CI/CD only)
vendor/bin/phpunit --coverage-text
XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-text

.PHONY: ci-cs
ci-cs: vendor ## Check all files using defined ECS rules (for CI/CD only)
Expand Down
6 changes: 4 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
}
],
"require": {
"php": "^8.1",
"php": ">=8.1",
"ext-mbstring": "*",
"paragonie/constant_time_encoding": "^2.0"
"paragonie/constant_time_encoding": "^2.0",
"psr/clock": "^1.0",
"symfony/deprecation-contracts": "^3.2"
},
"require-dev": {
"ekino/phpstan-banned-code": "^1.0",
Expand Down
1 change: 1 addition & 0 deletions deptrac.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ parameters:
- name: 'Vendors'
collectors:
- { type: className, regex: '^ParagonIE\\' }
- { type: className, regex: '^Psr\\Clock\\' }
ruleset:
OTP:
- 'Vendors'
16 changes: 16 additions & 0 deletions doc/UPGRADE_v10-v11.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,19 @@

Congratulation, you have nothing to do!
This version requires PHP8.1+, but no changes on your side are expected.

However, please note the change in behavior of the `window` feature between versions 10 and 11.

With version 10
---------------

The `window` of timestamps goes from `timestamp - window * period` to `timestamp + window * period`.
For example, if the window is `5`, the period `30` and the timestamp `1476822000`, the OTP tested are within `1476821850` (`1476822000 - 5 * 30`) and `1476822150` (`1476822000 + 5 * 30`).
In other words, this validated the **5 OTP before and after** the current timestamp.

With version 11
---------------
The window of TOTP acts as time drift.
If the window is `10`, the period `30` and the timestamp `147682209`, the OTP tested are within `1476821999` (`147682209 - 10`), `147682209` and `1476822219` (`147682209 + 10`).
This includes the previous OTP, but not the next one.
The `window` shall be lower than the `period`. In the previous example, the `window` shall be between `0` and `30`.
2 changes: 1 addition & 1 deletion doc/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ echo "The current OTP is: {$otp->now()}\n";

In the example above, we use the `TOTP` class, but you can use the `HOTP` one the same way.

Then, you have to configure you applications.
Then, you have to configure your applications.
You can use the provisioning Uri (`$otp->getProvisioningUri();`) as QR Code input to easily configure all of them.

We recommend you to use your own QR Code generator (e.g. [BaconQrCode](https://packagist.org/packages/bacon/bacon-qr-code) or [endroid/qr-code](https://github.com/endroid/qr-code)).
Expand Down
49 changes: 7 additions & 42 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@ parameters:
count: 1
path: src/HOTP.php

-
message: "#^Comparison operation \"\\>\\=\" between int\\<0, max\\>\\|null and 0 is always true\\.$#"
count: 1
path: src/HOTP.php

-
message: "#^Method OTPHP\\\\OTP\\:\\:generateSecret\\(\\) should return non\\-empty\\-string but returns string\\.$#"
count: 1
Expand All @@ -20,31 +15,6 @@ parameters:
count: 1
path: src/OTP.php

-
message: "#^Comparison operation \"\\>\\=\" between int\\<0, max\\> and 0 is always true\\.$#"
count: 1
path: src/TOTP.php

-
message: "#^Method OTPHP\\\\TOTP\\:\\:expiresIn\\(\\) should return int\\<0, max\\> but returns int\\.$#"
count: 1
path: src/TOTP.php

-
message: "#^Parameter \\#1 \\$epoch of method OTPHP\\\\TOTP\\:\\:setEpoch\\(\\) expects int\\<0, max\\>, int given\\.$#"
count: 1
path: src/TOTP.php

-
message: "#^Parameter \\#1 \\$input of method OTPHP\\\\TOTP\\:\\:at\\(\\) expects int\\<0, max\\>, int given\\.$#"
count: 1
path: src/TOTP.php

-
message: "#^Left side of \\|\\| is always true\\.$#"
count: 1
path: src/Url.php

-
message: "#^Parameter \\#2 \\$host of class OTPHP\\\\Url constructor expects non\\-empty\\-string, string given\\.$#"
count: 1
Expand All @@ -66,21 +36,16 @@ parameters:
path: src/Url.php

-
message: "#^Parameter \\#1 \\$counter of method OTPHP\\\\HOTP\\:\\:setCounter\\(\\) expects int\\<0, max\\>, \\-500 given\\.$#"
count: 1
path: tests/HOTPTest.php

-
message: "#^Parameter \\#1 \\$digits of method OTPHP\\\\OTP\\:\\:setDigits\\(\\) expects int\\<1, max\\>, 0 given\\.$#"
count: 1
path: tests/HOTPTest.php
message: "#^Parameter \\#1 \\$dateTime of method OTPHP\\\\Test\\\\ClockMock\\:\\:setDateTime\\(\\) expects DateTimeImmutable\\|null, DateTimeImmutable\\|false given\\.$#"
count: 5
path: tests/TOTPTest.php

-
message: "#^Parameter \\#1 \\$epoch of method OTPHP\\\\TOTP\\:\\:setEpoch\\(\\) expects int\\<0, max\\>, \\-1 given\\.$#"
count: 1
message: "#^Parameter \\#1 \\$otp of method OTPHP\\\\TOTP\\:\\:verify\\(\\) expects non\\-empty\\-string, string given\\.$#"
count: 2
path: tests/TOTPTest.php

-
message: "#^Parameter \\#1 \\$period of method OTPHP\\\\TOTP\\:\\:setPeriod\\(\\) expects int\\<1, max\\>, \\-20 given\\.$#"
count: 1
message: "#^Parameter \\#3 \\$leeway of method OTPHP\\\\TOTP\\:\\:verify\\(\\) expects int\\<0, max\\>\\|null, int given\\.$#"
count: 2
path: tests/TOTPTest.php
1 change: 1 addition & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ parameters:
- src
- tests

treatPhpDocTypesAsCertain: false
includes:
- vendor/phpstan/phpstan/conf/bleedingEdge.neon
- vendor/phpstan/phpstan-strict-rules/rules.neon
Expand Down
3 changes: 2 additions & 1 deletion rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

return static function (RectorConfig $config): void {
$config->import(SetList::DEAD_CODE);
$config->import(LevelSetList::UP_TO_PHP_80);
$config->import(LevelSetList::UP_TO_PHP_81);
$config->import(SymfonySetList::SYMFONY_CODE_QUALITY);
$config->import(PHPUnitSetList::PHPUNIT_100);
$config->import(PHPUnitSetList::ANNOTATIONS_TO_ATTRIBUTES);
Expand All @@ -21,6 +21,7 @@
$config->paths([__DIR__ . '/src', __DIR__ . '/tests']);
$config->skip([PreferPHPUnitThisCallRector::class]);
$config->phpVersion(PhpVersion::PHP_81);
$config->parallel();
$config->importNames();
$config->importShortClasses();
};
17 changes: 13 additions & 4 deletions src/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace OTPHP;

use InvalidArgumentException;
use Psr\Clock\ClockInterface;
use Throwable;
use function assert;
use function count;
Expand All @@ -16,16 +17,24 @@
*/
final class Factory implements FactoryInterface
{
public static function loadFromProvisioningUri(string $uri): OTPInterface
public static function loadFromProvisioningUri(string $uri, ?ClockInterface $clock = null): OTPInterface
{
try {
$parsed_url = Url::fromString($uri);
$parsed_url->getScheme() === 'otpauth' || throw new InvalidArgumentException('Invalid scheme.');
} catch (Throwable $throwable) {
throw new InvalidArgumentException('Not a valid OTP provisioning URI', $throwable->getCode(), $throwable);
}
if ($clock === null) {
trigger_deprecation(
'spomky-labs/otphp',
'11.3.0',
'The parameter "$clock" will become mandatory in 12.0.0. Please set a valid PSR Clock implementation instead of "null".'
);
$clock = new InternalClock();
}

$otp = self::createOTP($parsed_url);
$otp = self::createOTP($parsed_url, $clock);

self::populateOTP($otp, $parsed_url);

Expand Down Expand Up @@ -62,11 +71,11 @@ private static function populateOTP(OTPInterface $otp, Url $data): void
$otp->setIssuer($result[0]);
}

private static function createOTP(Url $parsed_url): OTPInterface
private static function createOTP(Url $parsed_url, ClockInterface $clock): OTPInterface
{
switch ($parsed_url->getHost()) {
case 'totp':
$totp = TOTP::createFromSecret($parsed_url->getSecret());
$totp = TOTP::createFromSecret($parsed_url->getSecret(), $clock);
$totp->setLabel(self::getLabel($parsed_url->getPath()));

return $totp;
Expand Down
Loading

0 comments on commit 663814e

Please sign in to comment.