diff --git a/Config/acl_manager.php b/Config/acl_manager.php new file mode 100755 index 0000000..b850a28 --- /dev/null +++ b/Config/acl_manager.php @@ -0,0 +1,55 @@ + array('Role', 'User'), + + /** + * Aliases to write into ARO table + */ + // 'aro_aliases' => array('Group' => 'name', 'User' => 'username'), + + /** + * Limit used to paginate AROs + * Replace {alias} with ARO alias + * '{alias}' => array('limit' => 3) + */ + // 'Role' => array('limit' => 3), + + /** + * Routing Prefix + * Set the prefix you would like to restrict the plugin to + * @see Configure::read('Routing.prefixes') + */ + // 'prefix' => 'admin', + + /** + * Ugly identation? + * Turn off when using CSS + */ + 'uglyIdent' => true, + + /** + * Actions to ignore when looking for new ACOs + * Format: 'action', 'Controller/action' or 'Plugin.Controller/action' + */ + 'ignoreActions' => array('isAuthorized'), + + /** + * List of ARO models to load + * Use only if AclManager.aros aliases are different from model name + */ + // 'models' => array('Group', 'Customer'), + + 'version' => "1.3.1" +); diff --git a/Config/bootstrap.php b/Config/bootstrap.php index e6f73e2..4387f98 100755 --- a/Config/bootstrap.php +++ b/Config/bootstrap.php @@ -13,55 +13,20 @@ * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ -/** - * List of AROs (Class aliases) - * Order is important! Parent to Children - */ -Configure::write('AclManager.aros', array('Role', 'User')); - -/** - * Limit used to paginate AROs - * Replace {alias} with ARO alias - * Configure::write('AclManager.{alias}.limit', 3) - */ -// Configure::write('AclManager.Role.limit', 3); - -/** - * Routing Prefix - * Set the prefix you would like to restrict the plugin to - * @see Configure::read('Routing.prefixes') - */ -// Configure::write('AclManager.prefix', 'admin'); - -/** - * Ugly identation? - * Turn off when using CSS - */ -Configure::write('AclManager.uglyIdent', true); - -/** - * Actions to ignore when looking for new ACOs - * Format: 'action', 'Controller/action' or 'Plugin.Controller/action' - */ -Configure::write('AclManager.ignoreActions', array('isAuthorized')); +// Default to Plugin config which can be overwritten by local app config +Configure::load('AclManager.acl_manager'); +$defaultConfig = Configure::read('AclManager'); -/** - * List of ARO models to load - * Use only if AclManager.aros aliases are different than model name - */ -// Configure::write('AclManager.models', array('Group', 'Customer')); +$config = array(); +// Local app config +if (file_exists(APP . 'Config' . DS . 'acl_manager.php')) { + Configure::load('acl_manager', 'default', false); + $config = Configure::read('AclManager'); +} -/** - * END OF USER SETTINGS - */ +$config = array_merge($defaultConfig, $config); +Configure::write('AclManager', $config); -Configure::write("AclManager.version", "1.2.5"); -if (!is_array(Configure::read('AclManager.aros'))) { - Configure::write('AclManager.aros', array(Configure::read('AclManager.aros'))); -} -if (!is_array(Configure::read('AclManager.ignoreActions'))) { - Configure::write('AclManager.ignoreActions', array(Configure::read('AclManager.ignoreActions'))); -} if (!Configure::read('AclManager.models')) { Configure::write('AclManager.models', Configure::read('AclManager.aros')); } diff --git a/Controller/AclController.php b/Controller/AclController.php index 4166a9e..d16283d 100755 --- a/Controller/AclController.php +++ b/Controller/AclController.php @@ -12,7 +12,7 @@ * @link http://github.com/FMCorz/AclManager * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ - + class AclController extends AclManagerAppController { public $paginate = array(); @@ -24,7 +24,7 @@ class AclController extends AclManagerAppController { */ public function beforeFilter() { parent::beforeFilter(); - + /** * Loading required Model */ @@ -32,7 +32,7 @@ public function beforeFilter() { foreach ($aros as $aro) { $this->loadModel($aro); } - + /** * Pagination */ @@ -56,7 +56,7 @@ public function drop() { $this->Session->setFlash(__("Both ACOs and AROs have been dropped")); $this->redirect(array("action" => "index")); } - + /** * Delete all permissions */ @@ -98,9 +98,9 @@ public function permissions() { $this->Acl->deny($node, $action); } } - } + } } - + $model = isset($this->request->params['named']['aro']) ? $this->request->params['named']['aro'] : null; if (!$model || !in_array($model, Configure::read('AclManager.aros'))) { $model = Configure::read('AclManager.aros'); @@ -110,18 +110,42 @@ public function permissions() { $Aro = $this->{$model}; $aros = $this->paginate($Aro->alias); $permKeys = $this->_getKeys(); - + /** * Build permissions info */ - $this->acos = $acos = $this->Acl->Aco->find('all', array('order' => 'Aco.lft ASC', 'recursive' => 1)); + $aroRecords = $this->Acl->Aro->find('all', array( + 'conditions' => array( + 'Aro.model' => $model, + 'Aro.foreign_key' => Hash::extract($aros, '{n}.{s}.id') + ), + 'recursive' => -1 + )); + $aroIds = implode(',', Hash::extract($aroRecords, '{n}.Aro.id')); + $acos = $this->Acl->Aco->query( + "SELECT * FROM acos as Aco + LEFT JOIN aros_acos as Permission on Permission.aco_id = Aco.id AND + Permission.aro_id IN({$aroIds}) + ORDER BY Aco.lft ASC + " + ); + // add Aro key along side each Aco + foreach($acos as $key => $row) { + $extractedAros = Hash::extract($aroRecords, '{n}.Aro'); + if (count($aroRecords) == 1) { + $acos[$key]['Aro'] = array_shift($extractedAros); + } else { + $acos[$key]['Aro'] = $extractedAros; + } + } + + $this->acos = $acos; $perms = array(); $parents = array(); foreach ($acos as $key => $data) { $aco =& $acos[$key]; - $aco = array('Aco' => $data['Aco'], 'Aro' => $data['Aro'], 'Action' => array()); $id = $aco['Aco']['id']; - + // Generate path if ($aco['Aco']['parent_id'] && isset($parents[$aco['Aco']['parent_id']])) { $parents[$id] = $parents[$aco['Aco']['parent_id']] . '/' . $aco['Aco']['alias']; @@ -135,7 +159,7 @@ public function permissions() { foreach($aros as $aro) { $aroId = $aro[$Aro->alias][$Aro->primaryKey]; $evaluate = $this->_evaluate_permissions($permKeys, array('id' => $aroId, 'alias' => $Aro->alias), $aco, $key); - + $perms[str_replace('/', ':', $acoNode)][$Aro->alias . ":" . $aroId . '-inherit'] = $evaluate['inherited']; $perms[str_replace('/', ':', $acoNode)][$Aro->alias . ":" . $aroId] = $evaluate['allowed']; } @@ -144,21 +168,21 @@ public function permissions() { $this->request->data = array('Perms' => $perms); $this->set('aroAlias', $Aro->alias); $this->set('aroDisplayField', $Aro->displayField); + $this->set('aroList', array_values($this->{$Aro->alias}->find('list'))); $this->set(compact('acos', 'aros')); } - + /** * Recursive function to find permissions avoiding slow $this->Acl->check(). */ - private function _evaluate_permissions($permKeys, $aro, $aco, $aco_index) { - $permissions = Set::extract("/Aro[model={$aro['alias']}][foreign_key={$aro['id']}]/Permission/.", $aco); - $permissions = array_shift($permissions); - + private function _evaluate_permissions($permKeys, $aro, $aco, $aco_index) { + $permissions = $aco['Permission']; + $allowed = false; $inherited = false; $inheritedPerms = array(); $allowedPerms = array(); - + /** * Manually checking permission * Part of this logic comes from DbAcl::check() @@ -177,7 +201,7 @@ private function _evaluate_permissions($permKeys, $aro, $aco, $aco_index) { $inheritedPerms[$key] = 0; } } - + if (count($allowedPerms) === count($permKeys)) { $allowed = true; } elseif (count($inheritedPerms) === count($permKeys)) { @@ -193,7 +217,7 @@ private function _evaluate_permissions($permKeys, $aro, $aco, $aco_index) { } else { /** - * Do not use Set::extract here. First of all it is terribly slow, + * Do not use Set::extract here. First of all it is terribly slow, * besides this we need the aco array index ($key) to cache are result. */ foreach ($this->acos as $key => $a) { @@ -206,19 +230,19 @@ private function _evaluate_permissions($permKeys, $aro, $aco, $aco_index) { if (isset($parent_aco['evaluated'][$aro['id']])) { return $parent_aco['evaluated'][$aro['id']]; } - + // Perform lookup of parent aco $evaluate = $this->_evaluate_permissions($permKeys, $aro, $parent_aco, $key); - + // Store result in acos array so we need less recursion for the next lookup $this->acos[$key]['evaluated'][$aro['id']] = $evaluate; $this->acos[$key]['evaluated'][$aro['id']]['inherited'] = true; - + $allowed = $evaluate['allowed']; } $inherited = true; } - + return array( 'allowed' => $allowed, 'inherited' => $inherited, @@ -230,10 +254,10 @@ private function _evaluate_permissions($permKeys, $aro, $aco, $aco_index) { * Sets the missing actions in the database */ public function update_acos() { - + $count = 0; $knownAcos = $this->_getAcos(); - + // Root node $aco = $this->_action(array(), ''); if (!$rootNode = $this->Acl->Aco->node($aco)) { @@ -241,17 +265,17 @@ public function update_acos() { $count++; } $knownAcos = $this->_removeActionFromAcos($knownAcos, $aco); - + // Loop around each controller and its actions $allActions = $this->_getActions(); foreach ($allActions as $controller => $actions) { if (empty($actions)) { continue; } - + $parentNode = $rootNode; list($plugin, $controller) = pluginSplit($controller); - + // Plugin $aco = $this->_action(array('plugin' => $plugin), '/:plugin/'); $aco = rtrim($aco, '/'); // Remove trailing slash @@ -262,7 +286,7 @@ public function update_acos() { } $parentNode = $newNode; $knownAcos = $this->_removeActionFromAcos($knownAcos, $aco); - + // Controller $aco = $this->_action(array('controller' => $controller, 'plugin' => $plugin), '/:plugin/:controller'); if (!$newNode = $this->Acl->Aco->node($aco)) { @@ -292,7 +316,7 @@ public function update_acos() { $acoIds = Set::extract('/Aco/id', $knownAcos); $this->Acl->Aco->deleteAll(array('Aco.id' => $acoIds)); } - + $this->Session->setFlash(sprintf(__("%d ACOs have been created/updated"), $count)); $this->redirect($this->request->referer()); } @@ -302,22 +326,22 @@ public function update_acos() { * Sets the missing AROs in the database */ public function update_aros() { - + // Debug off to enable redirect Configure::write('debug', 0); - + $count = 0; $type = 'Aro'; - + // Over each ARO Model $objects = Configure::read("AclManager.aros"); foreach ($objects as $object) { - + $Model = $this->{$object}; $items = $Model->find('all'); foreach ($items as $item) { - + $item = $item[$Model->alias]; $Model->create(); $Model->id = $item['id']; @@ -327,7 +351,7 @@ public function update_aros() { } catch (Exception $e) { $node = false; } - + // Node exists if ($node) { $parent = $Model->parentNode(); @@ -335,7 +359,7 @@ public function update_aros() { $parent = $Model->node($parent, $type); } $parent = isset($parent[0][$type]['id']) ? $parent[0][$type]['id'] : null; - + // Parent is incorrect if ($parent != $node[0][$type]['parent_id']) { // Remove Aro here, otherwise we've got duplicate Aros @@ -344,10 +368,10 @@ public function update_aros() { $node = null; } } - + // Missing Node or incorrect if (empty($node)) { - + // Extracted from AclBehavior::afterSave (and adapted) $parent = $Model->parentNode(); if (!empty($parent)) { @@ -358,7 +382,11 @@ public function update_aros() { 'model' => $Model->name, 'foreign_key' => $Model->id ); - + + if ($alias = Configure::read("AclManager.aro_aliases.{$Model->name}")) { + $data['alias'] = $item[$alias]; + } + // Creating ARO $this->Acl->{$type}->create($data); $this->Acl->{$type}->save(); @@ -366,7 +394,7 @@ public function update_aros() { } } } - + $this->Session->setFlash(sprintf(__("%d AROs have been created"), $count)); $this->redirect($this->request->referer()); } @@ -378,7 +406,7 @@ protected function _action($request = array(), $path = '/:plugin/:controller/:ac $plugin = empty($request['plugin']) ? null : Inflector::camelize($request['plugin']) . '/'; $params = array_merge(array('controller' => null, 'action' => null, 'plugin' => null), $request); $request = new CakeRequest(null, false); - $request->addParams($params); + $request->addParams($params); $authorizer = $this->_getAuthorizer(); return $authorizer->action($request, $path); } @@ -399,12 +427,12 @@ protected function _buildAcoNode($alias, $parent_id = null) { /** * Returns all the Actions found in the Controllers - * + * * Ignores: * - protected and private methods (starting with _) * - Controller methods * - methods matching Configure::read('AclManager.ignoreActions') - * + * * @return array('Controller' => array('action1', 'action2', ... )) */ protected function _getActions() { @@ -413,13 +441,13 @@ protected function _getActions() { foreach($methods as $method) { $ignore[] = $method; } - + $controllers = $this->_getControllers(); $actions = array(); foreach ($controllers as $controller) { - + list($plugin, $name) = pluginSplit($controller); - + $methods = get_class_methods($name . "Controller"); $methods = array_diff($methods, $ignore); foreach ($methods as $key => $method) { @@ -429,7 +457,7 @@ protected function _getActions() { } $actions[$controller] = $methods; } - + return $actions; } @@ -440,10 +468,10 @@ protected function _getAcos() { $acos = $this->Acl->Aco->find('all', array('order' => 'Aco.lft ASC', 'recursive' => -1)); $parents = array(); foreach ($acos as $key => $data) { - + $aco =& $acos[$key]; $id = $aco['Aco']['id']; - + // Generate path if ($aco['Aco']['parent_id'] && isset($parents[$aco['Aco']['parent_id']])) { $parents[$id] = $parents[$aco['Aco']['parent_id']] . '/' . $aco['Aco']['alias']; @@ -467,7 +495,7 @@ protected function _getAuthorizer() { if (!$object instanceOf ActionsAuthorize) { continue; } - $this->_authorizer = $object; + $this->_authorizer = $object; break; } if (empty($this->_authorizer)) { @@ -484,7 +512,7 @@ protected function _getAuthorizer() { * @return array('Controller1', 'Plugin.Controller2') */ protected function _getControllers() { - + // Getting Cake controllers $objects = array('Cake' => array()); $objects['Cake'] = App::objects('Controller'); @@ -492,14 +520,33 @@ protected function _getControllers() { if ($unsetIndex !== false) { unset($objects['Cake'][$unsetIndex]); } - + // App::objects does not return PagesController if (!in_array('PagesController', $objects['Cake'])) { array_unshift($objects['Cake'], 'PagesController'); } - + // Getting Plugins controllers $plugins = CakePlugin::loaded(); + + // remove ignored plugins + $ignore = Configure::read('AclManager.ignoreActions'); + $ignoredPlugins = $ignoredControllers = array(); + foreach($ignore as $path) { + if (stristr($path, '.*')) { + list($plugin, $controllerAction) = pluginSplit($path); + $ignoredPlugins[] = $plugin; + } elseif (stristr($path, '/*')) { + list($plugin, $controller) = pluginSplit($path); + if ($plugin == '') { + $plugin = 'Cake'; + } + $controller = str_ireplace('/*', '', $controller); + $ignoredControllers[$plugin][] = $controller; + } + } + $plugins = array_diff($plugins, $ignoredPlugins); + foreach ($plugins as $plugin) { $objects[$plugin] = App::objects($plugin . '.Controller'); $unsetIndex = array_search($plugin . "AppController", $objects[$plugin]); @@ -513,6 +560,9 @@ protected function _getControllers() { foreach ($objects as $plugin => $controllers) { $controllers = str_replace("Controller", "", $controllers); foreach ($controllers as $controller) { + if (isset($ignoredControllers[$plugin]) && in_array($controller, $ignoredControllers[$plugin])) { + continue; + } if ($plugin !== "Cake") { $controller = $plugin . "." . $controller; } @@ -540,7 +590,7 @@ protected function _getKeys() { } return $newKeys; } - + /** * Returns an array without the corresponding action */ diff --git a/README.md b/README.md index f26ed4d..1815b87 100644 --- a/README.md +++ b/README.md @@ -73,12 +73,12 @@ function isAuthorized($user) { #### Manually -Download the stable branch (https://github.com/FMCorz/AclManager/archive/stable.zip) and paste the content in your `app/Plugin/` directory. +Download the stable branch (https://github.com/houseoftech/cakephp-acl-manager/archive/stable.zip) and paste the content in your `app/Plugin/` directory. #### With Composer 1. [Install composer](http://getcomposer.org/doc/00-intro.md#locally) in the `app/` folder of your project. -2. Add `"fmcorz/acl-manager": "stable"` to your `require` key in your `composer.json` file. ([More about this](http://getcomposer.org/doc/01-basic-usage.md#the-require-key)) +2. Add `"houseoftech/cakephp-acl-manager": "stable"` to your `require` key in your `composer.json` file. ([More about this](http://getcomposer.org/doc/01-basic-usage.md#the-require-key)) 3. Run `php composer.phar install` to install the plugin. [Composer documentation](http://getcomposer.org/doc/) diff --git a/View/Acl/permissions.ctp b/View/Acl/permissions.ctp index 52e80cc..3d7c99c 100755 --- a/View/Acl/permissions.ctp +++ b/View/Acl/permissions.ctp @@ -1,13 +1,33 @@

