Completed
Push — master ( df2b0c...c88b01 )
by Tim
01:35
created

Modules::setRequiredApacheModules()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 10
nc 8
nop 0
dl 0
loc 18
rs 9.2
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
     * @param TestData $testData
0 ignored issues
show
Documentation introduced by
Should the type for parameter $testData not be TestData|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
73
     *
74
     * @return void
75
     */
76
    protected function initialize($testData = null)
77
    {
78
        $this->setRequiredApacheModules();
79
        $this->setRequiredPhpModules();
80
        $this->setRequiredSspModules();
81
    }
82
83
    /**
84
     * @return void
85
     */
86
    private function addRequiredApacheModule($module)
87
    {
88
        if (!in_array($module, $this->requiredApacheModules)) {
89
            $this->requiredApacheModules[] = $module;
90
        }
91
    }
92
93
    /**
94
     * @return void
95
     */
96
    private function addRequiredPhpModule($module)
97
    {
98
        if (!in_array($module, $this->requiredPhpModules)) {
99
            $this->requiredPhpModules[] = $module;
100
        }
101
    }
102
103
    /**
104
     * @return void
105
     */
106
    private function setRequiredApacheModules()
107
    {
108
        // Apache Modules
109
        if (\SimpleSAML\Utils\HTTP::isHTTPS()) {
110
            $this->addRequiredApacheModule('mod_ssl');
111
        }
112
        if (function_exists('apache_get_modules')) {
113
            $this->addRequiredApacheModule('mod_php|mod_php5');
114
        }
115
116
        // Determine extra required modules
117
        $configuration = $this->getConfiguration();
118
        $globalConfig = $configuration->getGlobalConfig();
119
        $store = $globalConfig->getValue('store.type');
120
        if (array_key_exists($store, $this->storeApacheDependencies)) {
121
            $this->addRequiredApacheModule($this->storeApacheDependencies[$store]);
122
        }
123
    }
124
125
    /**
126
     * @return void
127
     */
128
    private function setRequiredPhpModules()
129
    {
130
        // PHP modules
131
        $composerFile = \SimpleSAML\Utils\System::resolvePath('composer.json');
132
        $composerData = file_get_contents($composerFile);
133
        $composer = json_decode($composerData, true);
134
        $composerRequired = $composer['require'];
135
136
        foreach ($composerRequired as $ext => $ver) {
137
            if (preg_match('/^ext-/', $ext)) {
138
                $this->addRequiredPhpModule(substr($ext, 4));
139
            }
140
        }
141
142
        // Determine extra required modules
143
        $configuration = $this->getConfiguration();
144
        $globalConfig = $configuration->getGlobalConfig();
145
        $store = $globalConfig->getValue('store.type');
146
        if (array_key_exists($store, $this->storePhpDependencies)) {
147
            $this->addRequiredPhpModule($this->storePhpDependencies[$store]);
148
        }
149
    }
150
151
    /**
152
     * @return void
153
     */
154
    private function setRequiredSspModules()
155
    {
156
        $modules = \SimpleSAML\Module::getModules();
157
        foreach ($modules as $module) {
158
            if (\SimpleSAML\Module::isModuleEnabled($module)) {
159
                if (array_key_exists($module, $this->moduleApacheDependencies)) {
160
                    $dependencies = \SimpleSAML\Utils\Arrays::Arrayize($this->moduleApacheDependencies[$module]);
161
                    foreach ($dependencies as $dependency) {
162
                        $this->addRequiredApacheModule($dependency);
163
                    }
164
                }
165
                if (array_key_exists($module, $this->modulePhpDependencies)) {
166
                    $dependencies = \SimpleSAML\Utils\Arrays::Arrayize($this->modulePhpDependencies[$module]);
167
                    foreach ($dependencies as $dependency) {
168
                        $this->addRequiredPhpModule($dependency);
169
                    }
170
                }
171
            }
172
        }
173
    }
174
175
    /**
176
     * @return array<string,array>
177
     */
178
    public function getAvailableModules()
