From e37e689e17afa554fcf356f3ab2f49d03be805ab Mon Sep 17 00:00:00 2001 From: mscherer Date: Mon, 4 May 2026 13:44:07 +0200 Subject: [PATCH 1/5] Add PHP and YAML adapters for ACL and Allow config Introduces AbstractAclAdapter and AbstractAllowAdapter holding the section-processing logic shared across all adapters. INI adapters now extend the base classes; new PhpAclAdapter, PhpAllowAdapter, YamlAclAdapter and YamlAllowAdapter only implement the file parsing step. YAML adapters require symfony/yaml, declared as a suggest dependency and used for tests via require-dev. --- composer.json | 6 +- docs/AuthenticationAdapter.md | 21 ++++ docs/AuthorizationAdapter.md | 21 ++++ src/Auth/AclAdapter/AbstractAclAdapter.php | 108 ++++++++++++++++++ src/Auth/AclAdapter/IniAclAdapter.php | 93 +-------------- src/Auth/AclAdapter/PhpAclAdapter.php | 38 ++++++ src/Auth/AclAdapter/YamlAclAdapter.php | 45 ++++++++ .../AllowAdapter/AbstractAllowAdapter.php | 65 +++++++++++ src/Auth/AllowAdapter/IniAllowAdapter.php | 47 +------- src/Auth/AllowAdapter/PhpAllowAdapter.php | 38 ++++++ src/Auth/AllowAdapter/YamlAllowAdapter.php | 45 ++++++++ .../Auth/AclAdapter/PhpAclAdapterTest.php | 59 ++++++++++ .../Auth/AclAdapter/YamlAclAdapterTest.php | 59 ++++++++++ .../Auth/AllowAdapter/PhpAllowAdapterTest.php | 54 +++++++++ .../AllowAdapter/YamlAllowAdapterTest.php | 54 +++++++++ tests/test_files/auth_acl.php | 68 +++++++++++ tests/test_files/auth_acl.yml | 49 ++++++++ tests/test_files/auth_allow.php | 8 ++ tests/test_files/auth_allow.yml | 4 + 19 files changed, 751 insertions(+), 131 deletions(-) create mode 100644 src/Auth/AclAdapter/AbstractAclAdapter.php create mode 100644 src/Auth/AclAdapter/PhpAclAdapter.php create mode 100644 src/Auth/AclAdapter/YamlAclAdapter.php create mode 100644 src/Auth/AllowAdapter/AbstractAllowAdapter.php create mode 100644 src/Auth/AllowAdapter/PhpAllowAdapter.php create mode 100644 src/Auth/AllowAdapter/YamlAllowAdapter.php create mode 100644 tests/TestCase/Auth/AclAdapter/PhpAclAdapterTest.php create mode 100644 tests/TestCase/Auth/AclAdapter/YamlAclAdapterTest.php create mode 100644 tests/TestCase/Auth/AllowAdapter/PhpAllowAdapterTest.php create mode 100644 tests/TestCase/Auth/AllowAdapter/YamlAllowAdapterTest.php create mode 100644 tests/test_files/auth_acl.php create mode 100644 tests/test_files/auth_acl.yml create mode 100644 tests/test_files/auth_allow.php create mode 100644 tests/test_files/auth_allow.yml diff --git a/composer.json b/composer.json index ed489198..df253915 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,11 @@ "cakephp/debug_kit": "^5.0.1", "composer/semver": "^3.0", "fig-r/psr2r-sniffer": "dev-master", - "phpunit/phpunit": "^11.5 || ^12.1 || ^13.0" + "phpunit/phpunit": "^11.5 || ^12.1 || ^13.0", + "symfony/yaml": "^6.4 || ^7.0" + }, + "suggest": { + "symfony/yaml": "Required when using the YAML ACL/Allow adapters (^6.4 || ^7.0)." }, "minimum-stability": "stable", "prefer-stable": true, diff --git a/docs/AuthenticationAdapter.md b/docs/AuthenticationAdapter.md index 3cb47168..ebd070ca 100644 --- a/docs/AuthenticationAdapter.md +++ b/docs/AuthenticationAdapter.md @@ -1,6 +1,27 @@ ### Authentication Adapters For adapters to define allow/deny (public/protected) per controller action. +#### Built-in adapters + +| Adapter | Class | Default file | Notes | +|---------|-------|--------------|-------| +| INI | `TinyAuth\Auth\AllowAdapter\IniAllowAdapter` | `auth_allow.ini` | Default. Zero dependencies. | +| PHP | `TinyAuth\Auth\AllowAdapter\PhpAllowAdapter` | `auth_allow.php` | Returns a plain `return [...]` array. Zero dependencies. | +| YAML | `TinyAuth\Auth\AllowAdapter\YamlAllowAdapter` | `auth_allow.yml` | Requires `symfony/yaml` (`composer require symfony/yaml`). | + +Switch the adapter via the `allowAdapter` configuration key, e.g.: + +```php +'TinyAuth' => [ + 'allowAdapter' => \TinyAuth\Auth\AllowAdapter\PhpAllowAdapter::class, + 'allowFile' => 'auth_allow.php', +], +``` + +The PHP/YAML files use the same section/value shape as the INI variant — top-level keys are `Plugin.Prefix/Controller` identifiers and values are comma-separated action lists. + +#### Custom adapters + Implement the AllowAdapterInterface and make sure your `getAllow()` method returns an array like so: ```php // normal controller diff --git a/docs/AuthorizationAdapter.md b/docs/AuthorizationAdapter.md index a3a8deca..aabc70f0 100644 --- a/docs/AuthorizationAdapter.md +++ b/docs/AuthorizationAdapter.md @@ -1,6 +1,27 @@ ### Authorization Adapters For RBAC ACL adapters. +#### Built-in adapters + +| Adapter | Class | Default file | Notes | +|---------|-------|--------------|-------| +| INI | `TinyAuth\Auth\AclAdapter\IniAclAdapter` | `auth_acl.ini` | Default. Zero dependencies. | +| PHP | `TinyAuth\Auth\AclAdapter\PhpAclAdapter` | `auth_acl.php` | Returns a plain `return [...]` array. Zero dependencies. | +| YAML | `TinyAuth\Auth\AclAdapter\YamlAclAdapter` | `auth_acl.yml` | Requires `symfony/yaml` (`composer require symfony/yaml`). | + +Switch the adapter via the `aclAdapter` configuration key, e.g.: + +```php +'TinyAuth' => [ + 'aclAdapter' => \TinyAuth\Auth\AclAdapter\YamlAclAdapter::class, + 'aclFile' => 'auth_acl.yml', +], +``` + +The PHP/YAML files use the same section/key/value shape as the INI variant — top-level keys are `Plugin.Prefix/Controller` identifiers and each section maps action names (or comma-separated action lists) to comma-separated role lists. + +#### Custom adapters + Implement the AclAdapterInterface and make sure your `getAcl()` method returns an array like so: ```php // normal controller diff --git a/src/Auth/AclAdapter/AbstractAclAdapter.php b/src/Auth/AclAdapter/AbstractAclAdapter.php new file mode 100644 index 00000000..315205eb --- /dev/null +++ b/src/Auth/AclAdapter/AbstractAclAdapter.php @@ -0,0 +1,108 @@ + data array from the underlying config source. + * + * The returned shape matches `parse_ini_file($path, true)` — top-level keys are + * section identifiers (e.g. `Tags`, `Plugin.Admin/Tags`) and each value is a + * `actions => roles` map. + * + * @param array $config Current TinyAuth configuration values. + * @return array> + */ + abstract protected function parseConfig(array $config): array; + + /** + * @param array $availableRoles A list of available user roles. + * @param array $config Current TinyAuth configuration values. + * @return array + */ + public function getAcl(array $availableRoles, array $config): array { + $sections = $this->parseConfig($config); + + $acl = []; + foreach ($sections as $key => $array) { + $acl[$key] = Utility::deconstructIniKey($key); + if (Configure::read('debug')) { + $acl[$key]['map'] = $array; + } + $acl[$key]['deny'] = []; + $acl[$key]['allow'] = []; + + foreach ($array as $actions => $roles) { + $roles = explode(',', $roles); + $actions = explode(',', $actions); + + $deniedRoles = []; + foreach ($roles as $roleId => $role) { + $role = trim($role); + if (!$role) { + continue; + } + $denied = mb_substr($role, 0, 1) === '!'; + if ($denied) { + $role = mb_substr($role, 1); + if (!array_key_exists($role, $availableRoles)) { + unset($roles[$roleId]); + + continue; + } + + unset($roles[$roleId]); + $deniedRoles[] = $role; + + continue; + } + + if (!array_key_exists($role, $availableRoles) && $role !== '*') { + unset($roles[$roleId]); + + continue; + } + if ($role === '*') { + unset($roles[$roleId]); + $roles = array_merge($roles, array_keys($availableRoles)); + } + } + + foreach ($actions as $action) { + $action = trim($action); + if (!$action) { + continue; + } + + foreach ($roles as $role) { + $role = trim($role); + if (!$role) { + continue; + } + $roleName = strtolower($role); + + $newRole = $availableRoles[$roleName]; + $acl[$key]['allow'][$action][$roleName] = $newRole; + } + foreach ($deniedRoles as $role) { + $role = trim($role); + if (!$role) { + continue; + } + $roleName = strtolower($role); + + $newRole = $availableRoles[$roleName]; + $acl[$key]['deny'][$action][$roleName] = $newRole; + } + } + } + } + + return $acl; + } + +} diff --git a/src/Auth/AclAdapter/IniAclAdapter.php b/src/Auth/AclAdapter/IniAclAdapter.php index 3ff7f3d0..0884c377 100644 --- a/src/Auth/AclAdapter/IniAclAdapter.php +++ b/src/Auth/AclAdapter/IniAclAdapter.php @@ -2,99 +2,16 @@ namespace TinyAuth\Auth\AclAdapter; -use Cake\Core\Configure; use TinyAuth\Utility\Utility; -class IniAclAdapter implements AclAdapterInterface { +class IniAclAdapter extends AbstractAclAdapter { /** - * {@inheritDoc} - * - * @return array + * @param array $config Current TinyAuth configuration values. + * @return array> */ - public function getAcl(array $availableRoles, array $config): array { - $iniArray = Utility::parseFiles($config['filePath'], $config['file']); - - $acl = []; - foreach ($iniArray as $key => $array) { - $acl[$key] = Utility::deconstructIniKey($key); - if (Configure::read('debug')) { - $acl[$key]['map'] = $array; - } - $acl[$key]['deny'] = []; - $acl[$key]['allow'] = []; - - foreach ($array as $actions => $roles) { - // Get all roles used in the current INI section - $roles = explode(',', $roles); - $actions = explode(',', $actions); - - $deniedRoles = []; - foreach ($roles as $roleId => $role) { - $role = trim($role); - if (!$role) { - continue; - } - $denied = mb_substr($role, 0, 1) === '!'; - if ($denied) { - $role = mb_substr($role, 1); - if (!array_key_exists($role, $availableRoles)) { - unset($roles[$roleId]); - - continue; - } - - unset($roles[$roleId]); - $deniedRoles[] = $role; - - continue; - } - - // Prevent undefined roles appearing in the iniMap - if (!array_key_exists($role, $availableRoles) && $role !== '*') { - unset($roles[$roleId]); - - continue; - } - if ($role === '*') { - unset($roles[$roleId]); - $roles = array_merge($roles, array_keys($availableRoles)); - } - } - - foreach ($actions as $action) { - $action = trim($action); - if (!$action) { - continue; - } - - foreach ($roles as $role) { - $role = trim($role); - if (!$role) { - continue; - } - $roleName = strtolower($role); - - // Lookup role id by name in roles array - $newRole = $availableRoles[$roleName]; - $acl[$key]['allow'][$action][$roleName] = $newRole; - } - foreach ($deniedRoles as $role) { - $role = trim($role); - if (!$role) { - continue; - } - $roleName = strtolower($role); - - // Lookup role id by name in roles array - $newRole = $availableRoles[$roleName]; - $acl[$key]['deny'][$action][$roleName] = $newRole; - } - } - } - } - - return $acl; + protected function parseConfig(array $config): array { + return Utility::parseFiles($config['filePath'], $config['file']); } } diff --git a/src/Auth/AclAdapter/PhpAclAdapter.php b/src/Auth/AclAdapter/PhpAclAdapter.php new file mode 100644 index 00000000..94e2dea3 --- /dev/null +++ b/src/Auth/AclAdapter/PhpAclAdapter.php @@ -0,0 +1,38 @@ + $config Current TinyAuth configuration values. + * @throws \Cake\Core\Exception\CakeException + * @return array> + */ + protected function parseConfig(array $config): array { + $paths = $config['filePath'] ?? null; + if ($paths === null) { + $paths = ROOT . DS . 'config' . DS; + } + + $list = []; + foreach ((array)$paths as $path) { + $file = $path . $config['file']; + if (!file_exists($file)) { + throw new CakeException(sprintf('Missing TinyAuth config file (%s)', $file)); + } + + $data = include $file; + if (!is_array($data)) { + throw new CakeException(sprintf('Invalid TinyAuth config file (%s)', $file)); + } + + $list += $data; + } + + return $list; + } + +} diff --git a/src/Auth/AclAdapter/YamlAclAdapter.php b/src/Auth/AclAdapter/YamlAclAdapter.php new file mode 100644 index 00000000..c5d463f9 --- /dev/null +++ b/src/Auth/AclAdapter/YamlAclAdapter.php @@ -0,0 +1,45 @@ + $config Current TinyAuth configuration values. + * @throws \Cake\Core\Exception\CakeException + * @return array> + */ + protected function parseConfig(array $config): array { + if (!class_exists(Yaml::class)) { + throw new CakeException( + 'YamlAclAdapter requires symfony/yaml. Install via: composer require symfony/yaml', + ); + } + + $paths = $config['filePath'] ?? null; + if ($paths === null) { + $paths = ROOT . DS . 'config' . DS; + } + + $list = []; + foreach ((array)$paths as $path) { + $file = $path . $config['file']; + if (!file_exists($file)) { + throw new CakeException(sprintf('Missing TinyAuth config file (%s)', $file)); + } + + $data = Yaml::parseFile($file); + if (!is_array($data)) { + throw new CakeException(sprintf('Invalid TinyAuth config file (%s)', $file)); + } + + $list += $data; + } + + return $list; + } + +} diff --git a/src/Auth/AllowAdapter/AbstractAllowAdapter.php b/src/Auth/AllowAdapter/AbstractAllowAdapter.php new file mode 100644 index 00000000..e7ba0087 --- /dev/null +++ b/src/Auth/AllowAdapter/AbstractAllowAdapter.php @@ -0,0 +1,65 @@ + actions array from the underlying config source. + * + * The returned shape matches `parse_ini_file($path, false)` — top-level keys + * are section identifiers (e.g. `Users`, `Admin/Users`) and each value is the + * raw comma-separated action list. + * + * @param array $config Current TinyAuth configuration values. + * @return array + */ + abstract protected function parseConfig(array $config): array; + + /** + * @param array $config Current TinyAuth configuration values. + * @return array + */ + public function getAllow(array $config): array { + $sections = $this->parseConfig($config); + + $auth = []; + foreach ($sections as $key => $actions) { + $auth[$key] = Utility::deconstructIniKey($key); + + $actions = explode(',', $actions); + foreach ($actions as $k => $action) { + $action = trim($action); + if ($action === '') { + unset($actions[$k]); + + continue; + } + $actions[$k] = $action; + } + + if (Configure::read('debug')) { + $auth[$key]['map'] = $actions; + } + $auth[$key]['deny'] = []; + $auth[$key]['allow'] = []; + + foreach ($actions as $action) { + $denied = mb_substr($action, 0, 1) === '!'; + if ($denied) { + $auth[$key]['deny'][] = mb_substr($action, 1); + + continue; + } + + $auth[$key]['allow'][] = $action; + } + } + + return $auth; + } + +} diff --git a/src/Auth/AllowAdapter/IniAllowAdapter.php b/src/Auth/AllowAdapter/IniAllowAdapter.php index 3305df80..1d4c0847 100644 --- a/src/Auth/AllowAdapter/IniAllowAdapter.php +++ b/src/Auth/AllowAdapter/IniAllowAdapter.php @@ -2,53 +2,16 @@ namespace TinyAuth\Auth\AllowAdapter; -use Cake\Core\Configure; use TinyAuth\Utility\Utility; -class IniAllowAdapter implements AllowAdapterInterface { +class IniAllowAdapter extends AbstractAllowAdapter { /** - * {@inheritDoc} - * - * @return array + * @param array $config Current TinyAuth configuration values. + * @return array */ - public function getAllow(array $config): array { - $iniArray = Utility::parseFiles($config['filePath'], $config['file']); - - $auth = []; - foreach ($iniArray as $key => $actions) { - $auth[$key] = Utility::deconstructIniKey($key); - - $actions = explode(',', $actions); - foreach ($actions as $k => $action) { - $action = trim($action); - if ($action === '') { - unset($actions[$k]); - - continue; - } - $actions[$k] = $action; - } - - if (Configure::read('debug')) { - $auth[$key]['map'] = $actions; - } - $auth[$key]['deny'] = []; - $auth[$key]['allow'] = []; - - foreach ($actions as $action) { - $denied = mb_substr($action, 0, 1) === '!'; - if ($denied) { - $auth[$key]['deny'][] = mb_substr($action, 1); - - continue; - } - - $auth[$key]['allow'][] = $action; - } - } - - return $auth; + protected function parseConfig(array $config): array { + return Utility::parseFiles($config['filePath'], $config['file']); } } diff --git a/src/Auth/AllowAdapter/PhpAllowAdapter.php b/src/Auth/AllowAdapter/PhpAllowAdapter.php new file mode 100644 index 00000000..df3ebfce --- /dev/null +++ b/src/Auth/AllowAdapter/PhpAllowAdapter.php @@ -0,0 +1,38 @@ + $config Current TinyAuth configuration values. + * @throws \Cake\Core\Exception\CakeException + * @return array + */ + protected function parseConfig(array $config): array { + $paths = $config['filePath'] ?? null; + if ($paths === null) { + $paths = ROOT . DS . 'config' . DS; + } + + $list = []; + foreach ((array)$paths as $path) { + $file = $path . $config['file']; + if (!file_exists($file)) { + throw new CakeException(sprintf('Missing TinyAuth config file (%s)', $file)); + } + + $data = include $file; + if (!is_array($data)) { + throw new CakeException(sprintf('Invalid TinyAuth config file (%s)', $file)); + } + + $list += $data; + } + + return $list; + } + +} diff --git a/src/Auth/AllowAdapter/YamlAllowAdapter.php b/src/Auth/AllowAdapter/YamlAllowAdapter.php new file mode 100644 index 00000000..b7a64eec --- /dev/null +++ b/src/Auth/AllowAdapter/YamlAllowAdapter.php @@ -0,0 +1,45 @@ + $config Current TinyAuth configuration values. + * @throws \Cake\Core\Exception\CakeException + * @return array + */ + protected function parseConfig(array $config): array { + if (!class_exists(Yaml::class)) { + throw new CakeException( + 'YamlAllowAdapter requires symfony/yaml. Install via: composer require symfony/yaml', + ); + } + + $paths = $config['filePath'] ?? null; + if ($paths === null) { + $paths = ROOT . DS . 'config' . DS; + } + + $list = []; + foreach ((array)$paths as $path) { + $file = $path . $config['file']; + if (!file_exists($file)) { + throw new CakeException(sprintf('Missing TinyAuth config file (%s)', $file)); + } + + $data = Yaml::parseFile($file); + if (!is_array($data)) { + throw new CakeException(sprintf('Invalid TinyAuth config file (%s)', $file)); + } + + $list += $data; + } + + return $list; + } + +} diff --git a/tests/TestCase/Auth/AclAdapter/PhpAclAdapterTest.php b/tests/TestCase/Auth/AclAdapter/PhpAclAdapterTest.php new file mode 100644 index 00000000..cc638a89 --- /dev/null +++ b/tests/TestCase/Auth/AclAdapter/PhpAclAdapterTest.php @@ -0,0 +1,59 @@ +adapter = new PhpAclAdapter(); + } + + /** + * @return void + */ + public function testGetAcl() { + $availableRoles = [ + 'user' => 1, + 'moderator' => 2, + 'admin' => 3, + ]; + $config = [ + 'filePath' => Plugin::path('TinyAuth') . 'tests' . DS . 'test_files' . DS, + 'file' => 'auth_acl.php', + ]; + $result = $this->adapter->getAcl($availableRoles, $config); + + $this->assertCount(15, $result); + } + + /** + * @return void + */ + public function testMissingFileThrows() { + $config = [ + 'filePath' => Plugin::path('TinyAuth') . 'tests' . DS . 'test_files' . DS, + 'file' => 'does_not_exist.php', + ]; + + $this->expectException(CakeException::class); + $this->expectExceptionMessageMatches('/Missing TinyAuth config file/'); + + $this->adapter->getAcl([], $config); + } + +} diff --git a/tests/TestCase/Auth/AclAdapter/YamlAclAdapterTest.php b/tests/TestCase/Auth/AclAdapter/YamlAclAdapterTest.php new file mode 100644 index 00000000..0fbf9942 --- /dev/null +++ b/tests/TestCase/Auth/AclAdapter/YamlAclAdapterTest.php @@ -0,0 +1,59 @@ +adapter = new YamlAclAdapter(); + } + + /** + * @return void + */ + public function testGetAcl() { + $availableRoles = [ + 'user' => 1, + 'moderator' => 2, + 'admin' => 3, + ]; + $config = [ + 'filePath' => Plugin::path('TinyAuth') . 'tests' . DS . 'test_files' . DS, + 'file' => 'auth_acl.yml', + ]; + $result = $this->adapter->getAcl($availableRoles, $config); + + $this->assertCount(15, $result); + } + + /** + * @return void + */ + public function testMissingFileThrows() { + $config = [ + 'filePath' => Plugin::path('TinyAuth') . 'tests' . DS . 'test_files' . DS, + 'file' => 'does_not_exist.yml', + ]; + + $this->expectException(CakeException::class); + $this->expectExceptionMessageMatches('/Missing TinyAuth config file/'); + + $this->adapter->getAcl([], $config); + } + +} diff --git a/tests/TestCase/Auth/AllowAdapter/PhpAllowAdapterTest.php b/tests/TestCase/Auth/AllowAdapter/PhpAllowAdapterTest.php new file mode 100644 index 00000000..2b25f8d5 --- /dev/null +++ b/tests/TestCase/Auth/AllowAdapter/PhpAllowAdapterTest.php @@ -0,0 +1,54 @@ +adapter = new PhpAllowAdapter(); + } + + /** + * @return void + */ + public function testGetAllow() { + $config = [ + 'filePath' => Plugin::path('TinyAuth') . 'tests' . DS . 'test_files' . DS, + 'file' => 'auth_allow.php', + ]; + $result = $this->adapter->getAllow($config); + + $this->assertCount(4, $result); + } + + /** + * @return void + */ + public function testMissingFileThrows() { + $config = [ + 'filePath' => Plugin::path('TinyAuth') . 'tests' . DS . 'test_files' . DS, + 'file' => 'does_not_exist.php', + ]; + + $this->expectException(CakeException::class); + $this->expectExceptionMessageMatches('/Missing TinyAuth config file/'); + + $this->adapter->getAllow($config); + } + +} diff --git a/tests/TestCase/Auth/AllowAdapter/YamlAllowAdapterTest.php b/tests/TestCase/Auth/AllowAdapter/YamlAllowAdapterTest.php new file mode 100644 index 00000000..985ba0c3 --- /dev/null +++ b/tests/TestCase/Auth/AllowAdapter/YamlAllowAdapterTest.php @@ -0,0 +1,54 @@ +adapter = new YamlAllowAdapter(); + } + + /** + * @return void + */ + public function testGetAllow() { + $config = [ + 'filePath' => Plugin::path('TinyAuth') . 'tests' . DS . 'test_files' . DS, + 'file' => 'auth_allow.yml', + ]; + $result = $this->adapter->getAllow($config); + + $this->assertCount(4, $result); + } + + /** + * @return void + */ + public function testMissingFileThrows() { + $config = [ + 'filePath' => Plugin::path('TinyAuth') . 'tests' . DS . 'test_files' . DS, + 'file' => 'does_not_exist.yml', + ]; + + $this->expectException(CakeException::class); + $this->expectExceptionMessageMatches('/Missing TinyAuth config file/'); + + $this->adapter->getAllow($config); + } + +} diff --git a/tests/test_files/auth_acl.php b/tests/test_files/auth_acl.php new file mode 100644 index 00000000..75a49aed --- /dev/null +++ b/tests/test_files/auth_acl.php @@ -0,0 +1,68 @@ + [ + 'index' => 'user, undefined-role', + 'edit' => 'user', + 'delete' => 'admin', + 'very_long_underscored_action' => 'user', + 'veryLongActionNameAction' => 'user', + ], + 'Admin/Tags' => [ + 'index' => 'user, undefined-role', + 'edit' => 'user', + 'delete' => 'admin', + 'very_long_underscored_action' => 'user', + 'veryLongActionNameAction' => 'user', + ], + 'Tags.Tags' => [ + 'index' => 'user', + 'edit,view' => 'user', + 'delete' => 'admin', + 'very_long_underscored_action' => 'user', + 'veryLongActionNameAction' => 'user', + ], + 'Tags.Admin/Tags' => [ + 'index' => 'user', + 'view, edit' => 'user', + 'delete' => 'admin', + 'very_long_underscored_action' => 'user', + 'veryLongActionNameAction' => 'user', + ], + 'Special/Comments' => [ + '*' => 'admin', + ], + 'Comments.Special/Comments' => [ + '*' => 'admin', + ], + 'Posts' => [ + '*' => '*', + ], + 'Admin/Posts' => [ + '*' => '*', + ], + 'Posts.Posts' => [ + '*' => '*', + ], + 'Posts.Admin/Posts' => [ + '*' => '*', + ], + 'Blogs' => [ + '*' => 'user,moderator', + 'foo' => '!user', + ], + 'Admin/Blogs' => [ + '*' => 'moderator', + ], + 'Blogs.Blogs' => [ + '*' => 'moderator', + ], + 'Blogs.Admin/Blogs' => [ + '*' => 'moderator', + ], + 'Admin/MyPrefix/MyTest' => [ + 'myModerator' => 'moderator', + 'myAll' => '*', + 'myDenied' => '!moderator,admin', + ], +]; diff --git a/tests/test_files/auth_acl.yml b/tests/test_files/auth_acl.yml new file mode 100644 index 00000000..8f46862f --- /dev/null +++ b/tests/test_files/auth_acl.yml @@ -0,0 +1,49 @@ +Tags: + index: 'user, undefined-role' + edit: user + delete: admin + very_long_underscored_action: user + veryLongActionNameAction: user +Admin/Tags: + index: 'user, undefined-role' + edit: user + delete: admin + very_long_underscored_action: user + veryLongActionNameAction: user +Tags.Tags: + index: user + 'edit,view': user + delete: admin + very_long_underscored_action: user + veryLongActionNameAction: user +Tags.Admin/Tags: + index: user + 'view, edit': user + delete: admin + very_long_underscored_action: user + veryLongActionNameAction: user +Special/Comments: + '*': admin +Comments.Special/Comments: + '*': admin +Posts: + '*': '*' +Admin/Posts: + '*': '*' +Posts.Posts: + '*': '*' +Posts.Admin/Posts: + '*': '*' +Blogs: + '*': 'user,moderator' + foo: '!user' +Admin/Blogs: + '*': moderator +Blogs.Blogs: + '*': moderator +Blogs.Admin/Blogs: + '*': moderator +Admin/MyPrefix/MyTest: + myModerator: moderator + myAll: '*' + myDenied: '!moderator,admin' diff --git a/tests/test_files/auth_allow.php b/tests/test_files/auth_allow.php new file mode 100644 index 00000000..b2db93c5 --- /dev/null +++ b/tests/test_files/auth_allow.php @@ -0,0 +1,8 @@ + 'index, view', + 'Admin/Users' => 'index', + 'Extras.Offers' => '!superPrivate, *', + 'Admin/MyPrefix/MyTest' => 'myPublic', +]; diff --git a/tests/test_files/auth_allow.yml b/tests/test_files/auth_allow.yml new file mode 100644 index 00000000..f118a741 --- /dev/null +++ b/tests/test_files/auth_allow.yml @@ -0,0 +1,4 @@ +Users: 'index, view' +Admin/Users: index +Extras.Offers: '!superPrivate, *' +Admin/MyPrefix/MyTest: myPublic From 2b8ac38fe9d4c7212b069e859dae784d4b8838e7 Mon Sep 17 00:00:00 2001 From: mscherer Date: Mon, 4 May 2026 13:51:33 +0200 Subject: [PATCH 2/5] Note PHP and YAML built-ins in custom-adapters guide Master's new VitePress docs introduced a custom-adapters page that described tinyauth as having only INI-based stores. Update the intro to point at the now-three built-in formats so readers don't reach for a custom adapter when they just want a different file format. --- docs/guide/custom-adapters.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/custom-adapters.md b/docs/guide/custom-adapters.md index 6496e154..39bda92e 100644 --- a/docs/guide/custom-adapters.md +++ b/docs/guide/custom-adapters.md @@ -1,6 +1,6 @@ # Custom Adapters -TinyAuth's two INI-based stores (`auth_allow.ini` for public actions, `auth_acl.ini` for role permissions) are just the default backends. You can swap either one out for a database-driven, API-driven, or any other source by implementing a small interface. +TinyAuth ships with three file-based backends (`Ini`, `Php`, `Yaml` — see [Authorization Adapters](/authorization/adapter) and [Authentication Adapters](/authentication/adapter)) for the public-action whitelist (`auth_allow.*`) and the role permissions (`auth_acl.*`). If a different format is all you need, just switch the `aclAdapter` / `allowAdapter` config key. You only need a custom adapter when the data has to come from somewhere else entirely — a database, an API, etc. ## When you'd want a custom adapter From 8f3a8ef926ba5348f13cfc58b9534808d962e470 Mon Sep 17 00:00:00 2001 From: mscherer Date: Mon, 4 May 2026 14:12:59 +0200 Subject: [PATCH 3/5] Showcase lenient YAML quoting in test fixtures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Strip defensive quotes from comma-separated role/action lists where YAML's block-scalar rules tolerate them, and drop the unnecessary quotes around comma-bearing keys like 'edit,view' and 'view, edit'. Add header comments explaining when quoting IS required (only when a value/key starts with a YAML reserved character: *, !, &, >, |, etc.) so future readers don't reach for quotes by reflex. Tests still pass — the parsed structure is unchanged, only the on-disk shape is more idiomatic. --- tests/test_files/auth_acl.yml | 57 +++++++++++++++++++++++++++++---- tests/test_files/auth_allow.yml | 7 +++- 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/tests/test_files/auth_acl.yml b/tests/test_files/auth_acl.yml index 8f46862f..549fc314 100644 --- a/tests/test_files/auth_acl.yml +++ b/tests/test_files/auth_acl.yml @@ -1,31 +1,68 @@ +# YAML fixture mirroring auth_acl.ini. +# +# Quoting rule of thumb in this config shape: +# - Plain comma-separated role lists do NOT need quoting: +# index: user, undefined-role # bare scalar, comma is fine +# edit,view: user # comma in a bare key is fine +# view, edit: user # comma + space too +# - Quoting is only required when the value/key STARTS with a YAML +# reserved character — `*` (alias), `!` (tag), `&`, `>`, `|`, etc.: +# '*': '*' # `*` as bare key/value is reserved +# foo: '!user' # leading `!` is the tag indicator +# myDenied: '!moderator, admin' # same — quote the whole value + +# ---------------------------------------------------------- +# TagsController (no prefixed route, no plugin) +# ---------------------------------------------------------- Tags: - index: 'user, undefined-role' + index: user, undefined-role edit: user delete: admin very_long_underscored_action: user veryLongActionNameAction: user + +# ---------------------------------------------------------- +# TagsController (Admin prefixed route, no plugin) +# ---------------------------------------------------------- Admin/Tags: - index: 'user, undefined-role' + index: user, undefined-role edit: user delete: admin very_long_underscored_action: user veryLongActionNameAction: user + +# ---------------------------------------------------------- +# TagsController (plugin Tags, no prefixed route) +# ---------------------------------------------------------- Tags.Tags: index: user - 'edit,view': user + edit,view: user delete: admin very_long_underscored_action: user veryLongActionNameAction: user + +# ---------------------------------------------------------- +# TagsController (plugin Tags, Admin prefixed route) +# ---------------------------------------------------------- Tags.Admin/Tags: index: user - 'view, edit': user + view, edit: user delete: admin very_long_underscored_action: user veryLongActionNameAction: user + +# ---------------------------------------------------------- +# CommentsController, used for testing 'allowLoggedIn' access to +# non-Admin-prefixed routes. +# ---------------------------------------------------------- Special/Comments: '*': admin Comments.Special/Comments: '*': admin + +# ---------------------------------------------------------- +# PostsController, used for testing generic wildcard access +# ---------------------------------------------------------- Posts: '*': '*' Admin/Posts: @@ -34,8 +71,12 @@ Posts.Posts: '*': '*' Posts.Admin/Posts: '*': '*' + +# ---------------------------------------------------------- +# BlogsController, used for testing specific wildcard access +# ---------------------------------------------------------- Blogs: - '*': 'user,moderator' + '*': user, moderator foo: '!user' Admin/Blogs: '*': moderator @@ -43,7 +84,11 @@ Blogs.Blogs: '*': moderator Blogs.Admin/Blogs: '*': moderator + +# ---------------------------------------------------------- +# MyTestController, used for testing nested prefixes +# ---------------------------------------------------------- Admin/MyPrefix/MyTest: myModerator: moderator myAll: '*' - myDenied: '!moderator,admin' + myDenied: '!moderator, admin' diff --git a/tests/test_files/auth_allow.yml b/tests/test_files/auth_allow.yml index f118a741..936c2ef0 100644 --- a/tests/test_files/auth_allow.yml +++ b/tests/test_files/auth_allow.yml @@ -1,4 +1,9 @@ -Users: 'index, view' +# YAML allow fixture. +# See auth_acl.yml for quoting notes — public-action lists are plain +# comma-separated bare strings; only the leading `!` deny-shorthand +# (and `*` wildcards) need quoting. + +Users: index, view Admin/Users: index Extras.Offers: '!superPrivate, *' Admin/MyPrefix/MyTest: myPublic From 310dd69fb1c44f530b4abaa00cfea0f345f3a820 Mon Sep 17 00:00:00 2001 From: mscherer Date: Mon, 4 May 2026 14:16:30 +0200 Subject: [PATCH 4/5] Widen symfony/yaml constraint to allow v8 Composer can pick the highest version compatible with the host PHP: v6.4 (PHP 8.1+), v7.x (PHP 8.2+) or v8.x (PHP 8.4+). Tinyauth's PHP floor is 8.2, so v8 is reachable for users on PHP 8.4+ without breaking the prefer-lowest path on 8.2/8.3. --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index a025efa0..0addc19a 100644 --- a/composer.json +++ b/composer.json @@ -37,10 +37,10 @@ "composer/semver": "^3.0", "fig-r/psr2r-sniffer": "dev-master", "phpunit/phpunit": "^11.5 || ^12.1 || ^13.0", - "symfony/yaml": "^6.4 || ^7.0" + "symfony/yaml": "^6.4 || ^7.0 || ^8.0" }, "suggest": { - "symfony/yaml": "Required when using the YAML ACL/Allow adapters (^6.4 || ^7.0)." + "symfony/yaml": "Required when using the YAML ACL/Allow adapters (^6.4 || ^7.0 || ^8.0)." }, "minimum-stability": "stable", "prefer-stable": true, From 1c687ef47ed85fcec8cc0aae3374d1caa2fef010 Mon Sep 17 00:00:00 2001 From: mscherer Date: Mon, 4 May 2026 14:33:05 +0200 Subject: [PATCH 5/5] Drop YAML adapter, ship PHP-only alongside INI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit YAML treats `*` and `!` as reserved metasyntax (alias reference and tag), but those are exactly the characters TinyAuth's ACL grammar uses most: `*` for "all actions/roles" and `!action` / `!role` for negation. Every realistic YAML auth_acl.yml/auth_allow.yml ends up sprinkled with quoted keys, which is the opposite of "human-friendly" — and a fixture that needs a legend explaining what to quote is the wrong format for this domain. PHP arrays don't have this problem: `*` and `!index` are just strings, no escaping ceremony, IDE-friendly, no extra dependency. INI stays the zero-dependency default. The AbstractAclAdapter / AbstractAllowAdapter base classes from this PR are kept — INI and PHP both inherit from them, and a third-party YAML (or any other format) adapter is now ~30 lines for whoever wants it. --- composer.json | 6 +- docs/authentication/adapter.md | 3 +- docs/authorization/adapter.md | 7 +- docs/guide/custom-adapters.md | 2 +- src/Auth/AclAdapter/YamlAclAdapter.php | 45 --------- src/Auth/AllowAdapter/YamlAllowAdapter.php | 45 --------- .../Auth/AclAdapter/YamlAclAdapterTest.php | 59 ------------ .../AllowAdapter/YamlAllowAdapterTest.php | 54 ----------- tests/test_files/auth_acl.yml | 94 ------------------- tests/test_files/auth_allow.yml | 9 -- 10 files changed, 6 insertions(+), 318 deletions(-) delete mode 100644 src/Auth/AclAdapter/YamlAclAdapter.php delete mode 100644 src/Auth/AllowAdapter/YamlAllowAdapter.php delete mode 100644 tests/TestCase/Auth/AclAdapter/YamlAclAdapterTest.php delete mode 100644 tests/TestCase/Auth/AllowAdapter/YamlAllowAdapterTest.php delete mode 100644 tests/test_files/auth_acl.yml delete mode 100644 tests/test_files/auth_allow.yml diff --git a/composer.json b/composer.json index 0addc19a..d79b3d65 100644 --- a/composer.json +++ b/composer.json @@ -36,11 +36,7 @@ "cakephp/debug_kit": "^5.0.1", "composer/semver": "^3.0", "fig-r/psr2r-sniffer": "dev-master", - "phpunit/phpunit": "^11.5 || ^12.1 || ^13.0", - "symfony/yaml": "^6.4 || ^7.0 || ^8.0" - }, - "suggest": { - "symfony/yaml": "Required when using the YAML ACL/Allow adapters (^6.4 || ^7.0 || ^8.0)." + "phpunit/phpunit": "^11.5 || ^12.1 || ^13.0" }, "minimum-stability": "stable", "prefer-stable": true, diff --git a/docs/authentication/adapter.md b/docs/authentication/adapter.md index ebd070ca..dc2b6960 100644 --- a/docs/authentication/adapter.md +++ b/docs/authentication/adapter.md @@ -7,7 +7,6 @@ For adapters to define allow/deny (public/protected) per controller action. |---------|-------|--------------|-------| | INI | `TinyAuth\Auth\AllowAdapter\IniAllowAdapter` | `auth_allow.ini` | Default. Zero dependencies. | | PHP | `TinyAuth\Auth\AllowAdapter\PhpAllowAdapter` | `auth_allow.php` | Returns a plain `return [...]` array. Zero dependencies. | -| YAML | `TinyAuth\Auth\AllowAdapter\YamlAllowAdapter` | `auth_allow.yml` | Requires `symfony/yaml` (`composer require symfony/yaml`). | Switch the adapter via the `allowAdapter` configuration key, e.g.: @@ -18,7 +17,7 @@ Switch the adapter via the `allowAdapter` configuration key, e.g.: ], ``` -The PHP/YAML files use the same section/value shape as the INI variant — top-level keys are `Plugin.Prefix/Controller` identifiers and values are comma-separated action lists. +The PHP file uses the same section/value shape as the INI variant — top-level keys are `Plugin.Prefix/Controller` identifiers and values are comma-separated action lists. #### Custom adapters diff --git a/docs/authorization/adapter.md b/docs/authorization/adapter.md index aabc70f0..fc5e3f57 100644 --- a/docs/authorization/adapter.md +++ b/docs/authorization/adapter.md @@ -7,18 +7,17 @@ For RBAC ACL adapters. |---------|-------|--------------|-------| | INI | `TinyAuth\Auth\AclAdapter\IniAclAdapter` | `auth_acl.ini` | Default. Zero dependencies. | | PHP | `TinyAuth\Auth\AclAdapter\PhpAclAdapter` | `auth_acl.php` | Returns a plain `return [...]` array. Zero dependencies. | -| YAML | `TinyAuth\Auth\AclAdapter\YamlAclAdapter` | `auth_acl.yml` | Requires `symfony/yaml` (`composer require symfony/yaml`). | Switch the adapter via the `aclAdapter` configuration key, e.g.: ```php 'TinyAuth' => [ - 'aclAdapter' => \TinyAuth\Auth\AclAdapter\YamlAclAdapter::class, - 'aclFile' => 'auth_acl.yml', + 'aclAdapter' => \TinyAuth\Auth\AclAdapter\PhpAclAdapter::class, + 'aclFile' => 'auth_acl.php', ], ``` -The PHP/YAML files use the same section/key/value shape as the INI variant — top-level keys are `Plugin.Prefix/Controller` identifiers and each section maps action names (or comma-separated action lists) to comma-separated role lists. +The PHP file uses the same section/key/value shape as the INI variant — top-level keys are `Plugin.Prefix/Controller` identifiers and each section maps action names (or comma-separated action lists) to comma-separated role lists. #### Custom adapters diff --git a/docs/guide/custom-adapters.md b/docs/guide/custom-adapters.md index 39bda92e..3c39d724 100644 --- a/docs/guide/custom-adapters.md +++ b/docs/guide/custom-adapters.md @@ -1,6 +1,6 @@ # Custom Adapters -TinyAuth ships with three file-based backends (`Ini`, `Php`, `Yaml` — see [Authorization Adapters](/authorization/adapter) and [Authentication Adapters](/authentication/adapter)) for the public-action whitelist (`auth_allow.*`) and the role permissions (`auth_acl.*`). If a different format is all you need, just switch the `aclAdapter` / `allowAdapter` config key. You only need a custom adapter when the data has to come from somewhere else entirely — a database, an API, etc. +TinyAuth ships with two file-based backends (`Ini`, `Php` — see [Authorization Adapters](/authorization/adapter) and [Authentication Adapters](/authentication/adapter)) for the public-action whitelist (`auth_allow.*`) and the role permissions (`auth_acl.*`). If a different format is all you need, just switch the `aclAdapter` / `allowAdapter` config key. You only need a custom adapter when the data has to come from somewhere else entirely — a database, an API, etc. ## When you'd want a custom adapter diff --git a/src/Auth/AclAdapter/YamlAclAdapter.php b/src/Auth/AclAdapter/YamlAclAdapter.php deleted file mode 100644 index c5d463f9..00000000 --- a/src/Auth/AclAdapter/YamlAclAdapter.php +++ /dev/null @@ -1,45 +0,0 @@ - $config Current TinyAuth configuration values. - * @throws \Cake\Core\Exception\CakeException - * @return array> - */ - protected function parseConfig(array $config): array { - if (!class_exists(Yaml::class)) { - throw new CakeException( - 'YamlAclAdapter requires symfony/yaml. Install via: composer require symfony/yaml', - ); - } - - $paths = $config['filePath'] ?? null; - if ($paths === null) { - $paths = ROOT . DS . 'config' . DS; - } - - $list = []; - foreach ((array)$paths as $path) { - $file = $path . $config['file']; - if (!file_exists($file)) { - throw new CakeException(sprintf('Missing TinyAuth config file (%s)', $file)); - } - - $data = Yaml::parseFile($file); - if (!is_array($data)) { - throw new CakeException(sprintf('Invalid TinyAuth config file (%s)', $file)); - } - - $list += $data; - } - - return $list; - } - -} diff --git a/src/Auth/AllowAdapter/YamlAllowAdapter.php b/src/Auth/AllowAdapter/YamlAllowAdapter.php deleted file mode 100644 index b7a64eec..00000000 --- a/src/Auth/AllowAdapter/YamlAllowAdapter.php +++ /dev/null @@ -1,45 +0,0 @@ - $config Current TinyAuth configuration values. - * @throws \Cake\Core\Exception\CakeException - * @return array - */ - protected function parseConfig(array $config): array { - if (!class_exists(Yaml::class)) { - throw new CakeException( - 'YamlAllowAdapter requires symfony/yaml. Install via: composer require symfony/yaml', - ); - } - - $paths = $config['filePath'] ?? null; - if ($paths === null) { - $paths = ROOT . DS . 'config' . DS; - } - - $list = []; - foreach ((array)$paths as $path) { - $file = $path . $config['file']; - if (!file_exists($file)) { - throw new CakeException(sprintf('Missing TinyAuth config file (%s)', $file)); - } - - $data = Yaml::parseFile($file); - if (!is_array($data)) { - throw new CakeException(sprintf('Invalid TinyAuth config file (%s)', $file)); - } - - $list += $data; - } - - return $list; - } - -} diff --git a/tests/TestCase/Auth/AclAdapter/YamlAclAdapterTest.php b/tests/TestCase/Auth/AclAdapter/YamlAclAdapterTest.php deleted file mode 100644 index 0fbf9942..00000000 --- a/tests/TestCase/Auth/AclAdapter/YamlAclAdapterTest.php +++ /dev/null @@ -1,59 +0,0 @@ -adapter = new YamlAclAdapter(); - } - - /** - * @return void - */ - public function testGetAcl() { - $availableRoles = [ - 'user' => 1, - 'moderator' => 2, - 'admin' => 3, - ]; - $config = [ - 'filePath' => Plugin::path('TinyAuth') . 'tests' . DS . 'test_files' . DS, - 'file' => 'auth_acl.yml', - ]; - $result = $this->adapter->getAcl($availableRoles, $config); - - $this->assertCount(15, $result); - } - - /** - * @return void - */ - public function testMissingFileThrows() { - $config = [ - 'filePath' => Plugin::path('TinyAuth') . 'tests' . DS . 'test_files' . DS, - 'file' => 'does_not_exist.yml', - ]; - - $this->expectException(CakeException::class); - $this->expectExceptionMessageMatches('/Missing TinyAuth config file/'); - - $this->adapter->getAcl([], $config); - } - -} diff --git a/tests/TestCase/Auth/AllowAdapter/YamlAllowAdapterTest.php b/tests/TestCase/Auth/AllowAdapter/YamlAllowAdapterTest.php deleted file mode 100644 index 985ba0c3..00000000 --- a/tests/TestCase/Auth/AllowAdapter/YamlAllowAdapterTest.php +++ /dev/null @@ -1,54 +0,0 @@ -adapter = new YamlAllowAdapter(); - } - - /** - * @return void - */ - public function testGetAllow() { - $config = [ - 'filePath' => Plugin::path('TinyAuth') . 'tests' . DS . 'test_files' . DS, - 'file' => 'auth_allow.yml', - ]; - $result = $this->adapter->getAllow($config); - - $this->assertCount(4, $result); - } - - /** - * @return void - */ - public function testMissingFileThrows() { - $config = [ - 'filePath' => Plugin::path('TinyAuth') . 'tests' . DS . 'test_files' . DS, - 'file' => 'does_not_exist.yml', - ]; - - $this->expectException(CakeException::class); - $this->expectExceptionMessageMatches('/Missing TinyAuth config file/'); - - $this->adapter->getAllow($config); - } - -} diff --git a/tests/test_files/auth_acl.yml b/tests/test_files/auth_acl.yml deleted file mode 100644 index 549fc314..00000000 --- a/tests/test_files/auth_acl.yml +++ /dev/null @@ -1,94 +0,0 @@ -# YAML fixture mirroring auth_acl.ini. -# -# Quoting rule of thumb in this config shape: -# - Plain comma-separated role lists do NOT need quoting: -# index: user, undefined-role # bare scalar, comma is fine -# edit,view: user # comma in a bare key is fine -# view, edit: user # comma + space too -# - Quoting is only required when the value/key STARTS with a YAML -# reserved character — `*` (alias), `!` (tag), `&`, `>`, `|`, etc.: -# '*': '*' # `*` as bare key/value is reserved -# foo: '!user' # leading `!` is the tag indicator -# myDenied: '!moderator, admin' # same — quote the whole value - -# ---------------------------------------------------------- -# TagsController (no prefixed route, no plugin) -# ---------------------------------------------------------- -Tags: - index: user, undefined-role - edit: user - delete: admin - very_long_underscored_action: user - veryLongActionNameAction: user - -# ---------------------------------------------------------- -# TagsController (Admin prefixed route, no plugin) -# ---------------------------------------------------------- -Admin/Tags: - index: user, undefined-role - edit: user - delete: admin - very_long_underscored_action: user - veryLongActionNameAction: user - -# ---------------------------------------------------------- -# TagsController (plugin Tags, no prefixed route) -# ---------------------------------------------------------- -Tags.Tags: - index: user - edit,view: user - delete: admin - very_long_underscored_action: user - veryLongActionNameAction: user - -# ---------------------------------------------------------- -# TagsController (plugin Tags, Admin prefixed route) -# ---------------------------------------------------------- -Tags.Admin/Tags: - index: user - view, edit: user - delete: admin - very_long_underscored_action: user - veryLongActionNameAction: user - -# ---------------------------------------------------------- -# CommentsController, used for testing 'allowLoggedIn' access to -# non-Admin-prefixed routes. -# ---------------------------------------------------------- -Special/Comments: - '*': admin -Comments.Special/Comments: - '*': admin - -# ---------------------------------------------------------- -# PostsController, used for testing generic wildcard access -# ---------------------------------------------------------- -Posts: - '*': '*' -Admin/Posts: - '*': '*' -Posts.Posts: - '*': '*' -Posts.Admin/Posts: - '*': '*' - -# ---------------------------------------------------------- -# BlogsController, used for testing specific wildcard access -# ---------------------------------------------------------- -Blogs: - '*': user, moderator - foo: '!user' -Admin/Blogs: - '*': moderator -Blogs.Blogs: - '*': moderator -Blogs.Admin/Blogs: - '*': moderator - -# ---------------------------------------------------------- -# MyTestController, used for testing nested prefixes -# ---------------------------------------------------------- -Admin/MyPrefix/MyTest: - myModerator: moderator - myAll: '*' - myDenied: '!moderator, admin' diff --git a/tests/test_files/auth_allow.yml b/tests/test_files/auth_allow.yml deleted file mode 100644 index 936c2ef0..00000000 --- a/tests/test_files/auth_allow.yml +++ /dev/null @@ -1,9 +0,0 @@ -# YAML allow fixture. -# See auth_acl.yml for quoting notes — public-action lists are plain -# comma-separated bare strings; only the leading `!` deny-shorthand -# (and `*` wildcards) need quoting. - -Users: index, view -Admin/Users: index -Extras.Offers: '!superPrivate, *' -Admin/MyPrefix/MyTest: myPublic