ContainerService::findAll()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * \AppserverIo\Appserver\Core\Api\ContainerService
5
 *
6
 * NOTICE OF LICENSE
7
 *
8
 * This source file is subject to the Open Software License (OSL 3.0)
9
 * that is available through the world-wide-web at this URL:
10
 * http://opensource.org/licenses/osl-3.0.php
11
 *
12
 * PHP version 5
13
 *
14
 * @author    Tim Wagner <[email protected]>
15
 * @copyright 2015 TechDivision GmbH <[email protected]>
16
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
17
 * @link      https://github.com/appserver-io/appserver
18
 * @link      http://www.appserver.io
19
 */
20
21
namespace AppserverIo\Appserver\Core\Api;
22
23
use AppserverIo\Appserver\Core\Utilities\FileSystem;
24
use AppserverIo\Appserver\Meta\Composer\Script\Setup;
25
use AppserverIo\Appserver\Meta\Composer\Script\SetupKeys;
26
use AppserverIo\Appserver\Core\Utilities\DirectoryKeys;
27
use AppserverIo\Appserver\Core\Utilities\FileKeys;
28
29
/**
30
 * A service that handles container configuration data.
31
 *
32
 * @author    Tim Wagner <[email protected]>
33
 * @copyright 2015 TechDivision GmbH <[email protected]>
34
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
35
 * @link      https://github.com/appserver-io/appserver
36
 * @link      http://www.appserver.io
37
 */