179
    {
180
        $configuration = $this->getConfiguration();
181
        return array(
182
            'Apache' => $configuration->getAvailableApacheModules(),
183
            'Php' => $configuration->getAvailablePhpModules()
184
        );
185
    }
186
187
    /**
188
     * @return array<string,array>
189
     */
190
    private function getRequiredModules()
191
    {
192
        return array('Apache' => $this->requiredApacheModules, 'Php' => $this->requiredPhpModules);
193
    }
194
195
    /**
196
     * @return array<string,array>
197
     */
198
    private function getModuleDependencies()
199
    {
200
        return array('Apache' => $this->moduleApacheDependencies, 'Php' => $this->modulePhpDependencies);
201
    }
202
203
    /**
204
     * @return void
205
     */
206
    protected function invokeTestSuite()
207
    {
208
        $availableModules = $this->getAvailableModules();
209
        $requiredModules = $this->getRequiredModules();
210
        $moduleDependencies = $this->getModuleDependencies();
211
        $output = array();
212
213
        // Test for the availability of required modules
214
        foreach ($availableModules as $category => $available) {
215
            if (is_null($available)) {
216
                $output[$category][] = array(State::SKIPPED, $category, implode(', ', $requiredModules[$category]), 'Unable to verify installed modules');
217
            } else {
218
                $dependencies = array_key_exists($category, $moduleDependencies) ? $moduleDependencies[$category] : array();
219
                $required = array_key_exists($category, $requiredModules) ? $requiredModules[$category] : array();
220
                $available = array_key_exists($category, $availableModules) ? $availableModules[$category] : array();
221
222
                foreach ($required as $require) {
223
                    $testData = new TestData(
224
                        array(
225
                            'require' => $require,
226
                            'available' => $available
227
                        )
228
                    );
229
                    $this->testRequirement($testData, $category, $dependencies, $output);
230
                }
231
            }
232
        }
233
234
        $this->calculateState($output);
235
    }
236
237
    /**
238
     * @param array $output
239
     *
240
     * @return void
241
     */
242
    protected function calculateState($output)
243
    {
244
        $availableModules = $this->getAvailableModules();
245
        $tests = $this->getTests();
246
247
        foreach ($availableModules as $category => $available) {
248
            $categories = array_fill(0, count($tests), $category);
249
            if (!isSet($output[$category])) {
250
                $modules = array_map(
251
                  function($test, $category) {
252
                    return ($test->getCategory() === $category) ? $test->getModuleName() : false;
253
                  }, $tests, $categories
254
                );
255
                $modules = array_diff($modules, array(false));
256
                $output[$category][] = array(State::OK, $category, implode(', ', $modules), "All required modules are loaded");
257
            }
258
            $this->addMessages($output[$category], $category);
259
        }
260
261
        parent::calculateState();
262
    }
263
264
    /**
265
     * @param TestData $testData
266
     * @param string $category
267
     * @param array $dependencies
268
     * @param array $output
269
     *
270
     * @return void
271
     */
272
    private function testRequirement($testData, $category, $dependencies, &$output)
273
    {
274
        $require = $testData->getInput('require');
275
        $class = '\\SimpleSAML\\Module\\monitor\\TestCase\\Module\\' . $category;
276
277
        $test = new $class($this, $testData);
278
        $this->addTest($test);
279
280
        $state = $test->getState();
281
        if ($state !== State::OK) {
282
            $missing = array();
283
            while ($dependency = array_search($require, $dependencies)) {
284
                if (\SimpleSAML\Module::isModuleEnabled($dependency)) {
285
                    $missing[] = $dependency;
286
                }
287
                unset($dependencies[$dependency]);
288
            }
289
        }
290
291
        if (!empty($missing)) {
292
            $output[$category][] = array($state, $category, $test->getModuleName(), 'Module not loaded; dependency for ' . implode(', ', $missing));
293
        } else {
294
            $output[$category][] = array($state, $category, $test->getModuleName(), 'Module not loaded');
295
        }
296
    }
297
}
298