diff --git a/_test/tests/inc/Action/general.test.php b/_test/tests/inc/Action/general.test.php index 9aacfbf96bb15416ca4186c6298134fd7bc380c7..aafeb4a83748f8dd88b4d3b60a51a811b7c20914 100644 --- a/_test/tests/inc/Action/general.test.php +++ b/_test/tests/inc/Action/general.test.php @@ -26,7 +26,7 @@ class action_general extends DokuWikiTest { array('Revert', AUTH_ADMIN, array('exists' => true, 'ismanager' => false)), array('Revert', AUTH_EDIT, array('exists' => true, 'ismanager' => true)), - array('Admin', AUTH_ADMIN, array('exists' => true, 'ismanager' => false)), + array('Admin', AUTH_READ, array('exists' => true, 'ismanager' => false)), // let in, check later again array('Admin', AUTH_READ, array('exists' => true, 'ismanager' => true)), // let in, check later again array('Check', AUTH_READ, array('exists' => true, 'ismanager' => false)), // sensible? diff --git a/_test/tests/inc/template_include_page.test.php b/_test/tests/inc/template_include_page.test.php index 7dd13ba236be6a3f41f010c4573e7489e1371dcc..ea9e895581fb361ebc1703aaae95219d045a322b 100644 --- a/_test/tests/inc/template_include_page.test.php +++ b/_test/tests/inc/template_include_page.test.php @@ -32,7 +32,9 @@ class template_pagetitle_test extends DokuWikiTest { } function test_adminPluginTitle() { - global $ID,$ACT,$INPUT,$conf; + global $ID,$ACT,$INPUT,$conf,$INFO; + $INFO['isadmin'] = true; + $INFO['ismanager'] = true; if (!plugin_load('admin','revert')) { $this->markTestSkipped('Revert plugin not found, unable to test admin plugin titles'); diff --git a/inc/Action/Admin.php b/inc/Action/Admin.php index 8d43057888fdd26c00e55f5cdc40bedcee954796..cc6dfd74fd052d972156ed02cfbec796feeb6ff2 100644 --- a/inc/Action/Admin.php +++ b/inc/Action/Admin.php @@ -15,22 +15,11 @@ class Admin extends AbstractUserAction { /** @inheritdoc */ public function minimumPermission() { - global $INFO; - - if($INFO['ismanager']) { - return AUTH_READ; // let in check later - } else { - return AUTH_ADMIN; - } + return AUTH_READ; // let in check later } public function checkPreconditions() { parent::checkPreconditions(); - - global $INFO; - if(!$INFO['ismanager']) { - throw new ActionException('denied'); - } } public function preProcess() { @@ -41,7 +30,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/Menu/Item/Admin.php b/inc/Menu/Item/Admin.php index 7302f0f34fd42eabc96f886668d8efb691c8a0a1..e5506c220299302edb5a4ee854f2673af300dc74 100644 --- a/inc/Menu/Item/Admin.php +++ b/inc/Menu/Item/Admin.php @@ -11,14 +11,18 @@ class Admin extends AbstractItem { /** @inheritdoc */ public function __construct() { - global $INFO; parent::__construct(); $this->svg = DOKU_INC . 'lib/images/menu/settings.svg'; + } + + /** @inheritdoc */ + public function visibleInContext($ctx) + { + global $INFO; + if(!$INFO['ismanager']) return false; - if(!$INFO['ismanager']) { - throw new \RuntimeException("admin is for managers only"); - } + return parent::visibleInContext($ctx); } } diff --git a/inc/Ui/Admin.php b/inc/Ui/Admin.php index aa3b8b99e281c17e5c20b0a5b7159b495acc3e4c..20416e1371c86bc65eff9589674472a9f9ec91be 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 a395be43512ba5c4afe766353797200f67582ca3..0cd113b14825258485dcba8328af3d97216b1781 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/acl/action.php b/lib/plugins/acl/action.php index a7226f598c8ef7db825003a8e59a7811f8ccb4a4..1d6d05b806f58b33ab52439630b21e946f44d4b6 100644 --- a/lib/plugins/acl/action.php +++ b/lib/plugins/acl/action.php @@ -34,7 +34,7 @@ class action_plugin_acl extends DokuWiki_Action_Plugin { * @return void */ - public function handle_ajax_call_acl(Doku_Event &$event, $param) { + public function handle_ajax_call_acl(Doku_Event $event, $param) { if($event->data !== 'plugin_acl') { return; } @@ -44,7 +44,10 @@ class action_plugin_acl extends DokuWiki_Action_Plugin { global $ID; global $INPUT; - if(!auth_isadmin()) { + /** @var $acl admin_plugin_acl */ + $acl = plugin_load('admin', 'acl'); + + if(!$acl->isAccessibleByCurrentUser()) { echo 'for admins only'; return; } @@ -54,9 +57,6 @@ class action_plugin_acl extends DokuWiki_Action_Plugin { } $ID = getID(); - - /** @var $acl admin_plugin_acl */ - $acl = plugin_load('admin', 'acl'); $acl->handle(); $ajax = $INPUT->str('ajax'); diff --git a/lib/plugins/admin.php b/lib/plugins/admin.php index 4e1cbbb337c11393ab40afeaa4b769a9e35c11cd..d9c7d296998b4f6bbdc5041aa7cc45cec636d58b 100644 --- a/lib/plugins/admin.php +++ b/lib/plugins/admin.php @@ -72,6 +72,31 @@ 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 = []; + $data['instance'] = $this; + $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/extension/action.php b/lib/plugins/extension/action.php index 9e48f134b1f7d527804450ddf60d73d0d33febee..4af84f8df03f52b5037c7c18f75eda6109798fc9 100644 --- a/lib/plugins/extension/action.php +++ b/lib/plugins/extension/action.php @@ -36,7 +36,9 @@ class action_plugin_extension extends DokuWiki_Action_Plugin { $event->preventDefault(); $event->stopPropagation(); - if(empty($_SERVER['REMOTE_USER']) || !auth_isadmin($_SERVER['REMOTE_USER'], $USERINFO['grps'])) { + /** @var admin_plugin_extension $admin */ + $admin = plugin_load('admin', 'extension'); + if(!$admin->isAccessibleByCurrentUser()) { http_status(403); echo 'Forbidden'; exit; diff --git a/lib/plugins/styling/action.php b/lib/plugins/styling/action.php index 896e14bef8ed60d137ea28591760688bfe30d236..2190fd61dd900bd359713cd30fb4693d8aba8e27 100644 --- a/lib/plugins/styling/action.php +++ b/lib/plugins/styling/action.php @@ -41,7 +41,9 @@ class action_plugin_styling extends DokuWiki_Action_Plugin { global $ACT; global $INPUT; if($ACT != 'admin' || $INPUT->str('page') != 'styling') return; - if(!auth_isadmin()) return; + /** @var admin_plugin_styling $admin */ + $admin = plugin_load('admin', 'styling'); + if(!$admin->isAccessibleByCurrentUser()) return; // set preview $len = count($event->data['link']); diff --git a/lib/plugins/styling/popup.php b/lib/plugins/styling/popup.php index 964b19e296a1d2217591e5dd3f76d3d0003168f8..4a1735ccc47248102edeba550cb153ac2c8ac74f 100644 --- a/lib/plugins/styling/popup.php +++ b/lib/plugins/styling/popup.php @@ -8,7 +8,7 @@ header('X-UA-Compatible: IE=edge,chrome=1'); /** @var admin_plugin_styling $plugin */ $plugin = plugin_load('admin', 'styling'); -if(!auth_isadmin()) die('only admins allowed'); +if(!$plugin->isAccessibleByCurrentUser()) die('only admins allowed'); $plugin->ispopup = true; // handle posts diff --git a/lib/plugins/usermanager/admin.php b/lib/plugins/usermanager/admin.php index 6d9bf3b202bbd5067f9338e9523ed57500f3fe35..3148971ce19f137816ae1f333716ec80d0f7bdd1 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 *