38
class ContainerService extends AbstractFileOperationService
39
{
40
41
    /**
42
     * The flag that shows that the application server has been installed.
43
     *
44
     * @var string
45
     */
46
    const FLAG_IS_INSTALLED = '.is-installed';
47
48
    /**
49
     * The available setup modes.
50
     *
51
     * @var string
52
     */
53
    const SETUP_MODE_PROD = 'prod';
54
    const SETUP_MODE_DEV = 'dev';
55
    const SETUP_MODE_INSTALL = 'install';
56
57
    /**
58
     * Creates the SSL file passed as parameter or nothing if the file already exists.
59
     *
60
     * @param \SplFileInfo $certificate The file info about the SSL file to generate
61
     *
62
     * @return void
63
     *
64
     * @throws \Exception
65
     */
66 7
    public function createSslCertificate(\SplFileInfo $certificate)
67
    {
68
69
        // first we've to check if OpenSSL is available
70 7
        if (!$this->isOpenSslAvailable()) {
71 1
            return;
72
        }
73
74
        // do nothing if the file is already available
75 6
        if ($certificate->isFile()) {
76 1
            return;
77
        }
78
79
        // prepare the certificate data from our configuration
80
        $dn = array(
81 5
            "countryName" => "DE",
82 5
            "stateOrProvinceName" => "Bavaria",
83 5
            "localityName" => "Kolbermoor",
84 5
            "organizationName" => "appserver.io",
85 5
            "organizationalUnitName" => "Development",
86 5
            "commonName" => gethostname(),
87
            "emailAddress" => "[email protected]"
88 5
        );
89
90
        // check the operating system
91 5
        switch ($this->getOsIdentifier()) {
92
93 5
            case 'DAR': // on Mac OS X use the system default configuration
94
95 1
                $configargs = array('config' => $this->getBaseDirectory('/ssl/openssl.cnf'));
96 1
                break;
97
98 4
            case 'WIN': // on Windows use the system configuration we deliver
99
100 1
                $configargs = array('config' => $this->getBaseDirectory('/php/extras/ssl/openssl.cnf'));
101 1
                break;
102
103 3
            default: // on all other use a standard configuration
104
105
                $configargs = array(
106 3
                    'digest_alg' => 'sha256',
107 3
                    'x509_extensions' => 'v3_ca',
108 3
                    'req_extensions'   => 'v3_req',
109 3
                    'private_key_bits' => 2048,
110 3
                    'private_key_type' => OPENSSL_KEYTYPE_RSA,
111
                    'encrypt_key' => false
112 3
                );
113 5
        }
114
115
        // generate a new private (and public) key pair
116 5
        $privkey = openssl_pkey_new($configargs);
0 ignored issues
show
Unused Code introduced by
The call to AppserverIo\Appserver\Core\Api\openssl_pkey_new() has too many arguments starting with $configargs. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

116
        $privkey = /** @scrutinizer ignore-call */ openssl_pkey_new($configargs);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
117
118
        // Generate a certificate signing request
119 5
        $csr = openssl_csr_new($dn, $privkey, $configargs);
0 ignored issues
show
Unused Code introduced by
The call to AppserverIo\Appserver\Core\Api\openssl_csr_new() has too many arguments starting with $dn. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

119
        $csr = /** @scrutinizer ignore-call */ openssl_csr_new($dn, $privkey, $configargs);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
120
121
        // create a self-signed cert that is valid for 365 days
122 5
        $sscert = openssl_csr_sign($csr, null, $privkey, 365, $configargs);
0 ignored issues
show
Unused Code introduced by
The call to AppserverIo\Appserver\Core\Api\openssl_csr_sign() has too many arguments starting with $csr. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

122
        $sscert = /** @scrutinizer ignore-call */ openssl_csr_sign($csr, null, $privkey, 365, $configargs);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
123
124
        // export the cert + pk files
125 5
        $certout = '';
126 5
        $pkeyout = '';
127 5
        openssl_x509_export($sscert, $certout);
0 ignored issues
show
Unused Code introduced by
The call to AppserverIo\Appserver\Co...i\openssl_x509_export() has too many arguments starting with $sscert. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

127
        /** @scrutinizer ignore-call */ 
128
        openssl_x509_export($sscert, $certout);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
128 5
        openssl_pkey_export($privkey, $pkeyout, null, $configargs);
0 ignored issues
show
Unused Code introduced by
The call to AppserverIo\Appserver\Co...i\openssl_pkey_export() has too many arguments starting with $privkey. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

128
        /** @scrutinizer ignore-call */ 
129
        openssl_pkey_export($privkey, $pkeyout, null, $configargs);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
129
130
        // write the SSL certificate data to the target
131 5
        $file = $certificate->openFile('w');
132 5
        if (($written = $file->fwrite($certout . $pkeyout)) === false) {
133 1
            throw new \Exception(sprintf('Can\'t create SSL certificate %s', $certificate->getPathname()));
134
        }
135
136
        // log a message that the file has been written successfully
137 4
        $this->getInitialContext()->getSystemLogger()->info(
138 4
            sprintf('Successfully created %s with %d bytes', $certificate->getPathname(), $written)
139 4
        );
140
141
        // log any errors that occurred here
142 4
        while (($e = openssl_error_string()) !== false) {
143 1
            $this->getInitialContext()->getSystemLogger()->debug($e);
144 1
        }
145 4
    }
146
147
    /**
148
     * Return's all container node configurations.
149
     *
150
     * @return array An array with container node configurations
151
     * @see \AppserverIo\Psr\ApplicationServer\ServiceInterface::findAll()
152
     */
153
    public function findAll()
154
    {
155
        return $this->getSystemConfiguration()->getContainers();
156
    }
157
158
    /**
159
     * Returns the application base directory for the container
160
     * with the passed UUID.
161
     *
162
     * @param string $uuid UUID of the container to return the application base directory for
163
     *
164
     * @return string The application base directory for this container
165
     */
166
    public function getAppBase($uuid)
167
    {
168
        return $this->load($uuid)->getHost()->getAppBase();
169
    }
170
171
    /**
172
     * Returns true if the OpenSSL extension is loaded, false otherwise
173
     *
174
     * @return boolean
175
     *
176
     * @codeCoverageIgnore this will most likely always be mocked/stubbed, and it is trivial anyway
177
     */
178
    protected function isOpenSslAvailable()
179
    {
180
        return extension_loaded('openssl');
181
    }
182
183
    /**
184
     * Returns the container for the passed UUID.
185
     *
186
     * @param string $uuid Unique UUID of the container to return
187
     *
188
     * @return \AppserverIo\Appserver\Core\Api\Node\ContainerNode The container with the UUID passed as parameter
189
     * @see \AppserverIo\Psr\ApplicationServer\ServiceInterface::load($uuid)
190
     */
191
    public function load($uuid)
192
    {
193
        $containers = $this->findAll();
194
        if (array_key_exists($uuid, $containers)) {
195
            return $containers[$uuid];
196
        }
197
    }
198
199
    /**
200
     * Prepares filesystem to be sure that everything is on place as expected
201
     *
202
     * @return void
203
     * @throws \Exception Is thrown if a server directory can't be created
204
     */
205
    public function prepareFileSystem()
206
    {
207
208
        // load the directories
209
        $directories = $this->getDirectories();
210
211
        // load user and group from system configuration
212
        $user = $this->getSystemConfiguration()->getUser();
213
        $group = $this->getSystemConfiguration()->getGroup();
214
215
        // check if the necessary directories already exists, if not, create them
216
        foreach (DirectoryKeys::getServerDirectoryKeysToBeCreated() as $directoryKey) {
217
            // prepare the path to the directory to be created
218
            $toBeCreated = $this->realpath($directories[$directoryKey]);
219
220
            // prepare the directory name and check if the directory already exists
221
            if (is_dir($toBeCreated) === false) {
222
                // if not, try to create it
223
                if (mkdir($toBeCreated, 0755, true) === false) {
224
                    throw new \Exception(
225
                        sprintf('Can\'t create necessary directory %s while starting application server', $toBeCreated)
226
                    );
227
                }
228
229
                // set user/group specified from the system configuration
230
                if ($this->setUserRights(new \SplFileInfo($toBeCreated), $user, $group) === false) {
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->setUserRights(new...reated), $user, $group) targeting AppserverIo\Appserver\Co...ervice::setUserRights() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
231
                    throw new \Exception(
232
                        sprintf('Can\'t switch user/group to %s/% for directory %s while starting application server', $user, $group, $toBeCreated)
233
                    );
234
                }
235
            }
236
        }
237
238
        // create the container specific directories
239
        /** @var \AppserverIo\Psr\ApplicationServer\Configuration\ContainerConfigurationInterface $containerNode */
240
        foreach ($this->getSystemConfiguration()->getContainers() as $containerNode) {
241
            // iterate over the container host's directories
242
            foreach ($containerNode->getHost()->getDirectories() as $directory) {
243
                // prepare the path to the directory to be created
244
                $toBeCreated = $this->realpath($directory);
245
246
                // prepare the directory name and check if the directory already exists
247
                if (is_dir($toBeCreated) === false) {
248
                    // if not, try to create it
249
                    if (mkdir($toBeCreated, 0755, true) === false) {
250
                        throw new \Exception(
251
                            sprintf('Can\'t create necessary directory %s while starting application server', $toBeCreated)
252
                        );
253
                    }
254
255
                    // set user/group specified from the system configuration
256
                    if ($this->setUserRights(new \SplFileInfo($toBeCreated), $user, $group) === false) {
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->setUserRights(new...reated), $user, $group) targeting AppserverIo\Appserver\Co...ervice::setUserRights() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
257
                        throw new \Exception(
258
                            sprintf('Can\'t switch user/group to %s/% for directory %s while starting application server', $user, $group, $toBeCreated)
259
                        );
260
                    }
261
                }
262
            }
263
        }
