From cbc3a961b294faee046135586f8f67479262cf75 Mon Sep 17 00:00:00 2001 From: Mark Scherer Date: Fri, 26 Aug 2016 22:24:13 +0200 Subject: [PATCH 01/15] Allow TinyAuth as component to hook into authentication globally via authentication.ini --- src/Controller/Component/AuthComponent.php | 183 ++++++++++++++++++ .../Component/AuthComponentTest.php | 102 ++++++++++ tests/config/routes.php | 9 + tests/test_files/authentication.ini | 5 + 4 files changed, 299 insertions(+) create mode 100644 src/Controller/Component/AuthComponent.php create mode 100644 tests/TestCase/Controller/Component/AuthComponentTest.php create mode 100644 tests/config/routes.php create mode 100644 tests/test_files/authentication.ini diff --git a/src/Controller/Component/AuthComponent.php b/src/Controller/Component/AuthComponent.php new file mode 100644 index 00000000..6d03b266 --- /dev/null +++ b/src/Controller/Component/AuthComponent.php @@ -0,0 +1,183 @@ + '_cake_core_', + 'cacheKey' => 'tiny_auth_authentication', + 'autoClearCache' => false, // usually done by Cache automatically in debug mode, + 'authPath' => null, // possible to locate authentication.ini at given path e.g. Plugin::configPath('Admin') + 'authFile' => 'authentication.ini' + ]; + + /** + * @param \Cake\Controller\ComponentRegistry $registry + * @param array $config + * @throws \Cake\Core\Exception\Exception + */ + public function __construct(ComponentRegistry $registry, array $config = []) { + $config += (array)Configure::read('TinyAuth'); + $config += $this->_defaultTinyAuthConfig; + + parent::__construct($registry, $config); + + if (!in_array($config['cache'], Cache::configured())) { + throw new Exception(sprintf('Invalid TinyAuthorization cache `%s`', $config['cache'])); + } + } + + /** + * @param \Cake\Event\Event $event Event instance. + * @return \Cake\Network\Response|null + */ + public function startup(Event $event) { + $this->_prepareAuthentication(); + + return parent::startup($event); + } + + /** + * @return void + */ + protected function _prepareAuthentication() { + $authentication = $this->_getAuth($this->_config['authPath']); + + $params = $this->request->params; + foreach ($authentication as $array) { + if ($params['plugin'] && $params['plugin'] !== $array['plugin']) { + continue; + } + if (!empty($params['prefix']) && $params['prefix'] !== $array['prefix']) { + continue; + } + if ($params['controller'] !== $array['controller']) { + continue; + } + + if ($array['actions'] === []) { + $this->allow(); + return; + } + + $this->allow($array['actions']); + /* + if (in_array($params['action'], $array['actions'])) { + $this->allow($params['action']); + return; + } + */ + } + } + + /** + * Parse ini file and returns the allowed actions. + * + * Uses cache for maximum performance. + * + * @param string|null $path + * @return array Actions + */ + protected function _getAuth($path = null) { + if ($path === null) { + $path = ROOT . DS . 'config' . DS; + } + + if ($this->_config['autoClearCache'] && Configure::read('debug')) { + Cache::delete($this->_config['cacheKey'], $this->_config['cache']); + } + $roles = Cache::read($this->_config['cacheKey'], $this->_config['cache']); + if ($roles !== false) { + return $roles; + } + + $iniArray = $this->_parseFile($path . $this->_config['authFile']); + + $res = []; + foreach ($iniArray as $key => $actions) { + $res[$key] = $this->_deconstructIniKey($key); + $res[$key]['map'] = $actions; + + $actions = explode(',', $actions); + + if (in_array('*', $actions)) { + //$this->allow(); + $res[$key]['actions'] = []; + continue; + } + + foreach ($actions as $action) { + $action = trim($action); + if (!$action) { + continue; + } + + $res[$key]['actions'][] = $action; + } + } + + Cache::write($this->_config['cacheKey'], $res, $this->_config['cache']); + return $res; + } + + /** + * Returns the authentication.ini file as an array. + * + * @param string $ini Full path to the authentication.ini file + * @return array List with all available roles + * @throws \Cake\Core\Exception\Exception + */ + protected function _parseFile($ini) { + if (!file_exists($ini)) { + throw new Exception(sprintf('Missing TinyAuthorize authentication file (%s)', $ini)); + } + + if (function_exists('parse_ini_file')) { + $iniArray = parse_ini_file($ini, true); + } else { + $iniArray = parse_ini_string(file_get_contents($ini), true); + } + + if (!is_array($iniArray)) { + throw new Exception('Invalid TinyAuthorize authentication file'); + } + return $iniArray; + } + + /** + * Deconstructs an authentication ini section key into a named array with authentication parts. + * + * @param string $key INI section key as found in authentication.ini + * @return array Array with named keys for controller, plugin and prefix + */ + protected function _deconstructIniKey($key) { + $res = [ + 'plugin' => null, + 'prefix' => null + ]; + + if (strpos($key, '.') !== false) { + list($res['plugin'], $key) = explode('.', $key); + } + if (strpos($key, '/') !== false) { + list($res['prefix'], $key) = explode('/', $key); + } + $res['controller'] = $key; + return $res; + } + +} diff --git a/tests/TestCase/Controller/Component/AuthComponentTest.php b/tests/TestCase/Controller/Component/AuthComponentTest.php new file mode 100644 index 00000000..b67eed45 --- /dev/null +++ b/tests/TestCase/Controller/Component/AuthComponentTest.php @@ -0,0 +1,102 @@ +componentConfig = [ + 'authPath' => Plugin::path('TinyAuth') . 'tests' . DS . 'test_files' . DS + ]; + } + + /** + * @return void + */ + public function testValid() { + $request = new Request(['params' => [ + 'controller' => 'Users', + 'action' => 'view', + 'plugin' => null, + '_ext' => null, + 'pass' => [1] + ]]); + $controller = $this->getControllerMock($request); + + $registry = new ComponentRegistry($controller); + $this->AuthComponent = new AuthComponent($registry, $this->componentConfig); + + $config = []; + $this->AuthComponent->initialize($config); + + $event = new Event('Controller.startup', $controller); + $response = $this->AuthComponent->startup($event); + $this->assertNull($response); + } + + /** + * @return void + */ + public function testInvalid() { + $request = new Request(['params' => [ + 'controller' => 'FooBar', + 'action' => 'index', + 'plugin' => null, + '_ext' => null, + 'pass' => [] + ]]); + $controller = $this->getControllerMock($request); + + $registry = new ComponentRegistry($controller); + $this->AuthComponent = new AuthComponent($registry, $this->componentConfig); + + $config = []; + $this->AuthComponent->initialize($config); + + $event = new Event('Controller.startup', $controller); + $response = $this->AuthComponent->startup($event); + + $this->assertInstanceOf(Response::class, $response); + $this->assertSame(302, $response->statusCode()); + } + + /** + * @param \Cake\Network\Request $request + * @return Controller + */ + protected function getControllerMock(Request $request) { + $controller = $this->getMockBuilder(Controller::class) + ->setConstructorArgs([$request]) + ->setMethods(['isAction']) + ->getMock(); + + $controller->expects($this->once())->method('isAction')->willReturn(true); + + return $controller; + } + +} diff --git a/tests/config/routes.php b/tests/config/routes.php new file mode 100644 index 00000000..0e4c5cbf --- /dev/null +++ b/tests/config/routes.php @@ -0,0 +1,9 @@ +fallbacks(DashedRoute::class); +}); diff --git a/tests/test_files/authentication.ini b/tests/test_files/authentication.ini new file mode 100644 index 00000000..7319f129 --- /dev/null +++ b/tests/test_files/authentication.ini @@ -0,0 +1,5 @@ +Users = index, view ; Everyone can access index and view actions +admin/Users = index ; Only index action is public for admin prefix +Extras.Offers = *; All Offers controller actions in Extras plugin are public + +; ... From ae9536822147bfc5dff897bb3f7e3ba2763293c1 Mon Sep 17 00:00:00 2001 From: Mark Scherer Date: Fri, 26 Aug 2016 22:36:16 +0200 Subject: [PATCH 02/15] Cleanup --- src/Auth/TinyAuthorize.php | 73 +++++++--------------- src/Controller/Component/AuthComponent.php | 60 ++---------------- src/Utility/Utility.php | 54 ++++++++++++++++ 3 files changed, 82 insertions(+), 105 deletions(-) create mode 100644 src/Utility/Utility.php diff --git a/src/Auth/TinyAuthorize.php b/src/Auth/TinyAuthorize.php index 83c3d289..69e25893 100644 --- a/src/Auth/TinyAuthorize.php +++ b/src/Auth/TinyAuthorize.php @@ -8,6 +8,7 @@ use Cake\Core\Exception\Exception; use Cake\Network\Request; use Cake\ORM\TableRegistry; +use TinyAuth\Utility\Utility; /** * @deprecated Directly use configs @@ -219,7 +220,7 @@ protected function _getAcl($path = null) { $res = []; foreach ($iniArray as $key => $array) { - $res[$key] = $this->_deconstructIniKey($key); + $res[$key] = Utility::deconstructIniKey($key); $res[$key]['map'] = $array; foreach ($array as $actions => $roles) { @@ -267,51 +268,25 @@ protected function _getAcl($path = null) { return $res; } - /** - * Returns the acl.ini file as an array. - * - * @param string $ini Full path to the acl.ini file - * @return array List with all available roles - * @throws \Cake\Core\Exception\Exception - */ - protected function _parseFile($ini) { - if (!file_exists($ini)) { - throw new Exception(sprintf('Missing TinyAuthorize ACL file (%s)', $ini)); - } - - if (function_exists('parse_ini_file')) { - $iniArray = parse_ini_file($ini, true); - } else { - $iniArray = parse_ini_string(file_get_contents($ini), true); - } - - if (!is_array($iniArray)) { - throw new Exception('Invalid TinyAuthorize ACL file'); - } - return $iniArray; - } - - /** - * Deconstructs an ACL ini section key into a named array with ACL parts. - * - * @param string $key INI section key as found in acl.ini - * @return array Array with named keys for controller, plugin and prefix - */ - protected function _deconstructIniKey($key) { - $res = [ - 'plugin' => null, - 'prefix' => null - ]; - - if (strpos($key, '.') !== false) { - list($res['plugin'], $key) = explode('.', $key); - } - if (strpos($key, '/') !== false) { - list($res['prefix'], $key) = explode('/', $key); - } - $res['controller'] = $key; - return $res; - } + /** + * Returns the acl.ini file as an array. + * + * @param string $ini Full path to the acl.ini file + * @return array List with all available roles + */ + protected function _parseFile($ini) { + return Utility::parseFile($ini); + } + + /** + * Deconstructs an ACL ini section key into a named array with ACL parts. + * + * @param string $key INI section key as found in acl.ini + * @return array Array with named keys for controller, plugin and prefix + */ + protected function _deconstructIniKey($key) { + return Utility::deconstructIniKey($key); + } /** * Constructs an ACL ini section key from a given Request. @@ -351,7 +326,7 @@ protected function _getAvailableRoles() { })->toArray(); if (count($roles) < 1) { - throw new Exception('Invalid TinyAuthorize role setup (roles table `' . $this->_config['rolesTable'] . '` has no roles)'); + throw new Exception('Invalid TinyAuth role setup (roles table `' . $this->_config['rolesTable'] . '` has no roles)'); } return $roles; } @@ -374,7 +349,7 @@ protected function _getUserRoles($user) { if (isset($user[$this->_config['roleColumn']])) { return [$user[$this->_config['roleColumn']]]; } - throw new Exception(sprintf('Missing TinyAuthorize role id (%s) in user session', $this->_config['roleColumn'])); + throw new Exception(sprintf('Missing TinyAuth role id (%s) in user session', $this->_config['roleColumn'])); } // Multi-role case : load the pivot table @@ -399,7 +374,7 @@ protected function _getUserRoles($user) { ->toArray(); if (!count($roles)) { - throw new Exception('Missing TinyAuthorize roles for user in pivot table'); + throw new Exception('Missing TinyAuth roles for user in pivot table'); } return $roles; } diff --git a/src/Controller/Component/AuthComponent.php b/src/Controller/Component/AuthComponent.php index 6d03b266..dc233f6b 100644 --- a/src/Controller/Component/AuthComponent.php +++ b/src/Controller/Component/AuthComponent.php @@ -8,6 +8,7 @@ use Cake\Core\Configure; use Cake\Core\Exception\Exception; use Cake\Event\Event; +use TinyAuth\Utility\Utility; /** * TinyAuth AuthComponent to handle all authentication in a central ini file. @@ -37,7 +38,7 @@ public function __construct(ComponentRegistry $registry, array $config = []) { parent::__construct($registry, $config); if (!in_array($config['cache'], Cache::configured())) { - throw new Exception(sprintf('Invalid TinyAuthorization cache `%s`', $config['cache'])); + throw new Exception(sprintf('Invalid TinyAuth cache `%s`', $config['cache'])); } } @@ -75,12 +76,6 @@ protected function _prepareAuthentication() { } $this->allow($array['actions']); - /* - if (in_array($params['action'], $array['actions'])) { - $this->allow($params['action']); - return; - } - */ } } @@ -105,17 +100,16 @@ protected function _getAuth($path = null) { return $roles; } - $iniArray = $this->_parseFile($path . $this->_config['authFile']); + $iniArray = Utility::parseFile($path . $this->_config['authFile']); $res = []; foreach ($iniArray as $key => $actions) { - $res[$key] = $this->_deconstructIniKey($key); + $res[$key] = Utility::deconstructIniKey($key); $res[$key]['map'] = $actions; $actions = explode(',', $actions); if (in_array('*', $actions)) { - //$this->allow(); $res[$key]['actions'] = []; continue; } @@ -134,50 +128,4 @@ protected function _getAuth($path = null) { return $res; } - /** - * Returns the authentication.ini file as an array. - * - * @param string $ini Full path to the authentication.ini file - * @return array List with all available roles - * @throws \Cake\Core\Exception\Exception - */ - protected function _parseFile($ini) { - if (!file_exists($ini)) { - throw new Exception(sprintf('Missing TinyAuthorize authentication file (%s)', $ini)); - } - - if (function_exists('parse_ini_file')) { - $iniArray = parse_ini_file($ini, true); - } else { - $iniArray = parse_ini_string(file_get_contents($ini), true); - } - - if (!is_array($iniArray)) { - throw new Exception('Invalid TinyAuthorize authentication file'); - } - return $iniArray; - } - - /** - * Deconstructs an authentication ini section key into a named array with authentication parts. - * - * @param string $key INI section key as found in authentication.ini - * @return array Array with named keys for controller, plugin and prefix - */ - protected function _deconstructIniKey($key) { - $res = [ - 'plugin' => null, - 'prefix' => null - ]; - - if (strpos($key, '.') !== false) { - list($res['plugin'], $key) = explode('.', $key); - } - if (strpos($key, '/') !== false) { - list($res['prefix'], $key) = explode('/', $key); - } - $res['controller'] = $key; - return $res; - } - } diff --git a/src/Utility/Utility.php b/src/Utility/Utility.php new file mode 100644 index 00000000..5cbdeabd --- /dev/null +++ b/src/Utility/Utility.php @@ -0,0 +1,54 @@ + null, + 'prefix' => null + ]; + + if (strpos($key, '.') !== false) { + list($res['plugin'], $key) = explode('.', $key); + } + if (strpos($key, '/') !== false) { + list($res['prefix'], $key) = explode('/', $key); + } + $res['controller'] = $key; + return $res; + } + + /** + * Returns the ini file as an array. + * + * @param string $ini Full path to the ini file + * @return array List + * @throws \Cake\Core\Exception\Exception + */ + public static function parseFile($ini) { + if (!file_exists($ini)) { + throw new Exception(sprintf('Missing TinyAuth authentication file (%s)', $ini)); + } + + if (function_exists('parse_ini_file')) { + $iniArray = parse_ini_file($ini, true); + } else { + $iniArray = parse_ini_string(file_get_contents($ini), true); + } + + if (!is_array($iniArray)) { + throw new Exception('Invalid TinyAuth authentication file'); + } + return $iniArray; + } + +} From b78dd604909606096320b53124c03a995bf1202e Mon Sep 17 00:00:00 2001 From: Mark Scherer Date: Fri, 26 Aug 2016 22:45:12 +0200 Subject: [PATCH 03/15] For component we only have normal configs. --- src/Controller/Component/AuthComponent.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Controller/Component/AuthComponent.php b/src/Controller/Component/AuthComponent.php index dc233f6b..80a75ded 100644 --- a/src/Controller/Component/AuthComponent.php +++ b/src/Controller/Component/AuthComponent.php @@ -32,7 +32,6 @@ class AuthComponent extends CakeAuthComponent { * @throws \Cake\Core\Exception\Exception */ public function __construct(ComponentRegistry $registry, array $config = []) { - $config += (array)Configure::read('TinyAuth'); $config += $this->_defaultTinyAuthConfig; parent::__construct($registry, $config); From b04f82ec5fa11044f1ec0a62439f20cc054e57c5 Mon Sep 17 00:00:00 2001 From: Mark Scherer Date: Fri, 26 Aug 2016 22:50:15 +0200 Subject: [PATCH 04/15] PHP5.5+ --- .travis.yml | 1 - composer.json | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5c8770bd..0350e0b0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ language: php sudo: false php: - - 5.4 - 5.5 - 5.6 - 7.0 diff --git a/composer.json b/composer.json index c4986fcd..a0ac7737 100644 --- a/composer.json +++ b/composer.json @@ -13,8 +13,8 @@ } ], "require": { - "php": ">=5.4", - "cakephp/cakephp": "~3.0" + "php": ">=5.5", + "cakephp/cakephp": "~3.2" }, "require-dev": { "fig-r/psr2r-sniffer": "dev-master" From dee31fbb4f12af61632f47184560485b1115746a Mon Sep 17 00:00:00 2001 From: Mark Scherer Date: Fri, 26 Aug 2016 23:06:42 +0200 Subject: [PATCH 05/15] Add docs. --- README.md | 2 +- docs/Authentication.md | 224 ++++++++++++++++++++++++++++++++++++++ docs/Authorization.md | 140 ++++++++++++++++++++++++ docs/README.md | 241 ++++------------------------------------- 4 files changed, 385 insertions(+), 222 deletions(-) create mode 100644 docs/Authentication.md create mode 100644 docs/Authorization.md diff --git a/README.md b/README.md index 7670368d..31f68ba4 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Build Status](https://api.travis-ci.org/dereuromark/cakephp-tinyauth.svg?branch=master)](https://travis-ci.org/dereuromark/cakephp-tinyauth) [![Latest Stable Version](https://poser.pugx.org/dereuromark/cakephp-tinyauth/v/stable.svg)](https://packagist.org/packages/dereuromark/cakephp-tinyauth) [![Coverage Status](https://coveralls.io/repos/dereuromark/cakephp-tinyauth/badge.svg)](https://coveralls.io/r/dereuromark/cakephp-tinyauth) -[![Minimum PHP Version](http://img.shields.io/badge/php-%3E%3D%205.4-8892BF.svg)](https://php.net/) +[![Minimum PHP Version](http://img.shields.io/badge/php-%3E%3D%205.5-8892BF.svg)](https://php.net/) [![License](https://poser.pugx.org/dereuromark/cakephp-tinyauth/license.svg)](https://packagist.org/packages/dereuromark/cakephp-tinyauth) [![Total Downloads](https://poser.pugx.org/dereuromark/cakephp-tinyauth/d/total.svg)](https://packagist.org/packages/dereuromark/cakephp-tinyauth) [![Coding Standards](https://img.shields.io/badge/cs-PSR--2--R-yellow.svg)](https://github.com/php-fig-rectified/fig-rectified-standards) diff --git a/docs/Authentication.md b/docs/Authentication.md new file mode 100644 index 00000000..809167aa --- /dev/null +++ b/docs/Authentication.md @@ -0,0 +1,224 @@ +# TinyAuth Authentication +The fast and easy way for user authentication in CakePHP 3.x applications. + +Use TinyAuth Componont if you want to add instant (and easy) action whitelisting to your application. + +## Basic Features +- INI file (static) based access rights (controller-action setup) +- Lightweight and incredibly fast + +Do NOT use if +- you want to dynamically adjust access rights (or enhance it with a web +frontend yourself) + +## Enabling + +Authentication is set up in your controller's `initialize` method: + +```php +// src/Controller/AppController + +public function initialize() + parent::beforeFilter($event); + + $this->loadComponent('TinyAuth.Auth', [ + '' => [ + 'TinyAuth.Tiny' => [ + 'multiRole' => false + ] + ] + ]); +} +``` + +> Please note that TinyAuth Authorize can be used in combination with any +> [CakePHP Authentication Type](http://book.cakephp.org/3.0/en/controllers/components/authentication.html#choosing-an-authentication-type). + +## Roles + +TinyAuth requires the presence of roles to function so create those first using +one of the following two options. + +### Configure based roles + +Define your roles in a Configure array if you want to prevent database role +lookups, for example: + +```php +// config/app_custom.php + +/** +* Optionally define constants for easy referencing throughout your code +*/ +define('ROLE_USER', 1); +define('ROLE_ADMIN', 2); +define('ROLE_SUPERADMIN', 9); + +return [ + 'Roles' => [ + 'user' => ROLE_USER, + 'admin' => ROLE_ADMIN, + 'superadmin' => ROLE_SUPERADMIN + ] +]; +``` + +### Database roles +When you choose to store your roles in the database TinyAuth expects a table +named ``roles``. If you prefer to use another table name simply specify it using the +``rolesTable`` configuration option. + +>**Note:** make sure to add an "alias" field to your roles table (used as slug +identifier in the acl.ini file) + +Example of a record from a valid roles table: + +```php +'id' => '11' +'name' => 'User' +'description' => 'Basic authenticated user' +'alias' => 'user' +'created' => '2010-01-07 03:36:33' +'modified' => '2010-01-07 03:36:33' +``` + +> Please note that you do NOT need Configure based roles when using database +> roles. Also make sure to remove (or rename) existing Configure based roles +> since TinyAuth will always first try to find a matching Configure roles array +> before falling back to using the database. + +## Users + +### Single-role + +When using the single-role-per-user model TinyAuth expects your Users model to +contain an column named ``role_id``. If you prefer to use another column name +simply specify it using the ``roleColumn`` configuration option. + +The ``roleColumn`` option is also used on pivot table in a multi-role setup. + +### Multi-role +When using the multiple-roles-per-user model: + +- your database MUST have a ``roles`` table +- your database MUST have a valid join table (e.g. ``roles_users``). This can be overridden with the ``pivotTable`` option. +- the configuration option ``multiRole`` MUST be set to ``true`` + +Example of a record from a valid join table: + +```php +'id' => 1 +'user_id' => 1 +'role_id' => 1 +``` + +If you want to have default database tables here for multi-role auth, you can use the plugin shipped Migrations file: +``` +bin/cake migrations migrate -p TinyAuth +``` +Alternatively you can copy and paste this migration file to your `app/Config` folder and adjust the fields and table names and then use that modified version instead. + +## acl.ini + +TinyAuth expects an ``acl.ini`` file in your config directory. +Use it to specify who gets access to which resources. + +The section key syntax follows the CakePHP naming convention for plugins. + +Make sure to create an entry for each action you want to expose and use: + +- one or more role names (groups granted access) +- the ``*`` wildcard to allow access to all authenticated users + +```ini +; ---------------------------------------------------------- +; Userscontroller +; ---------------------------------------------------------- +[Users] +index = user, admin, undefined-role +edit, view = user, admin +* = admin +; ---------------------------------------------------------- +; UsersController using /api prefixed route +; ---------------------------------------------------------- +[api/Users] +view = user +* = admin +; ---------------------------------------------------------- +; UsersController using /admin prefixed route +; ---------------------------------------------------------- +[admin/Users] +* = admin +; ---------------------------------------------------------- +; AccountsController in plugin named Accounts +; ---------------------------------------------------------- +[Accounts.Accounts] +view, edit = user +* = admin +; ---------------------------------------------------------- +; AccountsController in plugin named Accounts using /admin +; prefixed route +; ---------------------------------------------------------- +[Accounts.admin/Accounts] +* = admin +; ---------------------------------------------------------- +; CompaniesController in plugin named Accounts +; ---------------------------------------------------------- +[Accounts.Companies] +view, edit = user +* = admin +; ---------------------------------------------------------- +; CompaniesController in plugin named Accounts using /admin +; prefixed route +; ---------------------------------------------------------- +[Accounts.admin/Companies] +* = admin +``` + +## Caching + +TinyAuth makes heavy use of caching to achieve optimal performance. + +You may however want to disable caching while developing RBAC to prevent +confusing (outdated) results. + +To disable caching either: + +- pass ``true`` to the ``autoClearCache`` configuration option +- use the example below to disable caching automatically for CakePHP debug mode + +```php +'TinyAuth' => [ + 'autoClearCache' => Configure::read('debug') +] +``` + +## Configuration + +TinyAuth supports the following configuration options. + +Option | Type | Description +:----- | :--- | :---------- +roleColumn|string|Name of column in user table holding role id (used for foreign key in users table in a single role per user setup, or in the pivot table on multi-roles setup) +userColumn|string|Name of column in pivot table holding role id (only used in pivot table on multi-roles setup) +aliasColumn|string|Name of the column for the alias in the role table +idColumn|string|Name of the ID Column in users table +rolesTable|string|Name of Configure key holding all available roles OR class name of roles database table +usersTable|string|Class name of the users table. +pivotTable|string|Name of the pivot table, for a multi-group setup. +rolesTablePlugin|string|Name of the plugin for the roles table, if any. +pivotTablePlugin|string|Name of the plugin for the pivot table, if any. +multiRole|bool|True will enable multi-role/HABTM authorization (requires a valid join table) +superAdminRole|int|Id of the super admin role. Users with this role will have access to ALL resources. +superAdmin|int or string|Id/name of the super admin. Users with this id/name will have access to ALL resources.null/0/"0" take disable it +superAdminColumn|string|Column of super admin in user table.default use idColumn option +authorizeByPrefix|bool|If prefixed routes should be auto-handled by their matching role name. +prefixes|array|A list of authorizeByPrefix handled prefixes. +allowUser|bool|True will give authenticated users access to all resources except those using the `adminPrefix` +adminPrefix|string|Name of the prefix used for admin pages. Defaults to admin. +autoClearCache|bool|True will generate a new ACL cache file every time. +aclPath|string|Full path to the acl.ini. Defaults to `ROOT . DS . 'config' . DS`. + + +## Auth user data +For reading auth user data take a look at [Tools plugin AuthUser component/helper](https://github.com/dereuromark/cakephp-tools/blob/master/docs/Auth/Auth.md). diff --git a/docs/Authorization.md b/docs/Authorization.md new file mode 100644 index 00000000..227cf3e1 --- /dev/null +++ b/docs/Authorization.md @@ -0,0 +1,140 @@ +# TinyAuth Authorization +The fast and easy way for user authorization in CakePHP 3.x applications. + +Enable TinyAuth Authorize adapter if you want to add instant (and easy) role +based access control (RBAC) to your application. + +## Basic Features +- Single or multi role +- DB (dynamic) or Configure based role definition +- INI file (static) based access rights (controller-action/role setup) +- Lightweight and incredibly fast + +Do NOT use if +- you need ROW based access +- you want to dynamically adjust access rights (or enhance it with a web +frontend yourself) + +## Enabling + +Assuming you already have authentication set up correctly you can enable +authorization in your controller's `beforeFilter` like this example: + +```php +// src/Controller/AppController + +use Cake\Event\Event; + +public function beforeFilter(Event $event) { + parent::beforeFilter($event); + + $this->loadComponent('Auth', [ + 'authFile' => ... + ]); +} +``` + +## authentication.ini + +TinyAuth expects an ``acl.ini`` file in your config directory. +Use it to specify who gets access to which resources. + +The section key syntax follows the CakePHP naming convention for plugins. + +Make sure to create an entry for each action you want to expose and use: + +- one or more role names (groups granted access) +- the ``*`` wildcard to allow access to all authenticated users + +```ini +; ---------------------------------------------------------- +; Userscontroller +; ---------------------------------------------------------- +[Users] +index = user, admin, undefined-role +edit, view = user, admin +* = admin +; ---------------------------------------------------------- +; UsersController using /api prefixed route +; ---------------------------------------------------------- +[api/Users] +view = user +* = admin +; ---------------------------------------------------------- +; UsersController using /admin prefixed route +; ---------------------------------------------------------- +[admin/Users] +* = admin +; ---------------------------------------------------------- +; AccountsController in plugin named Accounts +; ---------------------------------------------------------- +[Accounts.Accounts] +view, edit = user +* = admin +; ---------------------------------------------------------- +; AccountsController in plugin named Accounts using /admin +; prefixed route +; ---------------------------------------------------------- +[Accounts.admin/Accounts] +* = admin +; ---------------------------------------------------------- +; CompaniesController in plugin named Accounts +; ---------------------------------------------------------- +[Accounts.Companies] +view, edit = user +* = admin +; ---------------------------------------------------------- +; CompaniesController in plugin named Accounts using /admin +; prefixed route +; ---------------------------------------------------------- +[Accounts.admin/Companies] +* = admin +``` + +## Caching + +TinyAuth makes heavy use of caching to achieve optimal performance. + +You may however want to disable caching while developing RBAC to prevent +confusing (outdated) results. + +To disable caching either: + +- pass ``true`` to the ``autoClearCache`` configuration option +- use the example below to disable caching automatically for CakePHP debug mode + +```php +'TinyAuth' => [ + 'autoClearCache' => Configure::read('debug') +] +``` + +## Configuration + +TinyAuth supports the following configuration options. + +Option | Type | Description +:----- | :--- | :---------- +roleColumn|string|Name of column in user table holding role id (used for foreign key in users table in a single role per user setup, or in the pivot table on multi-roles setup) +userColumn|string|Name of column in pivot table holding role id (only used in pivot table on multi-roles setup) +aliasColumn|string|Name of the column for the alias in the role table +idColumn|string|Name of the ID Column in users table +rolesTable|string|Name of Configure key holding all available roles OR class name of roles database table +usersTable|string|Class name of the users table. +pivotTable|string|Name of the pivot table, for a multi-group setup. +rolesTablePlugin|string|Name of the plugin for the roles table, if any. +pivotTablePlugin|string|Name of the plugin for the pivot table, if any. +multiRole|bool|True will enable multi-role/HABTM authorization (requires a valid join table) +superAdminRole|int|Id of the super admin role. Users with this role will have access to ALL resources. +superAdmin|int or string|Id/name of the super admin. Users with this id/name will have access to ALL resources.null/0/"0" take disable it +superAdminColumn|string|Column of super admin in user table.default use idColumn option +authorizeByPrefix|bool|If prefixed routes should be auto-handled by their matching role name. +prefixes|array|A list of authorizeByPrefix handled prefixes. +allowUser|bool|True will give authenticated users access to all resources except those using the `adminPrefix` +adminPrefix|string|Name of the prefix used for admin pages. Defaults to admin. +autoClearCache|bool|True will generate a new ACL cache file every time. +aclPath|string|Full path to the acl.ini. Defaults to `ROOT . DS . 'config' . DS`. + + +## Auth user data +For reading auth user data take a look at [Tools plugin AuthUser component/helper](https://github.com/dereuromark/cakephp-tools/blob/master/docs/Auth/Auth.md). diff --git a/docs/README.md b/docs/README.md index 8cfd205c..f60d36f8 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,231 +1,30 @@ -# TinyAuth Authorization -The fast and easy way for user authorization in CakePHP 3.x applications. +# TinyAuth Authentication and Authorization -Enable TinyAuth Authorize adapter if you want to add instant (and easy) role -based access (RBAC) to your application. +This plugin ships with both +- Authentication: Always comes first - "who is it"? +- Authorization: Afterwards - "What can this person see"? -## Basic Features -- Single or multi role -- DB (dynamic) or Configure based role definition -- INI file (static) based access rights (controller-action/role setup) -- Lightweight and incredibly fast +For the first one usually declares as a whitelist of actions per controller that will not require any authentication. +If an action is not whitelisted, it will trigger the login process. -Do NOT use if -- you need ROW based access -- you want to dynamically adjust access rights (or enhance it with a web -frontend yourself) +The second only gets invoked once the person is already logged in. +In that case the role of this logged in user decides if that action is allowed to be accessed. -## Enabling +## Authentication +NEW since version 1.4.0 -Assuming you already have authentication set up correctly you can enable -authorization in your controller's `beforeFilter` like this example: +This is done via TinyAuth Auth Component. -```php -// src/Controller/AppController +The component plays well together with the adapter (see below). +If you do not have any roles and either all are logged in or not logged in you can also use this stand-alone to make certain pages public. -use Cake\Event\Event; +See [Authentication](/docs/Authentication) docs. -public function beforeFilter(Event $event) { - parent::beforeFilter($event); +## Authorization +For this we have a TinyAuth Authorize adapter. - $this->loadComponent('Auth', [ - 'authorize' => [ - 'TinyAuth.Tiny' => [ - 'multiRole' => false - ] - ] - ]); -} -``` +The adapter plays well together with the component above. +But if you prefer to control the action whitelisting for authentication via code and `$this->Auth->allow()` calls, you can +also just use this adapter stand-alone for the ACL part of your application. -> Please note that TinyAuth Authorize can be used in combination with any -> [CakePHP Authentication Type](http://book.cakephp.org/3.0/en/controllers/components/authentication.html#choosing-an-authentication-type). - -## Roles - -TinyAuth requires the presence of roles to function so create those first using -one of the following two options. - -### Configure based roles - -Define your roles in a Configure array if you want to prevent database role -lookups, for example: - -```php -// config/app_custom.php - -/** -* Optionally define constants for easy referencing throughout your code -*/ -define('ROLE_USER', 1); -define('ROLE_ADMIN', 2); -define('ROLE_SUPERADMIN', 9); - -return [ - 'Roles' => [ - 'user' => ROLE_USER, - 'admin' => ROLE_ADMIN, - 'superadmin' => ROLE_SUPERADMIN - ] -]; -``` - -### Database roles -When you choose to store your roles in the database TinyAuth expects a table -named ``roles``. If you prefer to use another table name simply specify it using the -``rolesTable`` configuration option. - ->**Note:** make sure to add an "alias" field to your roles table (used as slug -identifier in the acl.ini file) - -Example of a record from a valid roles table: - -```php -'id' => '11' -'name' => 'User' -'description' => 'Basic authenticated user' -'alias' => 'user' -'created' => '2010-01-07 03:36:33' -'modified' => '2010-01-07 03:36:33' -``` - -> Please note that you do NOT need Configure based roles when using database -> roles. Also make sure to remove (or rename) existing Configure based roles -> since TinyAuth will always first try to find a matching Configure roles array -> before falling back to using the database. - -## Users - -### Single-role - -When using the single-role-per-user model TinyAuth expects your Users model to -contain an column named ``role_id``. If you prefer to use another column name -simply specify it using the ``roleColumn`` configuration option. - -The ``roleColumn`` option is also used on pivot table in a multi-role setup. - -### Multi-role -When using the multiple-roles-per-user model: - -- your database MUST have a ``roles`` table -- your database MUST have a valid join table (e.g. ``roles_users``). This can be overridden with the ``pivotTable`` option. -- the configuration option ``multiRole`` MUST be set to ``true`` - -Example of a record from a valid join table: - -```php -'id' => 1 -'user_id' => 1 -'role_id' => 1 -``` - -If you want to have default database tables here for multi-role auth, you can use the plugin shipped Migrations file: -``` -bin/cake migrations migrate -p TinyAuth -``` -Alternatively you can copy and paste this migration file to your `app/Config` folder and adjust the fields and table names and then use that modified version instead. - -## acl.ini - -TinyAuth expects an ``acl.ini`` file in your config directory. -Use it to specify who gets access to which resources. - -The section key syntax follows the CakePHP naming convention for plugins. - -Make sure to create an entry for each action you want to expose and use: - -- one or more role names (groups granted access) -- the ``*`` wildcard to allow access to all authenticated users - -```ini -; ---------------------------------------------------------- -; Userscontroller -; ---------------------------------------------------------- -[Users] -index = user, admin, undefined-role -edit, view = user, admin -* = admin -; ---------------------------------------------------------- -; UsersController using /api prefixed route -; ---------------------------------------------------------- -[api/Users] -view = user -* = admin -; ---------------------------------------------------------- -; UsersController using /admin prefixed route -; ---------------------------------------------------------- -[admin/Users] -* = admin -; ---------------------------------------------------------- -; AccountsController in plugin named Accounts -; ---------------------------------------------------------- -[Accounts.Accounts] -view, edit = user -* = admin -; ---------------------------------------------------------- -; AccountsController in plugin named Accounts using /admin -; prefixed route -; ---------------------------------------------------------- -[Accounts.admin/Accounts] -* = admin -; ---------------------------------------------------------- -; CompaniesController in plugin named Accounts -; ---------------------------------------------------------- -[Accounts.Companies] -view, edit = user -* = admin -; ---------------------------------------------------------- -; CompaniesController in plugin named Accounts using /admin -; prefixed route -; ---------------------------------------------------------- -[Accounts.admin/Companies] -* = admin -``` - -## Caching - -TinyAuth makes heavy use of caching to achieve optimal performance. - -You may however want to disable caching while developing RBAC to prevent -confusing (outdated) results. - -To disable caching either: - -- pass ``true`` to the ``autoClearCache`` configuration option -- use the example below to disable caching automatically for CakePHP debug mode - -```php -'TinyAuth' => [ - 'autoClearCache' => Configure::read('debug') -] -``` - -## Configuration - -TinyAuth supports the following configuration options. - -Option | Type | Description -:----- | :--- | :---------- -roleColumn|string|Name of column in user table holding role id (used for foreign key in users table in a single role per user setup, or in the pivot table on multi-roles setup) -userColumn|string|Name of column in pivot table holding role id (only used in pivot table on multi-roles setup) -aliasColumn|string|Name of the column for the alias in the role table -idColumn|string|Name of the ID Column in users table -rolesTable|string|Name of Configure key holding all available roles OR class name of roles database table -usersTable|string|Class name of the users table. -pivotTable|string|Name of the pivot table, for a multi-group setup. -rolesTablePlugin|string|Name of the plugin for the roles table, if any. -pivotTablePlugin|string|Name of the plugin for the pivot table, if any. -multiRole|bool|True will enable multi-role/HABTM authorization (requires a valid join table) -superAdminRole|int|Id of the super admin role. Users with this role will have access to ALL resources. -superAdmin|int or string|Id/name of the super admin. Users with this id/name will have access to ALL resources.null/0/"0" take disable it -superAdminColumn|string|Column of super admin in user table.default use idColumn option -authorizeByPrefix|bool|If prefixed routes should be auto-handled by their matching role name. -prefixes|array|A list of authorizeByPrefix handled prefixes. -allowUser|bool|True will give authenticated users access to all resources except those using the `adminPrefix` -adminPrefix|string|Name of the prefix used for admin pages. Defaults to admin. -autoClearCache|bool|True will generate a new ACL cache file every time. -aclPath|string|Full path to the acl.ini. Defaults to `ROOT . DS . 'config' . DS`. - - -## Auth user data -For reading auth user data take a look at [Tools plugin AuthUser component/helper](https://github.com/dereuromark/cakephp-tools/blob/master/docs/Auth/Auth.md). +See [Authorization](/docs/Authorization) docs. From 270cafd67c06d50f0f917048dd96956ebab0c5ab Mon Sep 17 00:00:00 2001 From: Mark Scherer Date: Sat, 27 Aug 2016 00:51:16 +0200 Subject: [PATCH 06/15] Fix CS --- src/Auth/TinyAuthorize.php | 38 +-- src/Controller/Component/AuthComponent.php | 224 +++++++++--------- src/Utility/Utility.php | 90 +++---- .../Component/AuthComponentTest.php | 164 ++++++------- tests/config/routes.php | 4 +- 5 files changed, 260 insertions(+), 260 deletions(-) diff --git a/src/Auth/TinyAuthorize.php b/src/Auth/TinyAuthorize.php index 69e25893..18f3c8d4 100644 --- a/src/Auth/TinyAuthorize.php +++ b/src/Auth/TinyAuthorize.php @@ -268,25 +268,25 @@ protected function _getAcl($path = null) { return $res; } - /** - * Returns the acl.ini file as an array. - * - * @param string $ini Full path to the acl.ini file - * @return array List with all available roles - */ - protected function _parseFile($ini) { - return Utility::parseFile($ini); - } - - /** - * Deconstructs an ACL ini section key into a named array with ACL parts. - * - * @param string $key INI section key as found in acl.ini - * @return array Array with named keys for controller, plugin and prefix - */ - protected function _deconstructIniKey($key) { - return Utility::deconstructIniKey($key); - } + /** + * Returns the acl.ini file as an array. + * + * @param string $ini Full path to the acl.ini file + * @return array List with all available roles + */ + protected function _parseFile($ini) { + return Utility::parseFile($ini); + } + + /** + * Deconstructs an ACL ini section key into a named array with ACL parts. + * + * @param string $key INI section key as found in acl.ini + * @return array Array with named keys for controller, plugin and prefix + */ + protected function _deconstructIniKey($key) { + return Utility::deconstructIniKey($key); + } /** * Constructs an ACL ini section key from a given Request. diff --git a/src/Controller/Component/AuthComponent.php b/src/Controller/Component/AuthComponent.php index 80a75ded..574c7f67 100644 --- a/src/Controller/Component/AuthComponent.php +++ b/src/Controller/Component/AuthComponent.php @@ -3,8 +3,8 @@ namespace TinyAuth\Controller\Component; use Cake\Cache\Cache; -use Cake\Controller\Component\AuthComponent as CakeAuthComponent; use Cake\Controller\ComponentRegistry; +use Cake\Controller\Component\AuthComponent as CakeAuthComponent; use Cake\Core\Configure; use Cake\Core\Exception\Exception; use Cake\Event\Event; @@ -15,116 +15,116 @@ */ class AuthComponent extends CakeAuthComponent { - /** - * @var array - */ - protected $_defaultTinyAuthConfig = [ - 'cache' => '_cake_core_', - 'cacheKey' => 'tiny_auth_authentication', - 'autoClearCache' => false, // usually done by Cache automatically in debug mode, - 'authPath' => null, // possible to locate authentication.ini at given path e.g. Plugin::configPath('Admin') - 'authFile' => 'authentication.ini' - ]; - - /** - * @param \Cake\Controller\ComponentRegistry $registry - * @param array $config - * @throws \Cake\Core\Exception\Exception - */ - public function __construct(ComponentRegistry $registry, array $config = []) { - $config += $this->_defaultTinyAuthConfig; - - parent::__construct($registry, $config); - - if (!in_array($config['cache'], Cache::configured())) { - throw new Exception(sprintf('Invalid TinyAuth cache `%s`', $config['cache'])); - } - } - - /** - * @param \Cake\Event\Event $event Event instance. - * @return \Cake\Network\Response|null - */ - public function startup(Event $event) { - $this->_prepareAuthentication(); - - return parent::startup($event); - } - - /** - * @return void - */ - protected function _prepareAuthentication() { - $authentication = $this->_getAuth($this->_config['authPath']); - - $params = $this->request->params; - foreach ($authentication as $array) { - if ($params['plugin'] && $params['plugin'] !== $array['plugin']) { - continue; - } - if (!empty($params['prefix']) && $params['prefix'] !== $array['prefix']) { - continue; - } - if ($params['controller'] !== $array['controller']) { - continue; - } - - if ($array['actions'] === []) { - $this->allow(); - return; - } - - $this->allow($array['actions']); - } - } - - /** - * Parse ini file and returns the allowed actions. - * - * Uses cache for maximum performance. - * - * @param string|null $path - * @return array Actions - */ - protected function _getAuth($path = null) { - if ($path === null) { - $path = ROOT . DS . 'config' . DS; - } - - if ($this->_config['autoClearCache'] && Configure::read('debug')) { - Cache::delete($this->_config['cacheKey'], $this->_config['cache']); - } - $roles = Cache::read($this->_config['cacheKey'], $this->_config['cache']); - if ($roles !== false) { - return $roles; - } - - $iniArray = Utility::parseFile($path . $this->_config['authFile']); - - $res = []; - foreach ($iniArray as $key => $actions) { - $res[$key] = Utility::deconstructIniKey($key); - $res[$key]['map'] = $actions; - - $actions = explode(',', $actions); - - if (in_array('*', $actions)) { - $res[$key]['actions'] = []; - continue; - } - - foreach ($actions as $action) { - $action = trim($action); - if (!$action) { - continue; - } - - $res[$key]['actions'][] = $action; - } - } - - Cache::write($this->_config['cacheKey'], $res, $this->_config['cache']); - return $res; - } + /** + * @var array + */ + protected $_defaultTinyAuthConfig = [ + 'cache' => '_cake_core_', + 'cacheKey' => 'tiny_auth_authentication', + 'autoClearCache' => false, // usually done by Cache automatically in debug mode, + 'authPath' => null, // possible to locate authentication.ini at given path e.g. Plugin::configPath('Admin') + 'authFile' => 'authentication.ini' + ]; + + /** + * @param \Cake\Controller\ComponentRegistry $registry + * @param array $config + * @throws \Cake\Core\Exception\Exception + */ + public function __construct(ComponentRegistry $registry, array $config = []) { + $config += $this->_defaultTinyAuthConfig; + + parent::__construct($registry, $config); + + if (!in_array($config['cache'], Cache::configured())) { + throw new Exception(sprintf('Invalid TinyAuth cache `%s`', $config['cache'])); + } + } + + /** + * @param \Cake\Event\Event $event Event instance. + * @return \Cake\Network\Response|null + */ + public function startup(Event $event) { + $this->_prepareAuthentication(); + + return parent::startup($event); + } + + /** + * @return void + */ + protected function _prepareAuthentication() { + $authentication = $this->_getAuth($this->_config['authPath']); + + $params = $this->request->params; + foreach ($authentication as $array) { + if ($params['plugin'] && $params['plugin'] !== $array['plugin']) { + continue; + } + if (!empty($params['prefix']) && $params['prefix'] !== $array['prefix']) { + continue; + } + if ($params['controller'] !== $array['controller']) { + continue; + } + + if ($array['actions'] === []) { + $this->allow(); + return; + } + + $this->allow($array['actions']); + } + } + + /** + * Parse ini file and returns the allowed actions. + * + * Uses cache for maximum performance. + * + * @param string|null $path + * @return array Actions + */ + protected function _getAuth($path = null) { + if ($path === null) { + $path = ROOT . DS . 'config' . DS; + } + + if ($this->_config['autoClearCache'] && Configure::read('debug')) { + Cache::delete($this->_config['cacheKey'], $this->_config['cache']); + } + $roles = Cache::read($this->_config['cacheKey'], $this->_config['cache']); + if ($roles !== false) { + return $roles; + } + + $iniArray = Utility::parseFile($path . $this->_config['authFile']); + + $res = []; + foreach ($iniArray as $key => $actions) { + $res[$key] = Utility::deconstructIniKey($key); + $res[$key]['map'] = $actions; + + $actions = explode(',', $actions); + + if (in_array('*', $actions)) { + $res[$key]['actions'] = []; + continue; + } + + foreach ($actions as $action) { + $action = trim($action); + if (!$action) { + continue; + } + + $res[$key]['actions'][] = $action; + } + } + + Cache::write($this->_config['cacheKey'], $res, $this->_config['cache']); + return $res; + } } diff --git a/src/Utility/Utility.php b/src/Utility/Utility.php index 5cbdeabd..5b3fbb62 100644 --- a/src/Utility/Utility.php +++ b/src/Utility/Utility.php @@ -5,50 +5,50 @@ class Utility { - /** - * Deconstructs an authentication ini section key into a named array with authentication parts. - * - * @param string $key INI section key as found in authentication.ini - * @return array Array with named keys for controller, plugin and prefix - */ - public static function deconstructIniKey($key) { - $res = [ - 'plugin' => null, - 'prefix' => null - ]; - - if (strpos($key, '.') !== false) { - list($res['plugin'], $key) = explode('.', $key); - } - if (strpos($key, '/') !== false) { - list($res['prefix'], $key) = explode('/', $key); - } - $res['controller'] = $key; - return $res; - } - - /** - * Returns the ini file as an array. - * - * @param string $ini Full path to the ini file - * @return array List - * @throws \Cake\Core\Exception\Exception - */ - public static function parseFile($ini) { - if (!file_exists($ini)) { - throw new Exception(sprintf('Missing TinyAuth authentication file (%s)', $ini)); - } - - if (function_exists('parse_ini_file')) { - $iniArray = parse_ini_file($ini, true); - } else { - $iniArray = parse_ini_string(file_get_contents($ini), true); - } - - if (!is_array($iniArray)) { - throw new Exception('Invalid TinyAuth authentication file'); - } - return $iniArray; - } + /** + * Deconstructs an authentication ini section key into a named array with authentication parts. + * + * @param string $key INI section key as found in authentication.ini + * @return array Array with named keys for controller, plugin and prefix + */ + public static function deconstructIniKey($key) { + $res = [ + 'plugin' => null, + 'prefix' => null + ]; + + if (strpos($key, '.') !== false) { + list($res['plugin'], $key) = explode('.', $key); + } + if (strpos($key, '/') !== false) { + list($res['prefix'], $key) = explode('/', $key); + } + $res['controller'] = $key; + return $res; + } + + /** + * Returns the ini file as an array. + * + * @param string $ini Full path to the ini file + * @return array List + * @throws \Cake\Core\Exception\Exception + */ + public static function parseFile($ini) { + if (!file_exists($ini)) { + throw new Exception(sprintf('Missing TinyAuth authentication file (%s)', $ini)); + } + + if (function_exists('parse_ini_file')) { + $iniArray = parse_ini_file($ini, true); + } else { + $iniArray = parse_ini_string(file_get_contents($ini), true); + } + + if (!is_array($iniArray)) { + throw new Exception('Invalid TinyAuth authentication file'); + } + return $iniArray; + } } diff --git a/tests/TestCase/Controller/Component/AuthComponentTest.php b/tests/TestCase/Controller/Component/AuthComponentTest.php index b67eed45..309e5bab 100644 --- a/tests/TestCase/Controller/Component/AuthComponentTest.php +++ b/tests/TestCase/Controller/Component/AuthComponentTest.php @@ -16,87 +16,87 @@ */ class AuthComponentTest extends TestCase { - /** - * @var AuthComponent - */ - protected $AuthComponent; - - /** - * @var - */ - protected $componentConfig = []; - /** - * @return void - */ - public function setUp() { - $this->componentConfig = [ - 'authPath' => Plugin::path('TinyAuth') . 'tests' . DS . 'test_files' . DS - ]; - } - - /** - * @return void - */ - public function testValid() { - $request = new Request(['params' => [ - 'controller' => 'Users', - 'action' => 'view', - 'plugin' => null, - '_ext' => null, - 'pass' => [1] - ]]); - $controller = $this->getControllerMock($request); - - $registry = new ComponentRegistry($controller); - $this->AuthComponent = new AuthComponent($registry, $this->componentConfig); - - $config = []; - $this->AuthComponent->initialize($config); - - $event = new Event('Controller.startup', $controller); - $response = $this->AuthComponent->startup($event); - $this->assertNull($response); - } - - /** - * @return void - */ - public function testInvalid() { - $request = new Request(['params' => [ - 'controller' => 'FooBar', - 'action' => 'index', - 'plugin' => null, - '_ext' => null, - 'pass' => [] - ]]); - $controller = $this->getControllerMock($request); - - $registry = new ComponentRegistry($controller); - $this->AuthComponent = new AuthComponent($registry, $this->componentConfig); - - $config = []; - $this->AuthComponent->initialize($config); - - $event = new Event('Controller.startup', $controller); - $response = $this->AuthComponent->startup($event); - - $this->assertInstanceOf(Response::class, $response); - $this->assertSame(302, $response->statusCode()); - } - - /** - * @param \Cake\Network\Request $request - * @return Controller - */ - protected function getControllerMock(Request $request) { - $controller = $this->getMockBuilder(Controller::class) - ->setConstructorArgs([$request]) - ->setMethods(['isAction']) - ->getMock(); - - $controller->expects($this->once())->method('isAction')->willReturn(true); - - return $controller; - } + /** + * @var \TinyAuth\Controller\Component\AuthComponent + */ + protected $AuthComponent; + + /** + * @var array + */ + protected $componentConfig = []; + /** + * @return void + */ + public function setUp() { + $this->componentConfig = [ + 'authPath' => Plugin::path('TinyAuth') . 'tests' . DS . 'test_files' . DS + ]; + } + + /** + * @return void + */ + public function testValid() { + $request = new Request(['params' => [ + 'controller' => 'Users', + 'action' => 'view', + 'plugin' => null, + '_ext' => null, + 'pass' => [1] + ]]); + $controller = $this->getControllerMock($request); + + $registry = new ComponentRegistry($controller); + $this->AuthComponent = new AuthComponent($registry, $this->componentConfig); + + $config = []; + $this->AuthComponent->initialize($config); + + $event = new Event('Controller.startup', $controller); + $response = $this->AuthComponent->startup($event); + $this->assertNull($response); + } + + /** + * @return void + */ + public function testInvalid() { + $request = new Request(['params' => [ + 'controller' => 'FooBar', + 'action' => 'index', + 'plugin' => null, + '_ext' => null, + 'pass' => [] + ]]); + $controller = $this->getControllerMock($request); + + $registry = new ComponentRegistry($controller); + $this->AuthComponent = new AuthComponent($registry, $this->componentConfig); + + $config = []; + $this->AuthComponent->initialize($config); + + $event = new Event('Controller.startup', $controller); + $response = $this->AuthComponent->startup($event); + + $this->assertInstanceOf(Response::class, $response); + $this->assertSame(302, $response->statusCode()); + } + + /** + * @param \Cake\Network\Request $request + * @return \Cake\Controller\Controller + */ + protected function getControllerMock(Request $request) { + $controller = $this->getMockBuilder(Controller::class) + ->setConstructorArgs([$request]) + ->setMethods(['isAction']) + ->getMock(); + + $controller->expects($this->once())->method('isAction')->willReturn(true); + + return $controller; + } } diff --git a/tests/config/routes.php b/tests/config/routes.php index 0e4c5cbf..647d8346 100644 --- a/tests/config/routes.php +++ b/tests/config/routes.php @@ -1,9 +1,9 @@ fallbacks(DashedRoute::class); + $routes->fallbacks(DashedRoute::class); }); From d048324f6c9e3319a6e397a83eb425fb29c668d6 Mon Sep 17 00:00:00 2001 From: Mark Scherer Date: Sat, 27 Aug 2016 01:21:26 +0200 Subject: [PATCH 07/15] Rename to auth-allow.ini --- .gitignore | 1 + src/Auth/TinyAuthorize.php | 17 ++++++++++++----- src/Controller/Component/AuthComponent.php | 10 +++++----- .../Controller/Component/AuthComponentTest.php | 2 +- tests/bootstrap.php | 1 - .../{authentication.ini => auth-allow.ini} | 0 6 files changed, 19 insertions(+), 12 deletions(-) rename tests/test_files/{authentication.ini => auth-allow.ini} (100%) diff --git a/.gitignore b/.gitignore index 35028697..32bd2402 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /tmp/ /composer.lock /composer.phar +/.idea diff --git a/src/Auth/TinyAuthorize.php b/src/Auth/TinyAuthorize.php index 18f3c8d4..fc3acad6 100644 --- a/src/Auth/TinyAuthorize.php +++ b/src/Auth/TinyAuthorize.php @@ -11,13 +11,13 @@ use TinyAuth\Utility\Utility; /** - * @deprecated Directly use configs + * @deprecated Directly use cache config key */ if (!defined('AUTH_CACHE')) { define('AUTH_CACHE', '_cake_core_'); // use the most persistent cache by default } /** - * @deprecated Directly use configs + * @deprecated Directly use file config key */ if (!defined('ACL_FILE')) { define('ACL_FILE', 'acl.ini'); // stored in /config/ by default @@ -67,10 +67,12 @@ class TinyAuthorize extends BaseAuthorize { 'prefixes' => [], // Whitelisted prefixes (only used when allowAdmin is enabled), leave empty to use all available 'allowUser' => false, // enable to allow ALL roles access to all actions except prefixed with 'adminPrefix' 'adminPrefix' => 'admin', // name of the admin prefix route (only used when allowUser is enabled) - 'cache' => AUTH_CACHE, + 'cache' => '_cake_core_', 'cacheKey' => 'tiny_auth_acl', 'autoClearCache' => false, // usually done by Cache automatically in debug mode, - 'aclPath' => null, // possible to locate acl.ini at given path e.g. Plugin::configPath('Admin') + 'aclPath' => null, // @deprecated Use filePath + 'filePath' => null, // possible to locate ini file at given path e.g. Plugin::configPath('Admin') + 'file' => 'acl.ini', ]; /** @@ -90,6 +92,11 @@ public function __construct(ComponentRegistry $registry, array $config = []) { if (!in_array($config['cache'], Cache::configured())) { throw new Exception(sprintf('Invalid TinyAuthorization cache `%s`', $config['cache'])); } + + // BC only + if (isset($this->_config['aclPath'])) { + $this->_config['filePath'] = $this->_config['aclPath']; + } } /** @@ -215,7 +222,7 @@ protected function _getAcl($path = null) { return $roles; } - $iniArray = $this->_parseFile($path . ACL_FILE); + $iniArray = $this->_parseFile($path . $this->_config['file']); $availableRoles = $this->_getAvailableRoles(); $res = []; diff --git a/src/Controller/Component/AuthComponent.php b/src/Controller/Component/AuthComponent.php index 574c7f67..9c9ab97b 100644 --- a/src/Controller/Component/AuthComponent.php +++ b/src/Controller/Component/AuthComponent.php @@ -22,8 +22,8 @@ class AuthComponent extends CakeAuthComponent { 'cache' => '_cake_core_', 'cacheKey' => 'tiny_auth_authentication', 'autoClearCache' => false, // usually done by Cache automatically in debug mode, - 'authPath' => null, // possible to locate authentication.ini at given path e.g. Plugin::configPath('Admin') - 'authFile' => 'authentication.ini' + 'filePath' => null, // possible to locate ini file at given path e.g. Plugin::configPath('Admin') + 'file' => 'auth-allow.ini', ]; /** @@ -55,7 +55,7 @@ public function startup(Event $event) { * @return void */ protected function _prepareAuthentication() { - $authentication = $this->_getAuth($this->_config['authPath']); + $authentication = $this->_getAuth($this->_config['filePath']); $params = $this->request->params; foreach ($authentication as $array) { @@ -90,7 +90,7 @@ protected function _getAuth($path = null) { if ($path === null) { $path = ROOT . DS . 'config' . DS; } - +debug($path); if ($this->_config['autoClearCache'] && Configure::read('debug')) { Cache::delete($this->_config['cacheKey'], $this->_config['cache']); } @@ -99,7 +99,7 @@ protected function _getAuth($path = null) { return $roles; } - $iniArray = Utility::parseFile($path . $this->_config['authFile']); + $iniArray = Utility::parseFile($path . $this->_config['file']); $res = []; foreach ($iniArray as $key => $actions) { diff --git a/tests/TestCase/Controller/Component/AuthComponentTest.php b/tests/TestCase/Controller/Component/AuthComponentTest.php index 309e5bab..00b2b5d7 100644 --- a/tests/TestCase/Controller/Component/AuthComponentTest.php +++ b/tests/TestCase/Controller/Component/AuthComponentTest.php @@ -30,7 +30,7 @@ class AuthComponentTest extends TestCase { */ public function setUp() { $this->componentConfig = [ - 'authPath' => Plugin::path('TinyAuth') . 'tests' . DS . 'test_files' . DS + 'filePath' => Plugin::path('TinyAuth') . 'tests' . DS . 'test_files' . DS ]; } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index ac6f82e1..f4649478 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -55,7 +55,6 @@ Cake\Cache\Cache::config($cache); -//needed? Cake\Core\Plugin::load('TinyAuth', ['path' => ROOT . DS, 'autoload' => true]); // Ensure default test connection is defined diff --git a/tests/test_files/authentication.ini b/tests/test_files/auth-allow.ini similarity index 100% rename from tests/test_files/authentication.ini rename to tests/test_files/auth-allow.ini From f7df253b61f967a9e4620f8b9da0543d1675c392 Mon Sep 17 00:00:00 2001 From: Mark Scherer Date: Sat, 27 Aug 2016 01:22:21 +0200 Subject: [PATCH 08/15] add default ini template file --- config/auth-allow.default.ini | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 config/auth-allow.default.ini diff --git a/config/auth-allow.default.ini b/config/auth-allow.default.ini new file mode 100644 index 00000000..7319f129 --- /dev/null +++ b/config/auth-allow.default.ini @@ -0,0 +1,5 @@ +Users = index, view ; Everyone can access index and view actions +admin/Users = index ; Only index action is public for admin prefix +Extras.Offers = *; All Offers controller actions in Extras plugin are public + +; ... From 4b24004ea29ac3f7afda5b2fb3fe2a47e0409950 Mon Sep 17 00:00:00 2001 From: Mark Scherer Date: Sat, 27 Aug 2016 01:57:31 +0200 Subject: [PATCH 09/15] Docs. --- docs/Authentication.md | 173 +++------------------ docs/Authorization.md | 105 ++++++++++++- src/Auth/TinyAuthorize.php | 4 +- src/Controller/Component/AuthComponent.php | 8 +- 4 files changed, 127 insertions(+), 163 deletions(-) diff --git a/docs/Authentication.md b/docs/Authentication.md index 809167aa..9d84128d 100644 --- a/docs/Authentication.md +++ b/docs/Authentication.md @@ -18,168 +18,54 @@ Authentication is set up in your controller's `initialize` method: ```php // src/Controller/AppController -public function initialize() - parent::beforeFilter($event); - +public function initialize() { $this->loadComponent('TinyAuth.Auth', [ - '' => [ - 'TinyAuth.Tiny' => [ - 'multiRole' => false - ] - ] + 'filePath' => ... ]); } ``` -> Please note that TinyAuth Authorize can be used in combination with any -> [CakePHP Authentication Type](http://book.cakephp.org/3.0/en/controllers/components/authentication.html#choosing-an-authentication-type). - -## Roles - -TinyAuth requires the presence of roles to function so create those first using -one of the following two options. - -### Configure based roles - -Define your roles in a Configure array if you want to prevent database role -lookups, for example: - -```php -// config/app_custom.php - -/** -* Optionally define constants for easy referencing throughout your code -*/ -define('ROLE_USER', 1); -define('ROLE_ADMIN', 2); -define('ROLE_SUPERADMIN', 9); - -return [ - 'Roles' => [ - 'user' => ROLE_USER, - 'admin' => ROLE_ADMIN, - 'superadmin' => ROLE_SUPERADMIN - ] -]; -``` - -### Database roles -When you choose to store your roles in the database TinyAuth expects a table -named ``roles``. If you prefer to use another table name simply specify it using the -``rolesTable`` configuration option. - ->**Note:** make sure to add an "alias" field to your roles table (used as slug -identifier in the acl.ini file) - -Example of a record from a valid roles table: - -```php -'id' => '11' -'name' => 'User' -'description' => 'Basic authenticated user' -'alias' => 'user' -'created' => '2010-01-07 03:36:33' -'modified' => '2010-01-07 03:36:33' -``` - -> Please note that you do NOT need Configure based roles when using database -> roles. Also make sure to remove (or rename) existing Configure based roles -> since TinyAuth will always first try to find a matching Configure roles array -> before falling back to using the database. - -## Users - -### Single-role - -When using the single-role-per-user model TinyAuth expects your Users model to -contain an column named ``role_id``. If you prefer to use another column name -simply specify it using the ``roleColumn`` configuration option. +## auth-allow.ini -The ``roleColumn`` option is also used on pivot table in a multi-role setup. - -### Multi-role -When using the multiple-roles-per-user model: - -- your database MUST have a ``roles`` table -- your database MUST have a valid join table (e.g. ``roles_users``). This can be overridden with the ``pivotTable`` option. -- the configuration option ``multiRole`` MUST be set to ``true`` - -Example of a record from a valid join table: - -```php -'id' => 1 -'user_id' => 1 -'role_id' => 1 -``` - -If you want to have default database tables here for multi-role auth, you can use the plugin shipped Migrations file: -``` -bin/cake migrations migrate -p TinyAuth -``` -Alternatively you can copy and paste this migration file to your `app/Config` folder and adjust the fields and table names and then use that modified version instead. - -## acl.ini - -TinyAuth expects an ``acl.ini`` file in your config directory. -Use it to specify who gets access to which resources. +TinyAuth expects an ``auth-allow.ini`` file in your config directory. +Use it to specify what actions are not protected by authentication. The section key syntax follows the CakePHP naming convention for plugins. Make sure to create an entry for each action you want to expose and use: -- one or more role names (groups granted access) -- the ``*`` wildcard to allow access to all authenticated users +- one or more action names +- the ``*`` wildcard to allow access to all actions of that controller ```ini ; ---------------------------------------------------------- -; Userscontroller +; UsersController ; ---------------------------------------------------------- -[Users] -index = user, admin, undefined-role -edit, view = user, admin -* = admin +Users = index ; ---------------------------------------------------------- ; UsersController using /api prefixed route ; ---------------------------------------------------------- -[api/Users] -view = user -* = admin +api/Users = index, view, edit ; ---------------------------------------------------------- ; UsersController using /admin prefixed route ; ---------------------------------------------------------- -[admin/Users] -* = admin +admin/Users = * ; ---------------------------------------------------------- ; AccountsController in plugin named Accounts ; ---------------------------------------------------------- -[Accounts.Accounts] -view, edit = user -* = admin +Accounts.Accounts = view, edit ; ---------------------------------------------------------- ; AccountsController in plugin named Accounts using /admin ; prefixed route ; ---------------------------------------------------------- -[Accounts.admin/Accounts] -* = admin -; ---------------------------------------------------------- -; CompaniesController in plugin named Accounts -; ---------------------------------------------------------- -[Accounts.Companies] -view, edit = user -* = admin -; ---------------------------------------------------------- -; CompaniesController in plugin named Accounts using /admin -; prefixed route -; ---------------------------------------------------------- -[Accounts.admin/Companies] -* = admin +Accounts.admin/Accounts = index ``` ## Caching TinyAuth makes heavy use of caching to achieve optimal performance. -You may however want to disable caching while developing RBAC to prevent +You may however want to disable caching while developing to prevent confusing (outdated) results. To disable caching either: @@ -188,37 +74,20 @@ To disable caching either: - use the example below to disable caching automatically for CakePHP debug mode ```php -'TinyAuth' => [ +$this->loadComponent('TinyAuth.Auth', [ 'autoClearCache' => Configure::read('debug') -] +)] ``` ## Configuration -TinyAuth supports the following configuration options. +TinyAuth AuthComponent supports the following configuration options. Option | Type | Description :----- | :--- | :---------- -roleColumn|string|Name of column in user table holding role id (used for foreign key in users table in a single role per user setup, or in the pivot table on multi-roles setup) -userColumn|string|Name of column in pivot table holding role id (only used in pivot table on multi-roles setup) -aliasColumn|string|Name of the column for the alias in the role table -idColumn|string|Name of the ID Column in users table -rolesTable|string|Name of Configure key holding all available roles OR class name of roles database table -usersTable|string|Class name of the users table. -pivotTable|string|Name of the pivot table, for a multi-group setup. -rolesTablePlugin|string|Name of the plugin for the roles table, if any. -pivotTablePlugin|string|Name of the plugin for the pivot table, if any. -multiRole|bool|True will enable multi-role/HABTM authorization (requires a valid join table) -superAdminRole|int|Id of the super admin role. Users with this role will have access to ALL resources. -superAdmin|int or string|Id/name of the super admin. Users with this id/name will have access to ALL resources.null/0/"0" take disable it -superAdminColumn|string|Column of super admin in user table.default use idColumn option -authorizeByPrefix|bool|If prefixed routes should be auto-handled by their matching role name. -prefixes|array|A list of authorizeByPrefix handled prefixes. -allowUser|bool|True will give authenticated users access to all resources except those using the `adminPrefix` -adminPrefix|string|Name of the prefix used for admin pages. Defaults to admin. autoClearCache|bool|True will generate a new ACL cache file every time. -aclPath|string|Full path to the acl.ini. Defaults to `ROOT . DS . 'config' . DS`. - +filePath|string|Full path to the acl.ini. Defaults to `ROOT . DS . 'config' . DS`. +file|string|Name of the ini file. Defaults to `acl.ini`. +cache|string|Cache type. Defaults to `_cake_core_`. +cacheKey|string|Cache key. Defaults to `tiny_auth_allow`. -## Auth user data -For reading auth user data take a look at [Tools plugin AuthUser component/helper](https://github.com/dereuromark/cakephp-tools/blob/master/docs/Auth/Auth.md). diff --git a/docs/Authorization.md b/docs/Authorization.md index 227cf3e1..5ebab379 100644 --- a/docs/Authorization.md +++ b/docs/Authorization.md @@ -29,12 +29,103 @@ public function beforeFilter(Event $event) { parent::beforeFilter($event); $this->loadComponent('Auth', [ - 'authFile' => ... + 'authorize' => [ + 'TinyAuth.Tiny' => [ + 'multiRole' => false + ] + ] ]); } ``` -## authentication.ini +> Please note that TinyAuth Authorize can be used in combination with any +> [CakePHP Authentication Type](http://book.cakephp.org/3.0/en/controllers/components/authentication.html#choosing-an-authentication-type). + +## Roles + +TinyAuth requires the presence of roles to function so create those first using +one of the following two options. + +### Configure based roles + +Define your roles in a Configure array if you want to prevent database role +lookups, for example: + +```php +// config/app_custom.php + +/** +* Optionally define constants for easy referencing throughout your code +*/ +define('ROLE_USER', 1); +define('ROLE_ADMIN', 2); +define('ROLE_SUPERADMIN', 9); + +return [ + 'Roles' => [ + 'user' => ROLE_USER, + 'admin' => ROLE_ADMIN, + 'superadmin' => ROLE_SUPERADMIN + ] +]; +``` + +### Database roles +When you choose to store your roles in the database TinyAuth expects a table +named ``roles``. If you prefer to use another table name simply specify it using the +``rolesTable`` configuration option. + +>**Note:** make sure to add an "alias" field to your roles table (used as slug +identifier in the acl.ini file) + +Example of a record from a valid roles table: + +```php +'id' => '11' +'name' => 'User' +'description' => 'Basic authenticated user' +'alias' => 'user' +'created' => '2010-01-07 03:36:33' +'modified' => '2010-01-07 03:36:33' +``` + +> Please note that you do NOT need Configure based roles when using database +> roles. Also make sure to remove (or rename) existing Configure based roles +> since TinyAuth will always first try to find a matching Configure roles array +> before falling back to using the database. + +## Users + +### Single-role + +When using the single-role-per-user model TinyAuth expects your Users model to +contain an column named ``role_id``. If you prefer to use another column name +simply specify it using the ``roleColumn`` configuration option. + +The ``roleColumn`` option is also used on pivot table in a multi-role setup. + +### Multi-role +When using the multiple-roles-per-user model: + +- your database MUST have a ``roles`` table +- your database MUST have a valid join table (e.g. ``roles_users``). This can be overridden with the ``pivotTable`` option. +- the configuration option ``multiRole`` MUST be set to ``true`` + +Example of a record from a valid join table: + +```php +'id' => 1 +'user_id' => 1 +'role_id' => 1 +``` + +If you want to have default database tables here for multi-role auth, you can use the plugin shipped Migrations file: +``` +bin/cake migrations migrate -p TinyAuth +``` +Alternatively you can copy and paste this migration file to your `app/Config` folder and adjust the fields and table names and then use that modified version instead. + +## acl.ini TinyAuth expects an ``acl.ini`` file in your config directory. Use it to specify who gets access to which resources. @@ -48,7 +139,7 @@ Make sure to create an entry for each action you want to expose and use: ```ini ; ---------------------------------------------------------- -; Userscontroller +; UsersController ; ---------------------------------------------------------- [Users] index = user, admin, undefined-role @@ -94,6 +185,7 @@ view, edit = user ## Caching TinyAuth makes heavy use of caching to achieve optimal performance. +By default it will not use caching in debug mode, though. You may however want to disable caching while developing RBAC to prevent confusing (outdated) results. @@ -111,7 +203,7 @@ To disable caching either: ## Configuration -TinyAuth supports the following configuration options. +TinyAuthorize adapter supports the following configuration options. Option | Type | Description :----- | :--- | :---------- @@ -133,7 +225,10 @@ prefixes|array|A list of authorizeByPrefix handled prefixes. allowUser|bool|True will give authenticated users access to all resources except those using the `adminPrefix` adminPrefix|string|Name of the prefix used for admin pages. Defaults to admin. autoClearCache|bool|True will generate a new ACL cache file every time. -aclPath|string|Full path to the acl.ini. Defaults to `ROOT . DS . 'config' . DS`. +filePath|string|Full path to the acl.ini. Defaults to `ROOT . DS . 'config' . DS`. +file|string|Name of the ini file. Defaults to `acl.ini`. +cache|string|Cache type. Defaults to `_cake_core_`. +cacheKey|string|Cache key. Defaults to `tiny_auth_acl`. ## Auth user data diff --git a/src/Auth/TinyAuthorize.php b/src/Auth/TinyAuthorize.php index fc3acad6..9e1dd232 100644 --- a/src/Auth/TinyAuthorize.php +++ b/src/Auth/TinyAuthorize.php @@ -69,9 +69,9 @@ class TinyAuthorize extends BaseAuthorize { 'adminPrefix' => 'admin', // name of the admin prefix route (only used when allowUser is enabled) 'cache' => '_cake_core_', 'cacheKey' => 'tiny_auth_acl', - 'autoClearCache' => false, // usually done by Cache automatically in debug mode, + 'autoClearCache' => false, // Set to true to delete cache automatically in debug mode 'aclPath' => null, // @deprecated Use filePath - 'filePath' => null, // possible to locate ini file at given path e.g. Plugin::configPath('Admin') + 'filePath' => null, // Possible to locate ini file at given path e.g. Plugin::configPath('Admin') 'file' => 'acl.ini', ]; diff --git a/src/Controller/Component/AuthComponent.php b/src/Controller/Component/AuthComponent.php index 9c9ab97b..4ffd18ef 100644 --- a/src/Controller/Component/AuthComponent.php +++ b/src/Controller/Component/AuthComponent.php @@ -20,9 +20,9 @@ class AuthComponent extends CakeAuthComponent { */ protected $_defaultTinyAuthConfig = [ 'cache' => '_cake_core_', - 'cacheKey' => 'tiny_auth_authentication', - 'autoClearCache' => false, // usually done by Cache automatically in debug mode, - 'filePath' => null, // possible to locate ini file at given path e.g. Plugin::configPath('Admin') + 'cacheKey' => 'tiny_auth_allow', + 'autoClearCache' => false, // Set to true to delete cache automatically in debug mode + 'filePath' => null, // Possible to locate ini file at given path e.g. Plugin::configPath('Admin') 'file' => 'auth-allow.ini', ]; @@ -90,7 +90,7 @@ protected function _getAuth($path = null) { if ($path === null) { $path = ROOT . DS . 'config' . DS; } -debug($path); + if ($this->_config['autoClearCache'] && Configure::read('debug')) { Cache::delete($this->_config['cacheKey'], $this->_config['cache']); } From b3ba6d23aedf81320c2678d886306b7116f2aef4 Mon Sep 17 00:00:00 2001 From: Mark Scherer Date: Sat, 27 Aug 2016 01:59:18 +0200 Subject: [PATCH 10/15] Fix links --- docs/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/README.md b/docs/README.md index f60d36f8..de7b9af7 100644 --- a/docs/README.md +++ b/docs/README.md @@ -18,7 +18,7 @@ This is done via TinyAuth Auth Component. The component plays well together with the adapter (see below). If you do not have any roles and either all are logged in or not logged in you can also use this stand-alone to make certain pages public. -See [Authentication](/docs/Authentication) docs. +See [Authentication](Authentication.md) docs. ## Authorization For this we have a TinyAuth Authorize adapter. @@ -27,4 +27,4 @@ The adapter plays well together with the component above. But if you prefer to control the action whitelisting for authentication via code and `$this->Auth->allow()` calls, you can also just use this adapter stand-alone for the ACL part of your application. -See [Authorization](/docs/Authorization) docs. +See [Authorization](Authorization.md) docs. From 022d6b64cc624e19d6fe54a9cff58311fd8803f4 Mon Sep 17 00:00:00 2001 From: Mark Scherer Date: Sat, 27 Aug 2016 02:03:05 +0200 Subject: [PATCH 11/15] Docs. --- README.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 31f68ba4..5952ec17 100644 --- a/README.md +++ b/README.md @@ -8,26 +8,32 @@ [![Total Downloads](https://poser.pugx.org/dereuromark/cakephp-tinyauth/d/total.svg)](https://packagist.org/packages/dereuromark/cakephp-tinyauth) [![Coding Standards](https://img.shields.io/badge/cs-PSR--2--R-yellow.svg)](https://github.com/php-fig-rectified/fig-rectified-standards) -A CakePHP 3.x plugin to handle user authorization the easy way. +A CakePHP 3.x plugin to handle authentication and user authorization the easy way. ## Demo +See http://sandbox3.dereuromark.de/auth-sandbox + +### auth-allow.ini +```ini +Users = index,view +PluginName.SomeController = * +``` + +### acl.ini ```ini [Users] index = * add,edit = user, mod -* = admin [admin/Users] * = admin ``` -See http://sandbox3.dereuromark.de/auth-sandbox - ## How to include Installing the plugin is pretty much as with every other CakePHP Plugin. ```bash -composer require dereuromark/cakephp-tinyauth:dev-master +composer require dereuromark/cakephp-tinyauth ``` Then, to load the plugin either run the following command: From 70b10a42737379dfb111d8209056cf54ebe45178 Mon Sep 17 00:00:00 2001 From: Mark Scherer Date: Sat, 27 Aug 2016 02:04:14 +0200 Subject: [PATCH 12/15] Docs. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5952ec17..8674b714 100644 --- a/README.md +++ b/README.md @@ -23,14 +23,14 @@ PluginName.SomeController = * ```ini [Users] index = * -add,edit = user, mod +add,edit = user,mod [admin/Users] * = admin ``` ## How to include -Installing the plugin is pretty much as with every other CakePHP Plugin. +Installing the plugin is pretty much as with every other CakePHP plugin: ```bash composer require dereuromark/cakephp-tinyauth @@ -51,7 +51,7 @@ Plugin::load('TinyAuth'); That's it. It should be up and running. ## Docs -See [Docs](/docs). +For setup and usage see [Docs](/docs). Also note the original [blog post](http://www.dereuromark.de/2011/12/18/tinyauth-the-fastest-and-easiest-authorization-for-cake2/) and how it all started. From d0263887303071c2da0e9154311f05ecb1d7b67d Mon Sep 17 00:00:00 2001 From: Mark Scherer Date: Sat, 27 Aug 2016 02:21:38 +0200 Subject: [PATCH 13/15] Add test for allow(). --- composer.json | 3 +- config/auth-allow.default.ini | 2 +- src/Controller/Component/AuthComponent.php | 12 ++++---- tests/TestApp/Controller/OffersController.php | 14 ++++++++++ .../Component/AuthComponentTest.php | 28 ++++++++++++++++++- tests/test_files/auth-allow.ini | 2 +- 6 files changed, 51 insertions(+), 10 deletions(-) create mode 100644 tests/TestApp/Controller/OffersController.php diff --git a/composer.json b/composer.json index a0ac7737..1f03a33b 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,8 @@ "autoload-dev": { "psr-4": { "TinyAuth\\Test\\": "tests", - "Cake\\Test\\": "vendor/cakephp/cakephp/tests" + "Cake\\Test\\": "vendor/cakephp/cakephp/tests", + "TestApp\\": "tests/TestApp" } }, "extra": { diff --git a/config/auth-allow.default.ini b/config/auth-allow.default.ini index 7319f129..92d402c8 100644 --- a/config/auth-allow.default.ini +++ b/config/auth-allow.default.ini @@ -1,5 +1,5 @@ Users = index, view ; Everyone can access index and view actions admin/Users = index ; Only index action is public for admin prefix -Extras.Offers = *; All Offers controller actions in Extras plugin are public +Extras.Offers = * ; All Offers controller actions in Extras plugin are public ; ... diff --git a/src/Controller/Component/AuthComponent.php b/src/Controller/Component/AuthComponent.php index 4ffd18ef..0e938cf9 100644 --- a/src/Controller/Component/AuthComponent.php +++ b/src/Controller/Component/AuthComponent.php @@ -58,23 +58,23 @@ protected function _prepareAuthentication() { $authentication = $this->_getAuth($this->_config['filePath']); $params = $this->request->params; - foreach ($authentication as $array) { - if ($params['plugin'] && $params['plugin'] !== $array['plugin']) { + foreach ($authentication as $rule) { + if ($params['plugin'] && $params['plugin'] !== $rule['plugin']) { continue; } - if (!empty($params['prefix']) && $params['prefix'] !== $array['prefix']) { + if (!empty($params['prefix']) && $params['prefix'] !== $rule['prefix']) { continue; } - if ($params['controller'] !== $array['controller']) { + if ($params['controller'] !== $rule['controller']) { continue; } - if ($array['actions'] === []) { + if ($rule['actions'] === []) { $this->allow(); return; } - $this->allow($array['actions']); + $this->allow($rule['actions']); } } diff --git a/tests/TestApp/Controller/OffersController.php b/tests/TestApp/Controller/OffersController.php new file mode 100644 index 00000000..1e84821c --- /dev/null +++ b/tests/TestApp/Controller/OffersController.php @@ -0,0 +1,14 @@ +componentConfig = [ - 'filePath' => Plugin::path('TinyAuth') . 'tests' . DS . 'test_files' . DS + 'filePath' => Plugin::path('TinyAuth') . 'tests' . DS . 'test_files' . DS, + 'autoClearCache' => true, ]; } @@ -58,6 +60,30 @@ public function testValid() { $this->assertNull($response); } + /** + * @return void + */ + public function testValidAnyAction() { + $request = new Request(['params' => [ + 'controller' => 'Offers', + 'action' => 'index', + 'plugin' => 'Extras', + '_ext' => null, + 'pass' => [1] + ]]); + $controller = new OffersController($request); + + $registry = new ComponentRegistry($controller); + $this->AuthComponent = new AuthComponent($registry, $this->componentConfig); + + $config = []; + $this->AuthComponent->initialize($config); + + $event = new Event('Controller.startup', $controller); + $response = $this->AuthComponent->startup($event); + $this->assertNull($response); + } + /** * @return void */ diff --git a/tests/test_files/auth-allow.ini b/tests/test_files/auth-allow.ini index 7319f129..92d402c8 100644 --- a/tests/test_files/auth-allow.ini +++ b/tests/test_files/auth-allow.ini @@ -1,5 +1,5 @@ Users = index, view ; Everyone can access index and view actions admin/Users = index ; Only index action is public for admin prefix -Extras.Offers = *; All Offers controller actions in Extras plugin are public +Extras.Offers = * ; All Offers controller actions in Extras plugin are public ; ... From 54628e4eb5a15eeb8d9d44716f2b8f9148115442 Mon Sep 17 00:00:00 2001 From: Mark Scherer Date: Sat, 27 Aug 2016 02:30:35 +0200 Subject: [PATCH 14/15] Typo. --- docs/Authentication.md | 2 +- docs/Authorization.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Authentication.md b/docs/Authentication.md index 9d84128d..b901c9b4 100644 --- a/docs/Authentication.md +++ b/docs/Authentication.md @@ -87,7 +87,7 @@ Option | Type | Description :----- | :--- | :---------- autoClearCache|bool|True will generate a new ACL cache file every time. filePath|string|Full path to the acl.ini. Defaults to `ROOT . DS . 'config' . DS`. -file|string|Name of the ini file. Defaults to `acl.ini`. +file|string|Name of the INI file. Defaults to `acl.ini`. cache|string|Cache type. Defaults to `_cake_core_`. cacheKey|string|Cache key. Defaults to `tiny_auth_allow`. diff --git a/docs/Authorization.md b/docs/Authorization.md index 5ebab379..23666014 100644 --- a/docs/Authorization.md +++ b/docs/Authorization.md @@ -226,7 +226,7 @@ allowUser|bool|True will give authenticated users access to all resources except adminPrefix|string|Name of the prefix used for admin pages. Defaults to admin. autoClearCache|bool|True will generate a new ACL cache file every time. filePath|string|Full path to the acl.ini. Defaults to `ROOT . DS . 'config' . DS`. -file|string|Name of the ini file. Defaults to `acl.ini`. +file|string|Name of the INI file. Defaults to `acl.ini`. cache|string|Cache type. Defaults to `_cake_core_`. cacheKey|string|Cache key. Defaults to `tiny_auth_acl`. From 3d39c74c0b27de815f5c10adae55423833014207 Mon Sep 17 00:00:00 2001 From: Draniu Date: Sat, 27 Aug 2016 14:24:08 +0200 Subject: [PATCH 15/15] Update Authentication.md fix defaults filename --- docs/Authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Authentication.md b/docs/Authentication.md index b901c9b4..be53622e 100644 --- a/docs/Authentication.md +++ b/docs/Authentication.md @@ -87,7 +87,7 @@ Option | Type | Description :----- | :--- | :---------- autoClearCache|bool|True will generate a new ACL cache file every time. filePath|string|Full path to the acl.ini. Defaults to `ROOT . DS . 'config' . DS`. -file|string|Name of the INI file. Defaults to `acl.ini`. +file|string|Name of the INI file. Defaults to `auth-allow.ini`. cache|string|Cache type. Defaults to `_cake_core_`. cacheKey|string|Cache key. Defaults to `tiny_auth_allow`.