diff options
-rwxr-xr-x | config/global.ini.php | 6 | ||||
-rw-r--r-- | config/global.php | 2 | ||||
-rw-r--r-- | core/Application/Kernel/PluginList.php | 4 | ||||
-rw-r--r-- | core/Config.php | 4 | ||||
-rw-r--r-- | core/Config/IniFileChain.php | 17 | ||||
-rw-r--r-- | core/Plugin.php | 4 | ||||
-rw-r--r-- | core/SettingsPiwik.php | 3 | ||||
-rw-r--r-- | plugins/CoreAdminHome/CustomLogo.php | 10 | ||||
-rw-r--r-- | plugins/CorePluginsAdmin/templates/macros.twig | 2 | ||||
-rw-r--r-- | plugins/SitesManager/API.php | 3 | ||||
-rw-r--r-- | plugins/SitesManager/Controller.php | 1 | ||||
-rw-r--r-- | plugins/SitesManager/Menu.php | 2 | ||||
-rw-r--r-- | plugins/SitesManager/SitesManager.php | 14 | ||||
-rw-r--r-- | plugins/UsersManager/API.php | 6 | ||||
-rw-r--r-- | plugins/UsersManager/Controller.php | 18 | ||||
-rw-r--r-- | plugins/UsersManager/Menu.php | 2 | ||||
-rw-r--r-- | plugins/UsersManager/UsersManager.php | 15 | ||||
-rw-r--r-- | plugins/UsersManager/templates/userSettings.twig | 4 |
18 files changed, 103 insertions, 14 deletions
diff --git a/config/global.ini.php b/config/global.ini.php index e874b68b66..ba088da242 100755 --- a/config/global.ini.php +++ b/config/global.ini.php @@ -674,6 +674,12 @@ enable_load_data_infile = 1 ; - links to Uninstall themes will be disabled (but user can still enable/disable themes) enable_plugins_admin = 1 +; By setting this option to 0 the users management will be disabled +enable_users_admin = 1 + +; By setting this option to 0 the websites management will be disabled +enable_sites_admin = 1 + ; By setting this option to 1, it will be possible for Super Users to upload Matomo plugin ZIP archives directly in Matomo Administration. ; Enabling this opens a remote code execution vulnerability where ; an attacker who gained Super User access could execute custom PHP code in a Matomo plugin. diff --git a/config/global.php b/config/global.php index e83091c882..187e6aae78 100644 --- a/config/global.php +++ b/config/global.php @@ -10,6 +10,8 @@ return array( 'path.root' => PIWIK_USER_PATH, + 'path.misc.user' => 'misc/user/', + 'path.tmp' => function (ContainerInterface $c) { $root = $c->get('path.root'); diff --git a/core/Application/Kernel/PluginList.php b/core/Application/Kernel/PluginList.php index 14d2591b73..dbab86e3bd 100644 --- a/core/Application/Kernel/PluginList.php +++ b/core/Application/Kernel/PluginList.php @@ -65,7 +65,9 @@ class PluginList public function getActivatedPlugins() { $section = $this->settings->getSection('Plugins'); - return @$section['Plugins'] ?: array(); + $plugins = @$section['Plugins'] ?: array(); + + return $plugins; } /** diff --git a/core/Config.php b/core/Config.php index 7a24ca4038..916b43b6dd 100644 --- a/core/Config.php +++ b/core/Config.php @@ -138,6 +138,10 @@ class Config */ public static function getLocalConfigPath() { + if (!empty($GLOBALS['CONFIG_INI_PATH_RESOLVER']) && is_callable($GLOBALS['CONFIG_INI_PATH_RESOLVER'])) { + return call_user_func($GLOBALS['CONFIG_INI_PATH_RESOLVER']); + } + $path = self::getByDomainConfigPath(); if ($path) { return $path; diff --git a/core/Config/IniFileChain.php b/core/Config/IniFileChain.php index 94ef276d58..c10930b1d4 100644 --- a/core/Config/IniFileChain.php +++ b/core/Config/IniFileChain.php @@ -11,6 +11,7 @@ use Piwik\Common; use Piwik\Ini\IniReader; use Piwik\Ini\IniReadingException; use Piwik\Ini\IniWriter; +use Piwik\Piwik; /** * Manages a list of INI files where the settings in each INI file merge with or override the @@ -510,6 +511,22 @@ class IniFileChain private function dumpSettings($values, $header) { + /** + * Triggered before a config is being written / saved on the local file system. + * + * A plugin can listen to it and modify which settings will be saved on the file system. This allows you + * to prevent saving config values that a plugin sets on demand. Say you configure the database password in the + * config on demand in your plugin, then you could prevent that the password is saved in the actual config file + * by listening to this event like this: + * + * **Example** + * function doNotSaveDbPassword (&$values) { + * unset($values['database']['password']); + * } + * + * @param array &$values Config values that will be saved + */ + Piwik::postEvent('Config.beforeSave', array(&$values)); $values = $this->encodeValues($values); $writer = new IniWriter(); diff --git a/core/Plugin.php b/core/Plugin.php index ac38b72e20..a3187bf70f 100644 --- a/core/Plugin.php +++ b/core/Plugin.php @@ -13,6 +13,8 @@ use Piwik\Plugin\Dependency; use Piwik\Plugin\Manager; use Piwik\Plugin\MetadataLoader; +if (!class_exists('Piwik\Plugin')) { + /** * Base class of all Plugin Descriptor classes. * @@ -593,3 +595,5 @@ class Plugin return $dependency; } } +} + diff --git a/core/SettingsPiwik.php b/core/SettingsPiwik.php index 1f8e8b203b..9be5fdf119 100644 --- a/core/SettingsPiwik.php +++ b/core/SettingsPiwik.php @@ -10,6 +10,7 @@ namespace Piwik; use Exception; use Piwik\Cache as PiwikCache; +use Piwik\Container\StaticContainer; /** * Contains helper methods that can be used to get common Piwik settings. @@ -354,7 +355,7 @@ class SettingsPiwik */ public static function rewriteMiscUserPathWithInstanceId($path) { - $tmp = 'misc/user/'; + $tmp = StaticContainer::get('path.misc.user'); $path = self::rewritePathAppendPiwikInstanceId($path, $tmp); return $path; } diff --git a/plugins/CoreAdminHome/CustomLogo.php b/plugins/CoreAdminHome/CustomLogo.php index ba2ce811d9..1c1368a1f5 100644 --- a/plugins/CoreAdminHome/CustomLogo.php +++ b/plugins/CoreAdminHome/CustomLogo.php @@ -9,6 +9,7 @@ namespace Piwik\Plugins\CoreAdminHome; use Piwik\Config; +use Piwik\Container\StaticContainer; use Piwik\Filesystem; use Piwik\Option; use Piwik\Piwik; @@ -136,22 +137,22 @@ class CustomLogo public static function getPathUserLogo() { - return static::rewritePath('misc/user/logo.png'); + return static::rewritePath(StaticContainer::get('path.misc.user') . 'logo.png'); } public static function getPathUserFavicon() { - return static::rewritePath('misc/user/favicon.png'); + return static::rewritePath(StaticContainer::get('path.misc.user') . 'favicon.png'); } public static function getPathUserSvgLogo() { - return static::rewritePath('misc/user/logo.svg'); + return static::rewritePath(StaticContainer::get('path.misc.user') . 'logo.svg'); } public static function getPathUserLogoSmall() { - return static::rewritePath('misc/user/logo-header.png'); + return static::rewritePath(StaticContainer::get('path.misc.user') . 'logo-header.png'); } protected static function rewritePath($path) @@ -288,3 +289,4 @@ class CustomLogo } } + diff --git a/plugins/CorePluginsAdmin/templates/macros.twig b/plugins/CorePluginsAdmin/templates/macros.twig index 13c7a41ec9..9c753c77d6 100644 --- a/plugins/CorePluginsAdmin/templates/macros.twig +++ b/plugins/CorePluginsAdmin/templates/macros.twig @@ -282,6 +282,7 @@ </tbody> </table> + {% if displayAdminLinks %} <div class="tableActionBar"> {% if isTheme %} <a href="{{ linkTo({'action':'browseThemes', 'sort': ''}) }}"><span class="icon-add"></span> {{ 'CorePluginsAdmin_InstallNewThemes'|translate }}</a> @@ -289,6 +290,7 @@ <a href="{{ linkTo({'action':'browsePlugins', 'sort': ''}) }}"><span class="icon-add"></span> {{ 'CorePluginsAdmin_InstallNewPlugins'|translate }}</a> {% endif %} </div> + {% endif %} <div class="footer-message"> {% set pluginsAlwaysActivated %} diff --git a/plugins/SitesManager/API.php b/plugins/SitesManager/API.php index b8da10cf39..909d694f23 100644 --- a/plugins/SitesManager/API.php +++ b/plugins/SitesManager/API.php @@ -615,6 +615,7 @@ class API extends \Piwik\Plugin\API $excludeUnknownUrls = null) { Piwik::checkUserHasSuperUserAccess(); + SitesManager::dieIfSitesAdminIsDisabled(); $this->checkName($siteName); @@ -795,6 +796,7 @@ class API extends \Piwik\Plugin\API public function deleteSite($idSite) { Piwik::checkUserHasSuperUserAccess(); + SitesManager::dieIfSitesAdminIsDisabled(); $idSites = $this->getSitesId(); if (!in_array($idSite, $idSites)) { @@ -1255,6 +1257,7 @@ class API extends \Piwik\Plugin\API $excludeUnknownUrls = null) { Piwik::checkUserHasAdminAccess($idSite); + SitesManager::dieIfSitesAdminIsDisabled(); $idSites = $this->getSitesId(); diff --git a/plugins/SitesManager/Controller.php b/plugins/SitesManager/Controller.php index 2d6c4a7824..da66d3a248 100644 --- a/plugins/SitesManager/Controller.php +++ b/plugins/SitesManager/Controller.php @@ -33,6 +33,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin public function index() { Piwik::checkUserHasSomeAdminAccess(); + SitesManager::dieIfSitesAdminIsDisabled(); return $this->renderTemplate('index'); } diff --git a/plugins/SitesManager/Menu.php b/plugins/SitesManager/Menu.php index d37353cada..f8b94dc97a 100644 --- a/plugins/SitesManager/Menu.php +++ b/plugins/SitesManager/Menu.php @@ -28,7 +28,7 @@ class Menu extends \Piwik\Plugin\Menu $menu->addMeasurableItem('General_Settings', $this->urlForAction('globalSettings'), $order = 11); } - if (Piwik::isUserHasSomeAdminAccess()) { + if (Piwik::isUserHasSomeAdminAccess() && SitesManager::isSitesAdminEnabled()) { $menu->addMeasurableItem('SitesManager_MenuManage', $this->urlForAction('index'), $order = 10); $type = $this->getFirstTypeIfOnlyOneIsInUse(); diff --git a/plugins/SitesManager/SitesManager.php b/plugins/SitesManager/SitesManager.php index 751c599f3a..23b7108c7b 100644 --- a/plugins/SitesManager/SitesManager.php +++ b/plugins/SitesManager/SitesManager.php @@ -11,6 +11,7 @@ namespace Piwik\Plugins\SitesManager; use Piwik\Access; use Piwik\API\Request; use Piwik\Common; +use Piwik\Config; use Piwik\Container\StaticContainer; use Piwik\Exception\UnexpectedWebsiteFoundException; use Piwik\Option; @@ -47,6 +48,19 @@ class SitesManager extends \Piwik\Plugin ); } + public static function isSitesAdminEnabled() + { + return (bool) Config::getInstance()->General['enable_sites_admin']; + } + + public static function dieIfSitesAdminIsDisabled() + { + Piwik::checkUserIsNotAnonymous(); + if (!self::isSitesAdminEnabled()) { + throw new \Exception('Creating, updating, and deleting sites has been disabled.'); + } + } + public function addSystemSummaryItems(&$systemSummary) { $websites = Request::processRequest('SitesManager.getAllSites', array('filter_limit' => '-1')); diff --git a/plugins/UsersManager/API.php b/plugins/UsersManager/API.php index fac5b6178d..d2614f581d 100644 --- a/plugins/UsersManager/API.php +++ b/plugins/UsersManager/API.php @@ -651,6 +651,7 @@ class API extends \Piwik\Plugin\API public function addUser($userLogin, $password, $email, $alias = false, $_isPasswordHashed = false, $initialIdSite = null) { Piwik::checkUserHasSomeAdminAccess(); + UsersManager::dieIfUsersAdminIsDisabled(); if (!Piwik::hasUserSuperUserAccess()) { if (empty($initialIdSite)) { @@ -709,6 +710,7 @@ class API extends \Piwik\Plugin\API { Piwik::checkUserHasSuperUserAccess(); $this->checkUserIsNotAnonymous($userLogin); + UsersManager::dieIfUsersAdminIsDisabled(); $requirePasswordConfirmation = self::$SET_SUPERUSER_ACCESS_REQUIRE_PASSWORD_CONFIRMATION; self::$SET_SUPERUSER_ACCESS_REQUIRE_PASSWORD_CONFIRMATION = true; @@ -874,6 +876,7 @@ class API extends \Piwik\Plugin\API $isEmailNotificationOnInConfig = Config::getInstance()->General['enable_update_users_email']; Piwik::checkUserHasSuperUserAccessOrIsTheUser($userLogin); + UsersManager::dieIfUsersAdminIsDisabled(); $this->checkUserIsNotAnonymous($userLogin); $this->checkUserExists($userLogin); @@ -957,6 +960,7 @@ class API extends \Piwik\Plugin\API public function deleteUser($userLogin) { Piwik::checkUserHasSuperUserAccess(); + UsersManager::dieIfUsersAdminIsDisabled(); $this->checkUserIsNotAnonymous($userLogin); $this->checkUserExist($userLogin); @@ -1049,6 +1053,8 @@ class API extends \Piwik\Plugin\API */ public function setUserAccess($userLogin, $access, $idSites) { + UsersManager::dieIfUsersAdminIsDisabled(); + if ($access != 'noaccess') { $this->checkAccessType($access); } diff --git a/plugins/UsersManager/Controller.php b/plugins/UsersManager/Controller.php index a9e2ffcf5b..3d67327469 100644 --- a/plugins/UsersManager/Controller.php +++ b/plugins/UsersManager/Controller.php @@ -15,6 +15,7 @@ use Piwik\Common; use Piwik\Container\StaticContainer; use Piwik\Option; use Piwik\Piwik; +use Piwik\Plugin; use Piwik\Plugin\ControllerAdmin; use Piwik\Plugins\LanguagesManager\API as APILanguagesManager; use Piwik\Plugins\LanguagesManager\LanguagesManager; @@ -53,6 +54,7 @@ class Controller extends ControllerAdmin { Piwik::checkUserIsNotAnonymous(); Piwik::checkUserHasSomeAdminAccess(); + UsersManager::dieIfUsersAdminIsDisabled(); $view = new View('@UsersManager/index'); @@ -183,6 +185,7 @@ class Controller extends ControllerAdmin $view->userEmail = $user['email']; $view->userTokenAuth = Piwik::getCurrentUserTokenAuth(); $view->ignoreSalt = $this->getIgnoreCookieSalt(); + $view->isUsersAdminEnabled = UsersManager::isUsersAdminEnabled(); $newsletterSignupOptionKey = NewsletterSignup::NEWSLETTER_SIGNUP_OPTION . $userLogin; $view->showNewsletterSignup = Option::get($newsletterSignupOptionKey) === false @@ -210,11 +213,14 @@ class Controller extends ControllerAdmin $view->defaultReportSiteName = Site::getNameFor($defaultReport); } - $view->defaultReportOptions = array( - array('key' => 'MultiSites', 'value' => Piwik::translate('General_AllWebsitesDashboard')), - array('key' => $reportOptionsValue, 'value' => Piwik::translate('General_DashboardForASpecificWebsite')), - ); + $defaultReportOptions = array(); + if (Plugin\Manager::getInstance()->isPluginActivated('MultiSites')) { + $defaultReportOptions[] = array('key' => 'MultiSites', 'value' => Piwik::translate('General_AllWebsitesDashboard')); + } + $defaultReportOptions[] = array('key' => $reportOptionsValue, 'value' => Piwik::translate('General_DashboardForASpecificWebsite')); + + $view->defaultReportOptions = $defaultReportOptions; $view->defaultDate = $this->getDefaultDateForUser($userLogin); $view->availableDefaultDates = $this->getDefaultDates(); @@ -383,7 +389,9 @@ class Controller extends ControllerAdmin Piwik::checkUserHasSuperUserAccessOrIsTheUser($userLogin); - $this->processPasswordChange($userLogin); + if (UsersManager::isUsersAdminEnabled()) { + $this->processPasswordChange($userLogin); + } LanguagesManager::setLanguageForSession($language); diff --git a/plugins/UsersManager/Menu.php b/plugins/UsersManager/Menu.php index 77fc8ced04..7945480d4e 100644 --- a/plugins/UsersManager/Menu.php +++ b/plugins/UsersManager/Menu.php @@ -15,7 +15,7 @@ class Menu extends \Piwik\Plugin\Menu { public function configureAdminMenu(MenuAdmin $menu) { - if (Piwik::isUserHasSomeAdminAccess()) { + if (Piwik::isUserHasSomeAdminAccess() && UsersManager::isUsersAdminEnabled()) { $menu->addSystemItem('UsersManager_MenuUsers', $this->urlForAction('index'), $order = 15); } diff --git a/plugins/UsersManager/UsersManager.php b/plugins/UsersManager/UsersManager.php index 7e3f6de428..ede90bea93 100644 --- a/plugins/UsersManager/UsersManager.php +++ b/plugins/UsersManager/UsersManager.php @@ -14,9 +14,11 @@ use Piwik\Access\Role\Write; use Piwik\API\Request; use Piwik\Auth\Password; use Piwik\Common; +use Piwik\Config; use Piwik\Option; use Piwik\Piwik; use Piwik\Plugins\CoreHome\SystemSummary; +use Piwik\Plugins\CorePluginsAdmin\CorePluginsAdmin; use Piwik\SettingsPiwik; /** @@ -45,6 +47,19 @@ class UsersManager extends \Piwik\Plugin ); } + public static function isUsersAdminEnabled() + { + return (bool) Config::getInstance()->General['enable_users_admin']; + } + + public static function dieIfUsersAdminIsDisabled() + { + Piwik::checkUserIsNotAnonymous(); + if (!self::isUsersAdminEnabled()) { + throw new \Exception('Creating, updating, and deleting users has been disabled.'); + } + } + public function addSystemSummaryItems(&$systemSummary) { $userLogins = Request::processRequest('UsersManager.getUsersLogin', array('filter_limit' => '-1')); diff --git a/plugins/UsersManager/templates/userSettings.twig b/plugins/UsersManager/templates/userSettings.twig index 567cac4984..22c9a58ccb 100644 --- a/plugins/UsersManager/templates/userSettings.twig +++ b/plugins/UsersManager/templates/userSettings.twig @@ -20,6 +20,7 @@ inline-help="{{ 'UsersManager_YourUsernameCannotBeChanged'|translate|e('html_attr') }}"> </div> + {% if isUsersAdminEnabled %} <div piwik-field uicontrol="text" name="email" ng-model="personalSettings.email" ng-change="personalSettings.requirePasswordConfirmation()" @@ -27,6 +28,7 @@ data-title="{{ 'UsersManager_Email'|translate|e('html_attr') }}" value="{{ userEmail }}"> </div> + {% endif %} <div id="languageHelp" class="inline-help-node"> <a target="_blank" rel="noreferrer noopener" href="https://matomo.org/translations/"> @@ -72,7 +74,7 @@ value="{{ defaultDate }}" options="{{ availableDefaultDates|json_encode }}"> </div> - {% if isValidHost is defined and isValidHost %} + {% if isValidHost is defined and isValidHost and isUsersAdminEnabled %} <div piwik-field uicontrol="password" name="password" autocomplete="off" ng-model="personalSettings.password" |