From 64cdf7793cbaf7c5b5e6cc05ef184827b5efa5ec Mon Sep 17 00:00:00 2001 From: Andreas Gohr <gohr@cosmocode.de> Date: Tue, 30 Oct 2018 14:07:15 +0100 Subject: [PATCH] add event to check access to admin plugins This adds a new method that capsulates the access check that has to be done to decide if an admin plugin's page should be shown to the user. The default implementation is the same as before, relying only on the forAdminOnly() method and the users' isadmin or ismanager status. Admin plugins themselves can override the method to do additional checks. In this patch, I added that to the usermanager plugin which will only return true if the current auth backend can list users. However the real idea behind this change is that the new method emits a new event called ADMINPLUGIN_ACCESS_CHECK which would allow plugins to overwrite it. This way it could be possible to give certain user groups access to certain admin plugins without giving them admin or manager permissions. Note: this does not change how the "Admin" link is shown, it still depends on ismanager or isadmin. A plugin as mentioned above would need to influence the display via the MENU_ITEMS_ASSEMBLY event. Note: this only covers the basic access check. Admin plugins may need further adjustments for access to other parts of the plugin (like AJAX components). An additional commit will update this for the bundled plugins. --- inc/Action/Admin.php | 2 +- inc/Ui/Admin.php | 112 ++++++++++++++---------------- inc/pluginutils.php | 2 +- lib/plugins/admin.php | 23 ++++++ lib/plugins/usermanager/admin.php | 18 +++++ 5 files changed, 96 insertions(+), 61 deletions(-) diff --git a/inc/Action/Admin.php b/inc/Action/Admin.php index 8d4305788..eedf7174c 100644 --- a/inc/Action/Admin.php +++ b/inc/Action/Admin.php @@ -41,7 +41,7 @@ class Admin extends AbstractUserAction { if(($page = $INPUT->str('page', '', true)) != '') { /** @var $plugin \DokuWiki_Admin_Plugin */ if($plugin = plugin_getRequestAdminPlugin()) { // FIXME this method does also permission checking - if($plugin->forAdminOnly() && !$INFO['isadmin']) { + if(!$plugin->isAccessibleByCurrentUser()) { throw new ActionException('denied'); } $plugin->handle(); diff --git a/inc/Ui/Admin.php b/inc/Ui/Admin.php index aa3b8b99e..20416e137 100644 --- a/inc/Ui/Admin.php +++ b/inc/Ui/Admin.php @@ -12,6 +12,9 @@ namespace dokuwiki\Ui; */ class Admin extends Ui { + protected $forAdmins = array('usermanager', 'acl', 'extension', 'config', 'styling'); + protected $forManagers = array('revert', 'popularity'); + /** @var array[] */ protected $menu; /** @@ -24,58 +27,30 @@ class Admin extends Ui { echo '<div class="ui-admin">'; echo p_locale_xhtml('admin'); $this->showSecurityCheck(); - $this->showAdminMenu(); - $this->showManagerMenu(); + $this->showMenu('admin'); + $this->showMenu('manager'); $this->showVersion(); - $this->showPluginMenu(); + $this->showMenu('other'); echo '</div>'; } /** - * Display the standard admin tasks + * Show the given menu of available plugins + * + * @param string $type admin|manager|other */ - protected function showAdminMenu() { - /** @var \DokuWiki_Auth_Plugin $auth */ - global $auth; - global $INFO; - - if(!$INFO['isadmin']) return; - - // user manager only if the auth backend supports it - if(!$auth || !$auth->canDo('getUsers') ) { - if(isset($this->menu['usermanager'])) unset($this->menu['usermanager']); + protected function showMenu($type) { + if (!$this->menu[$type]) return; + + if ($type === 'other') { + echo p_locale_xhtml('adminplugins'); + $class = 'admin_plugins'; + } else { + $class = 'admin_tasks'; } - echo '<ul class="admin_tasks">'; - foreach(array('usermanager','acl', 'extension', 'config', 'styling') as $plugin) { - if(!isset($this->menu[$plugin])) continue; - $this->showMenuItem($this->menu[$plugin]); - unset($this->menu[$plugin]); - } - echo '</ul>'; - } - - /** - * Display the standard manager tasks - */ - protected function showManagerMenu() { - echo '<ul class="admin_tasks">'; - foreach(array('revert','popularity') as $plugin) { - if(!isset($this->menu[$plugin])) continue; - $this->showMenuItem($this->menu[$plugin]); - unset($this->menu[$plugin]); - } - echo '</ul>'; - } - - /** - * Display all the remaining plugins - */ - protected function showPluginMenu() { - if(!count($this->menu)) return; - echo p_locale_xhtml('adminplugins'); - echo '<ul class="admin_plugins">'; - foreach ($this->menu as $item) { + echo "<ul class=\"$class\">"; + foreach ($this->menu[$type] as $item) { $this->showMenuItem($item); } echo '</ul>'; @@ -104,7 +79,9 @@ class Admin extends Ui { if(substr($conf['savedir'], 0, 2) !== './') return; echo '<a style="border:none; float:right;" href="http://www.dokuwiki.org/security#web_access_security"> - <img src="' . DOKU_URL . $conf['savedir'] . '/dont-panic-if-you-see-this-in-your-logs-it-means-your-directory-permissions-are-correct.png" alt="Your data directory seems to be protected properly." + <img src="' . DOKU_URL . $conf['savedir'] . + '/dont-panic-if-you-see-this-in-your-logs-it-means-your-directory-permissions-are-correct.png" + alt="Your data directory seems to be protected properly." onerror="this.parentNode.style.display=\'none\'" /></a>'; } @@ -136,19 +113,27 @@ class Admin extends Ui { * @return array list of plugins with their properties */ protected function getPluginList() { - global $INFO; global $conf; $pluginlist = plugin_list('admin'); - $menu = array(); + $menu = ['admin' => [], 'manager' => [], 'other' => []]; + foreach($pluginlist as $p) { /** @var \DokuWiki_Admin_Plugin $obj */ - if(($obj = plugin_load('admin', $p)) === null) continue; + if (($obj = plugin_load('admin', $p)) === null) continue; // check permissions - if($obj->forAdminOnly() && !$INFO['isadmin']) continue; + if (!$obj->isAccessibleByCurrentUser()) continue; + + if (in_array($p, $this->forAdmins, true)) { + $type = 'admin'; + } elseif (in_array($p, $this->forManagers, true)){ + $type = 'manager'; + } else { + $type = 'other'; + } - $menu[$p] = array( + $menu[$type][$p] = array( 'plugin' => $p, 'prompt' => $obj->getMenuText($conf['lang']), 'icon' => $obj->getMenuIcon(), @@ -157,17 +142,26 @@ class Admin extends Ui { } // sort by name, then sort - uasort( - $menu, - function ($a, $b) { - $strcmp = strcasecmp($a['prompt'], $b['prompt']); - if($strcmp != 0) return $strcmp; - if($a['sort'] == $b['sort']) return 0; - return ($a['sort'] < $b['sort']) ? -1 : 1; - } - ); + uasort($menu['admin'], [$this, 'menuSort']); + uasort($menu['manager'], [$this, 'menuSort']); + uasort($menu['other'], [$this, 'menuSort']); return $menu; } + /** + * Custom sorting for admin menu + * + * We sort alphabetically first, then by sort value + * + * @param array $a + * @param array $b + * @return int + */ + protected function menuSort ($a, $b) { + $strcmp = strcasecmp($a['prompt'], $b['prompt']); + if($strcmp != 0) return $strcmp; + if($a['sort'] === $b['sort']) return 0; + return ($a['sort'] < $b['sort']) ? -1 : 1; + } } diff --git a/inc/pluginutils.php b/inc/pluginutils.php index a395be435..0cd113b14 100644 --- a/inc/pluginutils.php +++ b/inc/pluginutils.php @@ -123,7 +123,7 @@ function plugin_getRequestAdminPlugin(){ /** @var $admin_plugin DokuWiki_Admin_Plugin */ $admin_plugin = plugin_load('admin', $page); // verify - if ($admin_plugin && $admin_plugin->forAdminOnly() && !$INFO['isadmin']) { + if ($admin_plugin && !$admin_plugin->isAccessibleByCurrentUser()) { $admin_plugin = null; $INPUT->remove('page'); msg('For admins only',-1); diff --git a/lib/plugins/admin.php b/lib/plugins/admin.php index 4e1cbbb33..770fe582f 100644 --- a/lib/plugins/admin.php +++ b/lib/plugins/admin.php @@ -72,6 +72,29 @@ class DokuWiki_Admin_Plugin extends DokuWiki_Plugin { trigger_error('html() not implemented in '.get_class($this), E_USER_WARNING); } + /** + * Checks if access should be granted to this admin plugin + * + * @return bool true if the current user may access this admin plugin + */ + public function isAccessibleByCurrentUser() { + global $INFO; + + $data['hasAccess'] = false; + + $event = new Doku_Event('ADMINPLUGIN_ACCESS_CHECK', $data); + if($event->advise_before()) { + if ($this->forAdminOnly()) { + $data['hasAccess'] = $INFO['isadmin']; + } else { + $data['hasAccess'] = $INFO['ismanager']; + } + } + $event->advise_after(); + + return $data['hasAccess']; + } + /** * Return true for access only by admins (config:superuser) or false if managers are allowed as well * diff --git a/lib/plugins/usermanager/admin.php b/lib/plugins/usermanager/admin.php index 6d9bf3b20..3148971ce 100644 --- a/lib/plugins/usermanager/admin.php +++ b/lib/plugins/usermanager/admin.php @@ -297,6 +297,24 @@ class admin_plugin_usermanager extends DokuWiki_Admin_Plugin { return true; } + /** + * User Manager is only available if the auth backend supports it + * + * @inheritdoc + * @return bool + */ + public function isAccessibleByCurrentUser() + { + /** @var DokuWiki_Auth_Plugin $auth */ + global $auth; + if(!$auth || !$auth->canDo('getUsers') ) { + return false; + } + + return parent::isAccessibleByCurrentUser(); + } + + /** * Display form to add or modify a user * -- GitLab