Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:

jobs:
testsuite:
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
Expand All @@ -26,7 +26,7 @@ jobs:
if: matrix.db-type == 'pgsql'
run: docker run --rm --name=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=cakephp -p 5432:5432 -d postgres

- uses: actions/checkout@v2
- uses: actions/checkout@v4

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

- name: Cache composer dependencies
uses: actions/cache@v1
uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ steps.key-date.outputs.date }}-${{ hashFiles('composer.json') }}-${{ matrix.prefer-lowest }}
Expand Down Expand Up @@ -75,14 +75,14 @@ jobs:

- name: Submit code coverage
if: matrix.php-version == '8.1'
uses: codecov/codecov-action@v1
uses: codecov/codecov-action@v5

cs-stan:
name: Coding Standard & Static Analysis
runs-on: ubuntu-22.04

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

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

- name: Cache composer dependencies
uses: actions/cache@v1
uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ steps.key-date.outputs.date }}-${{ hashFiles('composer.json') }}-${{ matrix.prefer-lowest }}
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ tmp
pmip
webroot/coverage
.php_cs.cache
.phpunit.cache/
9 changes: 7 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,15 @@
"firebase/php-jwt": "^6.3"
},
"require-dev": {
"cakephp/cakephp-codesniffer": "~4.4.0",
"cakephp/cakephp-codesniffer": "^4.5",
"google/recaptcha": "@stable",
"league/flysystem-vfs": "^1.0",
"laminas/laminas-diactoros": "^3.0",
"phpunit/phpunit": "^10.0",
"vlucas/phpdotenv": "^3.3"
"phpstan/phpstan": "^1.8",
"robthree/twofactorauth": "^1.6",
"vlucas/phpdotenv": "^3.3",
"web-auth/webauthn-lib": "^5.0"
},
"autoload": {
"psr-4": {
Expand Down
34 changes: 34 additions & 0 deletions config/Migrations/20231003201241_auth_store.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php
/**
* Copyright 2018 - 2020, Cake Development Corporation (https://www.cakedc.com)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2018 - 2020, Cake Development Corporation (https://www.cakedc.com)
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/

use Migrations\AbstractMigration;

class AuthStore extends AbstractMigration
{
public function change()
{
$this->table('auth_store', ['id' => false, 'primary_key' => ['id']])
->addColumn('id', 'string', [
'limit' => 500,
'null' => false,
])
->addColumn('store', 'text', [
'null' => true,
])
->addColumn('created', 'datetime', [
'null' => false,
])
->addColumn('modified', 'datetime', [
'null' => false,
])
->create();
}
}
17 changes: 17 additions & 0 deletions config/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,23 @@
'serviceLookupPlugins' => null,
'lookupMode' => 'underscore',

'2fa' => [
'enabled' => false,
],

'OneTimePasswordAuthenticator' => [
'login' => false,
'checker' => \CakeDC\Api\Service\Auth\TwoFactorAuthentication\DefaultOneTimePasswordAuthenticationChecker::class,
],
'Webauthn2fa' => [
'checker' => \CakeDC\Api\Service\Auth\TwoFactorAuthentication\DefaultWebauthn2fAuthenticationChecker::class,
'localhost' => [
'enabled' => false,
'appName' => 'apilocal',
'id' => 'localhost',
],
],

// auth permission uses require auth strategy
'Auth' => [
'Crud' => [
Expand Down
2 changes: 1 addition & 1 deletion config/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
$routes->plugin('CakeDC/Api', ['path' => '/api'], function ($routes) {
$useVersioning = Configure::read('Api.useVersioning');
$versionPrefix = Configure::read('Api.versionPrefix');
$middlewares = Configure::read('Api.Middleware');
$middlewares = Configure::read('Api.Middleware', []);
$middlewareNames = array_keys($middlewares);

$routes->applyMiddleware(...$middlewareNames);
Expand Down
7 changes: 0 additions & 7 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,6 @@ parameters:
count: 1
path: src\Service\Action\Auth\ResetPasswordAction.php

-
message: "#^Call to an undefined method Cake\\\\ORM\\\\Table\\:\\:validationPasswordConfirm\\(\\)\\.$#"
count: 1
path: src\Service\Action\Auth\ResetPasswordAction.php

-
message: "#^Call to an undefined method Cake\\\\ORM\\\\Table\\:\\:changePassword\\(\\)\\.$#"
count: 1
Expand All @@ -46,5 +41,3 @@ parameters:
message: "#^Call to an undefined method Cake\\\\ORM\\\\Table\\:\\:resetToken\\(\\)\\.$#"
count: 1
path: src\Service\Action\Auth\ValidateAccountRequestAction.php


2 changes: 1 addition & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ includes:
- phpstan-baseline.neon

parameters:
level: 4
level: 2
bootstrapFiles:
- tests/bootstrap.php
ignoreErrors:
Expand Down
153 changes: 153 additions & 0 deletions src/Command/ServiceRoutesCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
<?php
declare(strict_types=1);

/**
* Copyright 2016 - 2024, Cake Development Corporation (http://cakedc.com)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2016 - 2024, Cake Development Corporation (http://cakedc.com)
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/

namespace CakeDC\Api\Command;

use Cake\Command\Command;
use Cake\Console\Arguments;
use Cake\Console\ConsoleIo;
use Cake\Console\ConsoleOptionParser;
use CakeDC\Api\Service\ServiceRegistry;

/**
* Provides interactive CLI tools for CakeDC Api routing.
*/
class ServiceRoutesCommand extends Command
{
/**
* @inheritDoc
*/
public static function defaultName(): string
{
return 'service routes';
}

/**
* Build the option parser.
*
* @param \Cake\Console\ConsoleOptionParser $parser The option parser to update
* @return \Cake\Console\ConsoleOptionParser
*/
protected function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser
{
$parser = parent::buildOptionParser($parser);
$parser->setDescription(__('Display all routes in a service'));
$parser->addArgument('service', [
'help' => __('The name of the service to display routes for.'),
'required' => true,
]);

return $parser;
}

/**
* Display all routes in an application
*
* @param \Cake\Console\Arguments $args The command arguments.
* @param \Cake\Console\ConsoleIo $io The console io
* @return int|null The exit code or null for success
*/
public function execute(Arguments $args, ConsoleIo $io): ?int
{
$serviceName = $args->getArgument('service');
$header = ['Route name', 'Method(s)', 'URI template', 'Service', 'Action', 'Plugin'];
if ($args->getOption('verbose')) {
$header[] = 'Defaults';
}

$service = ServiceRegistry::getServiceLocator()->get($serviceName);
if ($service === null) {
$io->error(__('Service "{0}" not found', $serviceName));

return Command::CODE_ERROR;
}

$availableRoutes = $service->routes();

$output = $duplicateRoutesCounter = [];

foreach ($availableRoutes as $route) {
$methods = isset($route->defaults['_method']) ? (array)$route->defaults['_method'] : [''];

$item = [
$route->options['_name'] ?? $route->getName(),
implode(', ', $methods),
$route->template,
$route->defaults['controller'] ?? '',
$route->defaults['action'] ?? '',
$route->defaults['plugin'] ?? '',
];

if ($args->getOption('verbose')) {
ksort($route->defaults);
$item[] = json_encode($route->defaults);
}

$output[] = $item;

foreach ($methods as $method) {
if (!isset($duplicateRoutesCounter[$route->template][$method])) {
$duplicateRoutesCounter[$route->template][$method] = 0;
}

$duplicateRoutesCounter[$route->template][$method]++;
}
}

if ($args->getOption('sort')) {
usort($output, function ($a, $b) {
return strcasecmp($a[0], $b[0]);
});
}

array_unshift($output, $header);

$io->helper('table')->output($output);
$io->out();

$duplicateRoutes = [];

foreach ($availableRoutes as $route) {
$methods = isset($route->defaults['_method']) ? (array)$route->defaults['_method'] : [''];

foreach ($methods as $method) {
if (
$duplicateRoutesCounter[$route->template][$method] > 1 ||
($method === '' && count($duplicateRoutesCounter[$route->template]) > 1) ||
($method !== '' && isset($duplicateRoutesCounter[$route->template]['']))
) {
$duplicateRoutes[] = [
$route->options['_name'] ?? $route->getName(),
$route->template,
$route->defaults['plugin'] ?? '',
$route->defaults['prefix'] ?? '',
$route->defaults['controller'] ?? '',
$route->defaults['action'] ?? '',
implode(', ', $methods),
];

break;
}
}
}

if ($duplicateRoutes) {
array_unshift($duplicateRoutes, $header);
$io->warning('The following possible route collisions were detected.');
$io->helper('table')->output($duplicateRoutes);
$io->out();
}

return static::CODE_SUCCESS;
}
}
33 changes: 33 additions & 0 deletions src/Model/Entity/AuthStore.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);

namespace CakeDC\Api\Model\Entity;

use Cake\ORM\Entity;

/**
* AuthStore Entity
*
* @property string $id
* @property array|null $store
* @property \Cake\I18n\FrozenTime $created
* @property \Cake\I18n\FrozenTime $modified
*/
class AuthStore extends Entity
{
/**
* Fields that can be mass assigned using newEntity() or patchEntity().
*
* Note that when '*' is set to true, this allows all unspecified fields to
* be mass assigned. For security purposes, it is advised to set '*' to false
* (or remove it), and explicitly make individual fields accessible as needed.
*
* @var array<string, bool>
*/
protected array $_accessible = [
'id' => true,
'store' => true,
'created' => true,
'modified' => true,
];
}
Loading