264
265
        // check if specific directories has to be cleaned up on startup
266
        foreach (DirectoryKeys::getServerDirectoryKeysToBeCleanedUp() as $directoryKey) {
267
            // prepare the path to the directory to be cleaned up
268
            $toBeCleanedUp = $this->realpath($directories[$directoryKey]);
269
270
            // if the directory exists, clean it up
271
            if (is_dir($toBeCleanedUp)) {
272
                $this->cleanUpDir(new \SplFileInfo($toBeCleanedUp));
273
            }
274
        }
275
276
        // check if needed files do exist and have the correct user rights
277
        $files = $this->getFiles();
278
        foreach (FileKeys::getServerFileKeysToBeCreated() as $fileKeys) {
279
            // prepare the path to the file to be created
280
            $toBeCreated = $this->realpath($files[$fileKeys]);
281
282
            // touch the file (will lead to its creation if it does not exist by now)
283
            if (touch($toBeCreated) === false) {
284
                throw new \Exception(
285
                    sprintf('Can\'t create necessary file %s while starting application server', $toBeCreated)
286
                );
287
            } else {
288
                chmod($toBeCreated, 0755);
289
            }
290
291
            // set user/group specified from the system configuration
292
            if ($this->setUserRight(new \SplFileInfo($toBeCreated), $user, $group) === false) {
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->setUserRight(new ...reated), $user, $group) targeting AppserverIo\Appserver\Co...Service::setUserRight() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
293
                throw new \Exception(
294
                    sprintf('Can\'t switch user/group to %s/% for file %s while starting application server', $user, $group, $toBeCreated)
295
                );
296
            }
