Skip to content

Commit e7012e2

Browse files
authored
Merge pull request #118 from chav170/feature/port-2fa-features
Feature/port 2fa features
2 parents 98f5ee3 + 9de7bd6 commit e7012e2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+2274
-56
lines changed

.github/workflows/ci.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ on:
99

1010
jobs:
1111
testsuite:
12-
runs-on: ubuntu-22.04
12+
runs-on: ubuntu-24.04
1313
strategy:
1414
fail-fast: false
1515
matrix:
@@ -26,7 +26,7 @@ jobs:
2626
if: matrix.db-type == 'pgsql'
2727
run: docker run --rm --name=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=cakephp -p 5432:5432 -d postgres
2828

29-
- uses: actions/checkout@v2
29+
- uses: actions/checkout@v4
3030

3131
- name: Setup PHP
3232
uses: shivammathur/setup-php@v2
@@ -45,7 +45,7 @@ jobs:
4545
run: echo "::set-output name=date::$(date +'%Y-%m')"
4646

4747
- name: Cache composer dependencies
48-
uses: actions/cache@v1
48+
uses: actions/cache@v4
4949
with:
5050
path: ${{ steps.composer-cache.outputs.dir }}
5151
key: ${{ runner.os }}-composer-${{ steps.key-date.outputs.date }}-${{ hashFiles('composer.json') }}-${{ matrix.prefer-lowest }}
@@ -75,14 +75,14 @@ jobs:
7575
7676
- name: Submit code coverage
7777
if: matrix.php-version == '8.1'
78-
uses: codecov/codecov-action@v1
78+
uses: codecov/codecov-action@v5
7979

8080
cs-stan:
8181
name: Coding Standard & Static Analysis
8282
runs-on: ubuntu-22.04
8383

8484
steps:
85-
- uses: actions/checkout@v2
85+
- uses: actions/checkout@v4
8686

8787
- name: Setup PHP
8888
uses: shivammathur/setup-php@v2
@@ -100,7 +100,7 @@ jobs:
100100
run: echo "::set-output name=date::$(date +'%Y-%m')"
101101

102102
- name: Cache composer dependencies
103-
uses: actions/cache@v1
103+
uses: actions/cache@v4
104104
with:
105105
path: ${{ steps.composer-cache.outputs.dir }}
106106
key: ${{ runner.os }}-composer-${{ steps.key-date.outputs.date }}-${{ hashFiles('composer.json') }}-${{ matrix.prefer-lowest }}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ tmp
99
pmip
1010
webroot/coverage
1111
.php_cs.cache
12+
.phpunit.cache/

