Completed
Push — master ( a8ed13...20cb9d )
by Tim
16:23
created

Modules::getAvailableModules()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 0
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace SimpleSAML\Module\monitor\TestSuite;
4
5
use \SimpleSAML\Module\monitor\TestConfiguration as TestConfiguration;
6
use \SimpleSAML\Module\monitor\State as State;
7
use \SimpleSAML\Module\monitor\TestData as TestData;
8
9
final class Modules extends \SimpleSAML\Module\monitor\TestSuiteFactory
10
{
11
    /**
12
     * @var array
13
     */
14
    private $requiredApacheModules = array();
15
16
    /**
17
     * @var array
18
     */
19
    private $requiredPhpModules = array();
20
21
    /**
22
     * @var array
23
     */
24
    // Important!!  Modules-names are handled case-sensitive!!
25
    private $storeApacheDependencies = array();
26
27
    /**
28
     * @var array
29
     */
30
    private $storePhpDependencies = array(
31
        'memcache' => 'memcached|memcache',
32
        'phpsession' => 'session',
33
        'redis' => 'redis',
34
        'redissentinel' => 'redis',
35
        'riak:Store' => 'riak',
36
        'sql' => 'PDO'
37
    );
38
39
    /**
40
     * @var array
41
     */
42
    private $moduleApacheDependencies = array(
43
        'negotiateext' => 'mod_auth_kerb|mod_auth_gssapi'
44
    );
45
46
    /**
47
     * @var array
48
     */
49
    private $modulePhpDependencies = array(
50
        'authfacebook' => array('curl', 'json'),
51
        'authYubiKey' => 'curl',
52
// TODO: consent only requires pdo when database backend is used.. Should probably add this to required-list when processing metadata
53
//        'consent' => 'PDO',
54
        'consentAdmin' => 'PDO',
55
        'ldap' => 'ldap',
56
        'memcacheMonitor' => 'memcached|memcache',
57
        'negotiate' => 'krb5',
58
        'radius' => 'radius',
59
        'riak' => 'riak',
60
        'sqlauth' => 'PDO'
61
    );
62
63
    /**
64
     * @param TestConfiguration|null $configuration
65
     */
66
    public function __construct($configuration = null)
67
    {
68
        parent::__construct($configuration);
69
    }
70
71
    /**
72
     * @return void
73
     */
74
    protected function initialize()
75
    {
76
        $this->setRequiredApacheModules();
77
        $this->setRequiredPhpModules();
78
        $this->setRequiredSspModules();
79
    }
80
81
    /**
82
     * @return void
83
     */
84
    private function addRequiredApacheModule($module)
85
    {
86
        if (!in_array($module, $this->requiredApacheModules)) {
87
            $this->requiredApacheModules[] = $module;
88
        }
89
    }
90
91
    /**
92
     * @return void
93
     */
94
    private function addRequiredPhpModule($module)
95
    {
96
        if (!in_array($module, $this->requiredPhpModules)) {
97
            $this->requiredPhpModules[] = $module;
98
        }
99
    }
100
101
    /**
102
     * @return void
103
     */
104
    private function setRequiredApacheModules()
105
    {
106
        // Apache Modules
107
        if (\SimpleSAML\Utils\HTTP::isHTTPS()) {
108
            $this->addRequiredApacheModule('mod_ssl');
109
        }
110
        if (function_exists('apache_get_modules')) {
111
            $this->addRequiredApacheModule('mod_php|mod_php5');
112
        }
113
114
        // Determine extra required modules
115
        $configuration = $this->getConfiguration();
116
        $globalConfig = $configuration->getGlobalConfig();
117
        $store = $globalConfig->getValue('store.type');
118
        if (array_key_exists($store, $this->storeApacheDependencies)) {
119
            $this->addRequiredApacheModule($this->storeApacheDependencies[$store]);
120
        }
121
    }
122
123
    /**
124
     * @return void
125
     */
126
    private function setRequiredPhpModules()
127
    {
128
        // PHP modules
129
        $composerFile = \SimpleSAML\Utils\System::resolvePath('composer.json');
130
        $composerData = file_get_contents($composerFile);
131
        $composer = json_decode($composerData, true);
132
        $composerRequired = $composer['require'];
133
134
        foreach ($composerRequired as $ext => $ver) {
135
            if (preg_match('/^ext-/', $ext)) {
136
                $this->addRequiredPhpModule(substr($ext, 4));
137
            }
138
        }
139
140
        // Determine extra required modules
141
        $configuration = $this->getConfiguration();
142
        $globalConfig = $configuration->getGlobalConfig();
143
        $store = $globalConfig->getValue('store.type');
144
        if (array_key_exists($store, $this->storePhpDependencies)) {
145
            $this->addRequiredPhpModule($this->storePhpDependencies[$store]);
146
        }
147
    }
148
149
    /**
150
     * @return void
151
     */
152
    private function setRequiredSspModules()
153
    {
154
        $modules = \SimpleSAML\Module::getModules();
155
        foreach ($modules as $module) {
156
            if (\SimpleSAML\Module::isModuleEnabled($module)) {
157
                if (array_key_exists($module, $this->moduleApacheDependencies)) {
158
                    $dependencies = \SimpleSAML\Utils\Arrays::Arrayize($this->moduleApacheDependencies[$module]);
159
                    foreach ($dependencies as $dependency) {
160
                        $this->addRequiredApacheModule($dependency);
161
                    }
162
                }
163
                if (array_key_exists($module, $this->modulePhpDependencies)) {
164
                    $dependencies = \SimpleSAML\Utils\Arrays::Arrayize($this->modulePhpDependencies[$module]);
165
                    foreach ($dependencies as $dependency) {
166
                        $this->addRequiredPhpModule($dependency);
167
                    }
168
                }
169
            }
170
        }
171
    }
172
173
    /**
174
     * @return string[]
0 ignored issues
show
Documentation introduced by
Should the return type not be array<string,array>?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
175
     */
176
    public function getAvailableModules()
177
    {
178
        $configuration = $this->getConfiguration();
179
        return array(
180
            'Apache' => $configuration->getAvailableApacheModules(),
181
            'Php' => $configuration->getAvailablePhpModules()
182
        );
183
    }
184
185
    /**
186
     * @return array<string,array>
187
     */
188
    private function getRequiredModules()
189
    {
190
        return array('Apache' => $this->requiredApacheModules, 'Php' => $this->requiredPhpModules);
191
    }
192
193
    /**
194
     * @return array<string,array>
195
     */
196
    private function getModuleDependencies()
197
    {
198
        return array('Apache' => $this->moduleApacheDependencies, 'Php' => $this->modulePhpDependencies);
199
    }
200
201
    /**
202
     * @return void
203
     */
204
    protected function invokeTestSuite()
205
    {
206
        $availableModules = $this->getAvailableModules();
207
        $requiredModules = $this->getRequiredModules();
208
        $moduleDependencies = $this->getModuleDependencies();
209
        $output = array();
210
211
        // Test for the availability of required modules
212
        foreach ($availableModules as $category => $available) {
213
            if (is_null($available)) {
214
                $output[$category][] = array(State::SKIPPED, $category, implode(', ', $requiredModules[$category]), 'Unable to verify installed modules');
215
            } else {
216
                $dependencies = array_key_exists($category, $moduleDependencies) ? $moduleDependencies[$category] : array();
217
                $required = array_key_exists($category, $requiredModules) ? $requiredModules[$category] : array();
218
                $available = array_key_exists($category, $availableModules) ? $availableModules[$category] : array();
219
220
                foreach ($required as $require) {
221
                    $testData = new TestData(
222
                        array(
223
                            'require' => $require,
224
                            'available' => $available
225
                        )
226
                    );
227
                    $this->testRequirement($testData, $category, $dependencies, $output);
228
                }
229
            }
230
        }
231
232
        $tests = $this->getTests();
233
        foreach ($availableModules as $category => $available) {
234
            $categories = array_fill(0, count($tests), $category);
235
            if (!isSet($output[$category])) {
236
                $modules = array_map(
237
                  function($test, $category) {
238
                    return ($test->getCategory() === $category) ? $test->getModuleName() : false;
239
                  }, $tests, $categories
240
                );
241
                $modules = array_diff($modules, array(false));
242
                $output[$category][] = array(State::OK, $category, implode(', ', $modules), "All required modules are loaded");
243
            }
244
            $this->addMessages($output[$category], $category);
245
        }
246
247
        $this->calculateState();
248
    }
249
250
    /**
251
     * @param TestData $testData
252
     * @param string $category
253
     * @param array $dependencies
254
     * @param array $output
255
     *
256
     * @return void
257
     */
258
    private function testRequirement($testData, $category, $dependencies, &$output)
259
    {
260
        $require = $testData->getInput('require');
261
        $class = '\\SimpleSAML\\Module\\monitor\\TestCase\\Module\\' . $category;
262
263
        $test = new $class($this, $testData);
264
        $this->addTest($test);
265
266
        $state = $test->getState();
267
        if ($state !== State::OK) {
268
            $missing = array();
269
            while ($dependency = array_search($require, $dependencies)) {
270
                if (\SimpleSAML\Module::isModuleEnabled($dependency)) {
271
                    $missing[] = $dependency;
272
                }
273
                unset($dependencies[$dependency]);
274
            }
275
        }
276
277
        if (!empty($missing)) {
278
            $output[$category][] = array($state, $category, $test->getModuleName(), 'Module not loaded; dependency for ' . implode(', ', $missing));
279
        } else {
280
            $output[$category][] = array($state, $category, $test->getModuleName(), 'Module not loaded');
281
        }
282
    }
283
}
284