Skip to content

Commit 880dac3

Browse files
authored
Plugin whitelist now uses file system, #PG-5137 (#35)
* Added plugin list service with event to hook in to * Refactor to use DI for testability and add tests * Add post event validation * removed EventDispatcher dependency in favour of Piwik::postEvent * Apply PR feedback * remove redundant code * Fix tests
1 parent 4561d34 commit 880dac3

7 files changed

Lines changed: 264 additions & 115 deletions

File tree

API.php

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,36 +10,31 @@
1010
namespace Piwik\Plugins\OpenApiDocs;
1111

1212
use Piwik\Piwik;
13+
use Piwik\Plugins\OpenApiDocs\Generation\PluginListProvider;
1314
use Piwik\Plugin\Manager;
1415
use Piwik\Plugins\OpenApiDocs\Specs\SpecGenerator;
1516
use Piwik\Plugins\OpenApiDocs\Specs\PathResolver;
1617

1718
/**
1819
* Provides Reporting API endpoints for reading OpenAPI plugin configuration and specifications.
1920
*
20-
* Exposes endpoints to return the configured plugin whitelist, read pre-generated spec files,
21+
* Exposes endpoints to return the effective plugin list for spec generation, read pre-generated spec files,
2122
* or generate plugin OpenAPI specifications on demand.
2223
*
2324
* @method static \Piwik\Plugins\OpenApiDocs\API getInstance()
2425
*/
2526
class API extends \Piwik\Plugin\API
2627
{
2728
/**
28-
* Returns the plugin names configured for OpenApiDocs spec generation.
29+
* Returns the plugin names used for OpenApiDocs spec generation.
2930
*
30-
* @return array<int, string> The configured whitelist of plugin names from
31-
* `config/plugins.php`.
31+
* @return array<int, string>
3232
*/
33-
public function getPluginWhitelist(): array
33+
public function getAllowedPlugins(): array
3434
{
3535
Piwik::checkUserHasSomeViewAccess();
3636

37-
$pluginWhitelist = $this->loadPluginWhitelist();
38-
if (!is_array($pluginWhitelist)) {
39-
throw new \Exception('OpenApiDocs plugin whitelist config is invalid.');
40-
}
41-
42-
return $pluginWhitelist;
37+
return $this->getPluginListProvider()->getAllowedPlugins();
4338
}
4439

4540
/**
@@ -88,14 +83,6 @@ protected function getSpecFilePath(string $pluginName): string
8883
return $this->getSpecPathResolver()->getSpecFilePath($pluginName);
8984
}
9085

91-
/**
92-
* @return mixed
93-
*/
94-
protected function loadPluginWhitelist()
95-
{
96-
return require __DIR__ . '/config/plugins.php';
97-
}
98-
9986
protected function isSpecFileReadable(string $filePath): bool
10087
{
10188
return is_file($filePath) && is_readable($filePath);
@@ -127,6 +114,11 @@ protected function getSpecPathResolver(): PathResolver
127114
return new PathResolver();
128115
}
129116

117+
protected function getPluginListProvider(): PluginListProvider
118+
{
119+
return new PluginListProvider();
120+
}
121+
130122
/**
131123
* Generates an OpenAPI specification for one or more plugins and returns it immediately.
132124
*

Generation/PluginListProvider.php

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
/**
4+
* Matomo - free/libre analytics platform
5+
*
6+
* @link https://matomo.org
7+
* @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
8+
*/
9+
10+
declare(strict_types=1);
11+
12+
namespace Piwik\Plugins\OpenApiDocs\Generation;
13+
14+
use Piwik\Piwik;
15+
use Piwik\Plugin\Manager;
16+
17+
class PluginListProvider
18+
{
19+
/**
20+
* @var Manager
21+
*/
22+
private $pluginManager;
23+
24+
public function __construct(?Manager $pluginManager = null)
25+
{
26+
$this->pluginManager = $pluginManager ?? Manager::getInstance();
27+
}
28+
29+
/**
30+
* @return string[]
31+
*/
32+
public function getAllowedPlugins(): array
33+
{
34+
$pluginNames = array_values($this->pluginManager->getActivatedPlugins());
35+
36+
$this->dispatchUpdatePluginListEvent($pluginNames);
37+
38+
$pluginNames = array_values(array_unique($pluginNames));
39+
40+
return array_values(array_filter($pluginNames, function ($pluginName): bool {
41+
return is_string($pluginName) && $this->shouldIncludeEventProvidedPlugin($pluginName);
42+
}));
43+
}
44+
45+
private function shouldIncludeEventProvidedPlugin(string $pluginName): bool
46+
{
47+
if (!$this->pluginManager->isPluginInFilesystem($pluginName)) {
48+
return false;
49+
}
50+
return $this->pluginHasApiFile($pluginName);
51+
}
52+
53+
protected function pluginHasApiFile(string $pluginName): bool
54+
{
55+
return is_file(Manager::getPluginDirectory($pluginName) . '/API.php');
56+
}
57+
58+
/**
59+
* @param string[] $pluginNames
60+
*/
61+
private function dispatchUpdatePluginListEvent(array &$pluginNames): void
62+
{
63+
$this->postEvent('OpenApiDocs.updatePluginList', [&$pluginNames]);
64+
}
65+
66+
/**
67+
* @param array<int, mixed> $params
68+
*/
69+
protected function postEvent(string $eventName, array $params): void
70+
{
71+
Piwik::postEvent($eventName, $params);
72+
}
73+
}

Tasks.php

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Piwik\Config;
1515
use Piwik\Log\LoggerInterface;
16+
use Piwik\Plugins\OpenApiDocs\Generation\PluginListProvider;
1617
use Piwik\Plugins\OpenApiDocs\Generation\SpecGenerationService;
1718

1819
class Tasks extends \Piwik\Plugin\Tasks
@@ -27,10 +28,19 @@ class Tasks extends \Piwik\Plugin\Tasks
2728
*/
2829
private $logger;
2930

30-
public function __construct(SpecGenerationService $specGenerationService, LoggerInterface $logger)
31-
{
31+
/**
32+
* @var PluginListProvider
33+
*/
34+
private $pluginListProvider;
35+
36+
public function __construct(
37+
SpecGenerationService $specGenerationService,
38+
LoggerInterface $logger,
39+
?PluginListProvider $pluginListProvider = null
40+
) {
3241
$this->specGenerationService = $specGenerationService;
3342
$this->logger = $logger;
43+
$this->pluginListProvider = $pluginListProvider ?? new PluginListProvider();
3444
}
3545

3646
public function schedule()
@@ -42,7 +52,7 @@ public function schedule()
4252

4353
public function generateConfiguredPluginSpecs(): void
4454
{
45-
$pluginNames = require __DIR__ . '/config/plugins.php';
55+
$pluginNames = $this->pluginListProvider->getAllowedPlugins();
4656

4757
foreach ($pluginNames as $pluginName) {
4858
try {

config/plugins.php

Lines changed: 0 additions & 74 deletions
This file was deleted.

tests/Unit/APITest.php

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Piwik\Access;
1818
use Piwik\Container\StaticContainer;
1919
use Piwik\Plugins\OpenApiDocs\API;
20+
use Piwik\Plugins\OpenApiDocs\Generation\PluginListProvider;
2021
use Piwik\Plugins\OpenApiDocs\Specs\PathResolver;
2122
use Piwik\Tests\Framework\Mock\FakeAccess;
2223

@@ -61,29 +62,19 @@ public function testGetOpenApiSpecReturnsDecodedJsonForPlugin()
6162
$this->assertSame($expectedSpec, $result);
6263
}
6364

64-
public function testGetPluginWhitelistReturnsConfigValuesInOrder()
65+
public function testGetAllowedPluginsReturnsProviderValues(): void
6566
{
66-
$expectedWhitelist = ['RollUpReporting', 'Login', 'ActivityLog'];
67+
$provider = $this->createMock(PluginListProvider::class);
68+
$provider->expects($this->once())
69+
->method('getAllowedPlugins')
70+
->willReturn(['Login', 'ActivityLog']);
6771

6872
$api = $this->getMockBuilder(API::class)
69-
->onlyMethods(['loadPluginWhitelist'])
73+
->onlyMethods(['getPluginListProvider'])
7074
->getMock();
71-
$api->method('loadPluginWhitelist')->willReturn($expectedWhitelist);
75+
$api->method('getPluginListProvider')->willReturn($provider);
7276

73-
$this->assertSame($expectedWhitelist, $api->getPluginWhitelist());
74-
}
75-
76-
public function testGetPluginWhitelistThrowsExceptionWhenConfigIsInvalid()
77-
{
78-
$api = $this->getMockBuilder(API::class)
79-
->onlyMethods(['loadPluginWhitelist'])
80-
->getMock();
81-
$api->method('loadPluginWhitelist')->willReturn('invalid');
82-
83-
$this->expectException(\Exception::class);
84-
$this->expectExceptionMessage('OpenApiDocs plugin whitelist config is invalid.');
85-
86-
$api->getPluginWhitelist();
77+
$this->assertSame(['Login', 'ActivityLog'], $api->getAllowedPlugins());
8778
}
8879

8980
public function testGetOpenApiSpecThrowsExceptionWhenFileMissing()

0 commit comments

Comments
 (0)