composer.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,15 @@
3535
"firebase/php-jwt": "^6.3"
3636
},
3737
"require-dev": {
38-
"cakephp/cakephp-codesniffer": "~4.4.0",
38+
"cakephp/cakephp-codesniffer": "^4.5",
39+
"google/recaptcha": "@stable",
3940
"league/flysystem-vfs": "^1.0",
41+
"laminas/laminas-diactoros": "^3.0",
4042
"phpunit/phpunit": "^10.0",
41-
"vlucas/phpdotenv": "^3.3"
43+
"phpstan/phpstan": "^1.8",
44+
"robthree/twofactorauth": "^1.6",
45+
"vlucas/phpdotenv": "^3.3",
46+
"web-auth/webauthn-lib": "^5.0"
4247
},
4348
"autoload": {
4449
"psr-4": {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
/**
3+
* Copyright 2018 - 2020, Cake Development Corporation (https://www.cakedc.com)
4+
*
5+
* Licensed under The MIT License
6+
* Redistributions of files must retain the above copyright notice.
7+
*
8+
* @copyright Copyright 2018 - 2020, Cake Development Corporation (https://www.cakedc.com)
9+
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
10+
*/
11+
12+
use Migrations\AbstractMigration;
13+
14+
class AuthStore extends AbstractMigration
15+
{
16+
public function change()
17+
{
18+
$this->table('auth_store', ['id' => false, 'primary_key' => ['id']])
19+
->addColumn('id', 'string', [
20+
'limit' => 500,
21+
'null' => false,
22+
])
23+
->addColumn('store', 'text', [
24+
'null' => true,
25+
])
26+
->addColumn('created', 'datetime', [
27+
'null' => false,
28+
])
29+
->addColumn('modified', 'datetime', [
30+
'null' => false,
31+
])
32+
->create();
33+
}
34+
}

config/api.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,23 @@
3535
'serviceLookupPlugins' => null,
3636
'lookupMode' => 'underscore',
3737

38+
'2fa' => [
39+
'enabled' => false,
40+
],
41+
42+
'OneTimePasswordAuthenticator' => [
43+
'login' => false,
44+
'checker' => \CakeDC\Api\Service\Auth\TwoFactorAuthentication\DefaultOneTimePasswordAuthenticationChecker::class,
45+
],
46+
'Webauthn2fa' => [
47+
'checker' => \CakeDC\Api\Service\Auth\TwoFactorAuthentication\DefaultWebauthn2fAuthenticationChecker::class,
48+
'localhost' => [
49+
'enabled' => false,
50+
'appName' => 'apilocal',
51+
'id' => 'localhost',
52+
],
53+
],
54+
3855
// auth permission uses require auth strategy
3956
'Auth' => [
4057
'Crud' => [

config/routes.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
$routes->plugin('CakeDC/Api', ['path' => '/api'], function ($routes) {
2020
$useVersioning = Configure::read('Api.useVersioning');
2121
$versionPrefix = Configure::read('Api.versionPrefix');
22-
$middlewares = Configure::read('Api.Middleware');
22+
$middlewares = Configure::read('Api.Middleware', []);
2323
$middlewareNames = array_keys($middlewares);
2424

2525
$routes->applyMiddleware(...$middlewareNames);

phpstan-baseline.neon

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,6 @@ parameters:
1717
count: 1
1818
path: src\Service\Action\Auth\ResetPasswordAction.php
1919

20-
-
21-
message: "#^Call to an undefined method Cake\\\\ORM\\\\Table\\:\\:validationPasswordConfirm\\(\\)\\.$#"
22-
count: 1
23-
path: src\Service\Action\Auth\ResetPasswordAction.php
24-
2520
-
2621
message: "#^Call to an undefined method Cake\\\\ORM\\\\Table\\:\\:changePassword\\(\\)\\.$#"
2722
count: 1
@@ -46,5 +41,3 @@ parameters:
4641
message: "#^Call to an undefined method Cake\\\\ORM\\\\Table\\:\\:resetToken\\(\\)\\.$#"
4742
count: 1
4843
path: src\Service\Action\Auth\ValidateAccountRequestAction.php
49-
50-

phpstan.neon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ includes:
22
- phpstan-baseline.neon
33

44
parameters:
5-
level: 4
5+
level: 2
66
bootstrapFiles:
77
- tests/bootstrap.php
88
ignoreErrors:
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
/**
5+
* Copyright 2016 - 2024, Cake Development Corporation (http://cakedc.com)
6+
*
7+
* Licensed under The MIT License
8+
* Redistributions of files must retain the above copyright notice.
9+
*
10+
* @copyright Copyright 2016 - 2024, Cake Development Corporation (http://cakedc.com)
11+
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
12+
*/
13+
14+
namespace CakeDC\Api\Command;
15+
16+
use Cake\Command\Command;
17+
use Cake\Console\Arguments;
18+
use Cake\Console\ConsoleIo;
19+
use Cake\Console\ConsoleOptionParser;
20+
use CakeDC\Api\Service\ServiceRegistry;
21+
22+
/**
23+
* Provides interactive CLI tools for CakeDC Api routing.
24+
*/
25+
class ServiceRoutesCommand extends Command
26+
{
27+
/**
28+
* @inheritDoc
29+
*/
30+
public static function defaultName(): string
31+
{
32+
return 'service routes';
33+
}
34+
35+
/**
36+
* Build the option parser.
37+
*
38+
* @param \Cake\Console\ConsoleOptionParser $parser The option parser to update
39+
* @return \Cake\Console\ConsoleOptionParser
40+
*/
41+
protected function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser
42+
{
43+
$parser = parent::buildOptionParser($parser);
44+
$parser->setDescription(__('Display all routes in a service'));
45+
$parser->addArgument('service', [
46+
'help' => __('The name of the service to display routes for.'),
47+
'required' => true,
48+
]);
49+
50+
return $parser;
51+
}
52+
53+
/**
54+
* Display all routes in an application
55+
*
56+
* @param \Cake\Console\Arguments $args The command arguments.
57+
* @param \Cake\Console\ConsoleIo $io The console io
58+
* @return int|null The exit code or null for success
59+
*/
60+
public function execute(Arguments $args, ConsoleIo $io): ?int
61+
{
62+
$serviceName = $args->getArgument('service');
63+
$header = ['Route name', 'Method(s)', 'URI template', 'Service', 'Action', 'Plugin'];
64+
if ($args->getOption('verbose')) {
65+
$header[] = 'Defaults';
66+
}
67+
68+
$service = ServiceRegistry::getServiceLocator()->get($serviceName);
69+
if ($service === null) {
70+
$io->error(__('Service "{0}" not found', $serviceName));
71+
72+
return Command::CODE_ERROR;
73+
}
74+
75+
$availableRoutes = $service->routes();
76+
77+
$output = $duplicateRoutesCounter = [];
78+
79+
foreach ($availableRoutes as $route) {
80+
$methods = isset($route->defaults['_method']) ? (array)$route->defaults['_method'] : [''];
81+
82+
$item = [
83+
$route->options['_name'] ?? $route->getName(),
84+
implode(', ', $methods),
85+
$route->template,
86+
$route->defaults['controller'] ?? '',
87+
$route->defaults['action'] ?? '',
88+
$route->defaults['plugin'] ?? '',
89+
];
90+
91+
if ($args->getOption('verbose')) {
92+
ksort($route->defaults);
93+
$item[] = json_encode($route->defaults);
94+
}
95+
96+
$output[] = $item;
97+
98+
foreach ($methods as $method) {
99+
if (!isset($duplicateRoutesCounter[$route->template][$method])) {
100+
$duplicateRoutesCounter[$route->template][$method] = 0;
101+
}
102+
103+
$duplicateRoutesCounter[$route->template][$method]++;
104+
}
105+
}
106+
107+
if ($args->getOption('sort')) {
108+
usort($output, function ($a, $b) {
109+
return strcasecmp($a[0], $b[0]);
110+
});
111+
}
112+
113+
array_unshift($output, $header);
114+
115+
$io->helper('table')->output($output);
116+
$io->out();
117+
118+
$duplicateRoutes = [];
119+
120+
foreach ($availableRoutes as $route) {
121+
$methods = isset($route->defaults['_method']) ? (array)$route->defaults['_method'] : [''];
122+
123+
foreach ($methods as $method) {
124+
if (
125+
$duplicateRoutesCounter[$route->template][$method] > 1 ||
126+
($method === '' && count($duplicateRoutesCounter[$route->template]) > 1) ||
127+
($method !== '' && isset($duplicateRoutesCounter[$route->template]['']))
128+
) {
129+
$duplicateRoutes[] = [
130+
$route->options['_name'] ?? $route->getName(),
131+
$route->template,
132+
$route->defaults['plugin'] ?? '',
133+
$route->defaults['prefix'] ?? '',
134+
$route->defaults['controller'] ?? '',
135+
$route->defaults['action'] ?? '',
136+
implode(', ', $methods),
137+
];
138+
139+
break;
140+
}
141+
}
142+
}
143+
144+
if ($duplicateRoutes) {
145+
array_unshift($duplicateRoutes, $header);
146+
$io->warning('The following possible route collisions were detected.');
147+
$io->helper('table')->output($duplicateRoutes);
148+
$io->out();
149+
}
150+
151+
return static::CODE_SUCCESS;
152+
}
153+
}

src/Model/Entity/AuthStore.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace CakeDC\Api\Model\Entity;
5+
6+
use Cake\ORM\Entity;
7+
8+
/**
9+
* AuthStore Entity
10+
*
11+
* @property string $id
12+
* @property array|null $store
13+
* @property \Cake\I18n\FrozenTime $created
14+
* @property \Cake\I18n\FrozenTime $modified
15+
*/
16+
class AuthStore extends Entity
17+
{
18+
/**
19+
* Fields that can be mass assigned using newEntity() or patchEntity().
20+
*
21+
* Note that when '*' is set to true, this allows all unspecified fields to
22+
* be mass assigned. For security purposes, it is advised to set '*' to false
23+
* (or remove it), and explicitly make individual fields accessible as needed.
24+
*
25+
* @var array<string, bool>
26+
*/
27+
protected array $_accessible = [
28+
'id' => true,
29+
'store' => true,
30+
'created' => true,
31+
'modified' => true,
32+
];
33+
}

0 commit comments

Comments
 (0)