-

Paginator->counter(array('format' => __('Page %page% of %pages%, showing %current% records out of %count% total, starting on record %start%, ending on %end%'))); ?>

-
- Paginator->prev('<< ' . __('previous'), array(), null, array('class'=>'disabled'));?> - | Paginator->numbers();?> | - Paginator->next(__('next') . ' >>', array(), null, array('class' => 'disabled'));?> + +
+
+ Form->create('Page', array('default' => false));?> + Form->input('group_id', array('id' => 'AroSelector', 'div' => false, 'label' => 'Jump to...', 'options' => $aroList, 'empty' => $aroAlias, 'value' => isset($this->params['named']['page']) ? $this->params['named']['page'] -1 : ''));?> + Form->end(null);?> +
+ +
+ Paginator->pagination(array( + 'modulus' => '4', + 'first_title' => '«', + 'last_title' => '»', + 'prev_title' => '‹ prev', + 'next_title' => 'next ›', + 'ul' => 'pagination pagination-sm pull-right', + ));?> + +

+ Paginator->counter( + '{:count} results, showing {:start} - {:end}' + );?>   +

+
+ Form->create('Perms'); ?> - +
@@ -36,7 +56,21 @@ foreach ($acos as $id => $aco) { $allowed = $this->Form->value("Perms." . str_replace("/", ":", $action) . ".{$aroAlias}:{$aro[$aroAlias]['id']}"); $value = $inherit ? 'inherit' : null; $icon = $this->Html->image(($allowed ? 'test-pass-icon.png' : 'test-fail-icon.png')); ?> - +
Action Form->select("Perms." . str_replace("/", ":", $action) . ".{$aroAlias}:{$aro[$aroAlias]['id']}", array(array('inherit' => __('Inherit'), 'allow' => __('Allow'), 'deny' => __('Deny'))), array('empty' => __('No change'), 'value' => $value)); ?> + Form->input("Perms." . str_replace("/", ":", $action) . ".{$aroAlias}:{$aro[$aroAlias]['id']}", array( + 'options' => array( + 'inherit' => __('Inherit'), + 'allow' => __('Allow'), + 'deny' => __('Deny') + ), + 'empty' => __('No change'), + 'value' => $value, + 'div' => false, + 'label' => false, + 'wrapInput' => false, + 'class' => 'input-sm' + )); ?> +
Form->end(__("Save")); +echo $this->Form->submit(__("Save"), array('class' => 'btn btn-primary')); +echo $this->Form->end(null); ?> -

Paginator->counter(array('format' => __('Page %page% of %pages%, showing %current% records out of %count% total, starting on record %start%, ending on %end%'))); ?>

-
- Paginator->prev('<< ' . __('previous'), array(), null, array('class'=>'disabled'));?> - | Paginator->numbers();?> | - Paginator->next(__('next') . ' >>', array(), null, array('class' => 'disabled'));?> -
+ +Paginator->pagination(array( + 'modulus' => '4', + 'first_title' => '«', + 'last_title' => '»', + 'prev_title' => '‹ prev', + 'next_title' => 'next ›', + 'ul' => 'pagination pagination-sm pull-right', +));?> + +

+ Paginator->counter( + '{:count} results, showing {:start} - {:end}' + );?>   +

+

@@ -75,3 +120,25 @@ echo $this->Form->end(__("Save"));
  • Html->link(__('Drop permissions'), array('action' => 'drop_perms'), array(), __("Do you want to drop all the permissions?")); ?>
  • + + $this->params['prefix'], + 'plugin' => $this->params['plugin'], + 'controller' => $this->params['controller'], + 'action' => $this->params['action'] + ); +?> + + \ No newline at end of file diff --git a/composer.json b/composer.json index 02f3aff..31124da 100644 --- a/composer.json +++ b/composer.json @@ -1,11 +1,14 @@ { - "name": "fmcorz/acl-manager", - "type": "cakephp-plugin", - "description": "AclManager for CakePHP 2.x", - "homepage": "http://github.com/FMCorz/AclManager", - "license": "MIT", - "require": { - "php": ">=5.2.8", - "composer/installers": "*" - } + "name": "houseoftech/cakephp-acl-manager", + "type": "cakephp-plugin", + "description": "AclManager for CakePHP 2.x", + "homepage": "http://github.com/houseoftech/cakephp-acl-manager", + "license": "MIT", + "require": { + "php": ">=5.2.8", + "composer/installers": "*" + }, + "extra": { + "installer-name": "AclManager" + } }