Completed
Push — master ( e0fb57...7519fb )
by Tim
09:19 queued 07:48
created

Modules::calculateState()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 21
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

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