297
        }
298
    }
299
300
    /**
301
     * Return's the install flag information from the configuration directory.
302
     *
303
     * @return \SplFileInfo The install flag information
304
     */
305
    public function getIsInstalledFlag()
306
    {
307
        return new \SplFileInfo($this->getConfdDir(ContainerService::FLAG_IS_INSTALLED));
308
    }
309
310
    /**
311
     * Switches the setup mode to the passed value.
312
     *
313
     * @param string $newMode               The mode to switch to
314
     * @param string $configurationFilename The path of the configuration filename
315
     * @param string $user                  The name of the user who started the application server
316
     *
317
     * @return void
318
     * @throws \Exception Is thrown for an invalid setup mode passed
319
     */
320
    public function switchSetupMode($newMode, $configurationFilename, $user)
321
    {
322
323
        // log a message that we switch setup mode now
324
        $this->getInitialContext()->getSystemLogger()->info(sprintf('Now switch mode to %s!!!', $newMode));
325
326
        // init setup context
327
        Setup::prepareContext($this->getBaseDirectory());
328
329
        // init variable for the group
330
        $group = null;
331
332
        // pattern to replace the user in the etc/appserver/appserver.xml/appserver-single-app.xml file
333
        $configurationUserReplacePattern = '/(<appserver[^>]+>[^<]+<params>.*<param name="user[^>]+>)([^<]+)/s';
334
335
        // check setup modes
336
        switch ($newMode) {
337
338
            // prepares everything for developer mode
339
            case ContainerService::SETUP_MODE_DEV:
340
                // get defined group from configuration
341
                $group = Setup::getValue(SetupKeys::GROUP);
342
                // replace user in configuration file
343
                file_put_contents(
344
                    $configurationFilename,
345
                    preg_replace(
346
                        $configurationUserReplacePattern,
347
                        '${1}' . $user,
348
                        file_get_contents($configurationFilename)
349
                    )
350
                );
351
                // replace the user in the PHP-FPM configuration file
352
                file_put_contents(
353
                    $this->getEtcDir('php-fpm.conf'),
354
                    preg_replace(
355
                        '/user = (.*)/',
356
                        'user = ' . $user,
357
                        file_get_contents($this->getEtcDir('php-fpm.conf'))
358
                    )
359
                );
360
                // add everyone write access to configuration files for dev mode
361
                FileSystem::recursiveChmod($this->getEtcDir(), 0777, 0777);
362
363
                break;
364
365
            // prepares everything for production mode
366
            case ContainerService::SETUP_MODE_PROD:
367
                // get defined user and group from configuration
368
                $user = Setup::getValue(SetupKeys::USER);
369
                $group = Setup::getValue(SetupKeys::GROUP);
370
                // replace user to be same as user in configuration file
371
                file_put_contents(
372
                    $configurationFilename,
373
                    preg_replace(
374
                        $configurationUserReplacePattern,
375
                        '${1}' . $user,
376
                        file_get_contents($configurationFilename)
377
                    )
378
                );
379
                // replace the user in the PHP-FPM configuration file
380
                file_put_contents(
381
                    $this->getEtcDir('php-fpm.conf'),
382
                    preg_replace(
383
                        '/user = (.*)/',
384
                        'user = ' . $user,
385
                        file_get_contents($this->getEtcDir('php-fpm.conf'))
386
                    )
387
                );
388
389
                // set correct file permissions for configurations
390
                FileSystem::recursiveChmod($this->getEtcDir());
391
392
                break;
393
394
            // prepares everything for first installation which is default mode
395
            case ContainerService::SETUP_MODE_INSTALL:
396
                // load the flag marked the server as installed
397
                $isInstalledFlag = $this->getIsInstalledFlag();
398
399
                // first check if it is a fresh installation
400
                if ($isInstalledFlag->isReadable() === false) {
401
                     // first iterate over all containers deploy directories and look for application archives
402
                     /** @var \AppserverIo\Psr\ApplicationServer\Configuration\ContainerConfigurationInterface $containerNode */
403
                    foreach ($this->getSystemConfiguration()->getContainers() as $containerNode) {
404
                        // iterate over all found application archives and create the .dodeploy flag file
405
                        foreach (glob($this->getDeployDir($containerNode, '/*.phar')) as $archive) {
406
                            touch(sprintf('%s.dodeploy', $archive));
407
                        }
408
                    }
409
                }
410
411
                // create is installed flag for prevent further setup install mode calls
412
                touch($isInstalledFlag);
413
414
                // get defined user and group from configuration
415
                $user = Setup::getValue(SetupKeys::USER);
416
                $group = Setup::getValue(SetupKeys::GROUP);
417
418
                // set correct file permissions for configurations
419
                FileSystem::recursiveChmod($this->getEtcDir());
420
421
                break;
422
423
            default:
424
                throw new \Exception(sprintf('Invalid setup mode %s given', $newMode));
425
        }
426
427
        // check if user and group is set
428
        if (!is_null($user) && !is_null($group)) {
429
            // get needed files as accessable for all root files remove "." and ".." from the list
430
            $rootFiles = scandir($this->getBaseDirectory());
431
            // iterate all files
432
            foreach ($rootFiles as $rootFile) {
433
                // we want just files on root dir
434
                if (is_file($rootFile) && !in_array($rootFile, array('.', '..'))) {
435
                    FileSystem::chmod($rootFile, 0644);
436
                    FileSystem::chown($rootFile, $user, $group);
437
                }
438
            }
439
440
            // ... and change own and mod of following directories
441
            FileSystem::chown($this->getBaseDirectory(), $user, $group);
442
            FileSystem::recursiveChown($this->getBaseDirectory('resources'), $user, $group);
443
            FileSystem::recursiveChmod($this->getBaseDirectory('resources'));
444
            FileSystem::recursiveChown($this->getBaseDirectory('src'), $user, $group);
445
            FileSystem::recursiveChmod($this->getBaseDirectory('src'));
446
            FileSystem::recursiveChown($this->getBaseDirectory('var'), $user, $group);
447
            FileSystem::recursiveChmod($this->getBaseDirectory('var'));
448
            FileSystem::recursiveChown($this->getBaseDirectory('tests'), $user, $group);
449
            FileSystem::recursiveChmod($this->getBaseDirectory('tests'));
450
            FileSystem::recursiveChown($this->getBaseDirectory('vendor'), $user, $group);
451
            FileSystem::recursiveChmod($this->getBaseDirectory('vendor'));
452
453
            // ... and the change own and mod for the system's temporary directory
454
            FileSystem::recursiveChown($this->getSystemTmpDir(), $user, $group);
455
            FileSystem::recursiveChmod($this->getSystemTmpDir());
456
457
            // ... and change own and mod for the container specific directories
458
            /** @var \AppserverIo\Psr\ApplicationServer\Configuration\ContainerConfigurationInterface $containerNode */
459
            foreach ($this->getSystemConfiguration()->getContainers() as $containerNode) {
460
                FileSystem::recursiveChown($this->getWebappsDir($containerNode), $user, $group);
461
                FileSystem::recursiveChmod($this->getWebappsDir($containerNode));
462
                FileSystem::recursiveChown($this->getTmpDir($containerNode), $user, $group);
463
                FileSystem::recursiveChmod($this->getTmpDir($containerNode));
464
                FileSystem::recursiveChown($this->getDeployDir($containerNode), $user, $group);
465
                FileSystem::recursiveChmod($this->getDeployDir($containerNode));
466
            }
467
468
            // make server.php executable
469
            FileSystem::chmod($this->getBaseDirectory('server.php'), 0755);
470
471
            // log a message that we successfully switched to the new setup mode
472
            $this->getInitialContext()->getSystemLogger()->info(sprintf("Setup for mode '%s' done successfully!", $newMode));
473
474
        } else {
475
            throw new \Exception('No user or group given');
476
        }
477
    }
478
}
479