Completed
Push — master ( d7fc79...487414 )
by Joschi
04:26
created

PersistenceService::renameAccount()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 5
c 0
b 0
f 0
ccs 0
cts 2
cp 0
rs 9.4285
cc 1
eloc 1
nc 1
nop 1
crap 2
1
<?php
2
3
/**
4
 * admin
5
 *
6
 * @category    Tollwerk
7
 * @package     Tollwerk\Admin
8
 * @subpackage  Tollwerk\Admin\Infrastructure\Service
9
 * @author      Joschi Kuphal <[email protected]> / @jkphl
10
 * @copyright   Copyright © 2016 Joschi Kuphal <[email protected]> / @jkphl
11
 * @license     http://opensource.org/licenses/MIT The MIT License (MIT)
12
 */
13
14
/***********************************************************************************
15
 *  The MIT License (MIT)
16
 *
17
 *  Copyright © 2016 Joschi Kuphal <[email protected]> / @jkphl
18
 *
19
 *  Permission is hereby granted, free of charge, to any person obtaining a copy of
20
 *  this software and associated documentation files (the "Software"), to deal in
21
 *  the Software without restriction, including without limitation the rights to
22
 *  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
23
 *  the Software, and to permit persons to whom the Software is furnished to do so,
24
 *  subject to the following conditions:
25
 *
26
 *  The above copyright notice and this permission notice shall be included in all
27
 *  copies or substantial portions of the Software.
28
 *
29
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
31
 *  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
32
 *  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
33
 *  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
34
 *  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35
 ***********************************************************************************/
36
37
namespace Tollwerk\Admin\Infrastructure\Service;
38
39
use Tollwerk\Admin\Application\Contract\PersistenceAdapterFactoryInterface;
40
use Tollwerk\Admin\Application\Contract\PersistenceServiceInterface;
41
use Tollwerk\Admin\Domain\Account\AccountInterface;
42
use Tollwerk\Admin\Domain\Vhost\VhostInterface;
43
use Tollwerk\Admin\Infrastructure\Persistence\AccountHelper;
44
45
/**
46
 * Persistence service
47
 *
48
 * @package Tollwerk\Admin
49
 * @subpackage Tollwerk\Admin\Infrastructure
50
 */
51
class PersistenceService implements PersistenceServiceInterface
52
{
53
    /**
54
     * Persistence adapter factory
55
     *
56
     * @var PersistenceAdapterFactoryInterface
57
     */
58
    protected $persistenceAdapterFactory;
59
    /**
60
     * Webservers scheduled for reload
61
     *
62
     * @var array
63
     */
64
    protected $reloadWebserver = [];
65
    /**
66
     * PHP-Restart flag
67
     *
68
     * @var bool
69
     */
70
    protected $reloadPhp = false;
71
72
    /**
73
     * Constructor
74
     *
75
     * @param PersistenceAdapterFactoryInterface $persistenceAdapterFactory Persistence adapter factory
76
     */
77
    public function __construct(PersistenceAdapterFactoryInterface $persistenceAdapterFactory)
78
    {
79
        $this->persistenceAdapterFactory = $persistenceAdapterFactory;
80
    }
81
82
    /**
83
     * Create an account
84
     *
85
     * @param AccountInterface $account Account
86
     * @return void
87
     */
88
    public function createAccount(AccountInterface $account)
89
    {
90
    }
91
92
    /**
93
     * Delete an account
94
     *
95
     * @param AccountInterface $account Account
96
     * @return void
97
     */
98
    public function deleteAccount(AccountInterface $account)
99
    {
100
        // Delete all virtual hosts
101
        /** @var VhostInterface $vhost */
102
        foreach ($account->getVhosts() as $vhost) {
103
            $this->deleteVhost($account, $vhost);
104
        }
105
    }
106
107
    /**
108
     * Enable an account
109
     *
110
     * @param AccountInterface $account Account
111
     * @return void
112
     */
113
    public function enableAccount(AccountInterface $account)
114
    {
115
        // Enable all virtual hosts
116
        /** @var VhostInterface $vhost */
117
        foreach ($account->getVhosts() as $vhost) {
118
            $this->enableVhost($account, $vhost);
119
        }
120
    }
121
122
    /**
123
     * Disable an account
124
     *
125
     * @param AccountInterface $account Account
126
     * @return void
127
     */
128
    public function disableAccount(AccountInterface $account)
129
    {
130
        // Disable all virtual hosts
131
        /** @var VhostInterface $vhost */
132
        foreach ($account->getVhosts() as $vhost) {
133
            $this->disableVhost($account, $vhost);
134
        }
135
    }
136
137
    /**
138
     * Rename an account
139
     *
140
     * @param AccountInterface $account Account
141
     * @return void
142
     */
143
    public function renameAccount(AccountInterface $account)
144
    {
145
        // Rewrite all virtual hosts
146
        // Reload Apache
147
    }
148
149
    /**
150
     * Create a virtual host
151
     *
152
     * @param AccountInterface $account Account
153
     * @param VhostInterface $vhost Virtual host
154
     * @return void
155
     */
156
    public function createVhost(AccountInterface $account, VhostInterface $vhost)
157
    {
158
        $accountHelper = new AccountHelper($account);
159
        $availableVhost = $accountHelper->vhostDirectory($vhost, false);
160
        if (is_dir($availableVhost)) {
161
            throw new \RuntimeException(
162
                sprintf('Virtual host "%s" already exists', strval($vhost->getPrimaryDomain())),
163
                1476018313
164
            );
165
        }
166
167
        // Prepare the virtual host directory
168
        $this->prepareDirectory($availableVhost);
169
170
        // Persist the virtual host
171
        $this->persistVhost($account, $vhost);
172
    }
173
174
    /**
175
     * Delete a virtual host
176
     *
177
     * @param AccountInterface $account Account
178
     * @param VhostInterface $vhost Virtual host
179
     * @return void
180
     */
181
    public function deleteVhost(AccountInterface $account, VhostInterface $vhost)
182
    {
183
        // Disable the virtual host first
184
        $this->disableVhost($account, $vhost);
185
186
        $accountHelper = new AccountHelper($account);
187
        $availableVhost = $accountHelper->vhostDirectory($vhost, false);
188
        if (is_dir($availableVhost)) {
189
            $this->deleteDirectory($availableVhost);
190
        }
191
    }
192
193
    /**
194
     * Enable a virtual host
195
     *
196
     * @param AccountInterface $account Account
197
     * @param VhostInterface $vhost Virtual host
198
     * @return void
199
     * @throws \RuntimeException If the virtual host directory doesn't exist yet
200
     * @throws \RuntimeException If the virtual host is already enabled but cannot be renewed
201
     */
202
    public function enableVhost(AccountInterface $account, VhostInterface $vhost)
203
    {
204
        // If both the account and the virtual host are active: Enable the virtual host
205
        if ($account->isActive() && $vhost->isActive()) {
206
            $accountHelper = new AccountHelper($account);
207
            $availableVhost = $accountHelper->vhostDirectory($vhost, false);
208
209
            // If the virtual host directory doesn't exist yet: Error
210
            if (!is_dir($availableVhost)) {
211
                throw new \RuntimeException(
212
                    sprintf('Virtual host "%s" isn\'t persisted yet', strval($vhost->getPrimaryDomain())),
213
                    1476015113
214
                );
215
            }
216
217
            $enabledVhost = $accountHelper->vhostDirectory($vhost, true);
218
            $this->prepareDirectory(dirname($enabledVhost));
219
220
            // If the virtual host is already enabled but cannot be renewed
221
            if (file_exists($enabledVhost) && !unlink($enabledVhost)) {
222
                throw new \RuntimeException(
223
                    sprintf(
224
                        'Virtual host "%s" is already enabled but cannot be renewed',
225
                        strval($vhost->getPrimaryDomain())
226
                    ),
227
                    1476016371
228
                );
229
            }
230
231
            $relativeEnabledVhost = '..'.substr($availableVhost, strlen($accountHelper->directory('config')));
232
            symlink($relativeEnabledVhost, $enabledVhost);
233
234
            // Reload apache
235
            $this->registerWebserverReload($vhost->getType());
236
        }
237
    }
238
239
    /**
240
     * Disable a virtual host
241
     *
242
     * @param AccountInterface $account Account
243
     * @param VhostInterface $vhost Virtual host
244
     * @return void
245
     */
246
    public function disableVhost(AccountInterface $account, VhostInterface $vhost)
247
    {
248
        $accountHelper = new AccountHelper($account);
249
        $enabledVhost = $accountHelper->vhostDirectory($vhost, true);
250
251
        // If the virtual host cannot be disabled
252
        if (file_exists($enabledVhost) && !unlink($enabledVhost)) {
253
            throw new \RuntimeException(
254
                sprintf('Virtual host "%s" cannot be disabled', strval($vhost->getPrimaryDomain())),
255
                1476016872
256
            );
257
        }
258
259
        // Reload apache
260
        $this->registerWebserverReload($vhost->getType());
261
    }
262
263
    /**
264
     * Redirect a virtual host
265
     *
266
     * @param AccountInterface $account Account
267
     * @param VhostInterface $vhost Virtual host
268
     * @return void
269
     */
270
    public function redirectVhost(AccountInterface $account, VhostInterface $vhost)
271
    {
272
        // Re-persist the virtual host
273
        $this->persistVhost($account, $vhost);
274
275
        // Reload apache
276
        $this->registerWebserverReload($vhost->getType());
277
    }
278
279
    /**
280
     * Configure the PHP version of a virtual host
281
     *
282
     * @param AccountInterface $account Account
283
     * @param VhostInterface $vhost Virtual host
284
     * @return void
285
     * @throws \RuntimeException If the previous PHP configuration cannot be removed
286
     */
287
    public function phpVhost(AccountInterface $account, VhostInterface $vhost)
288
    {
289
        $accountHelper = new AccountHelper($account);
290
        $availableVhost = $accountHelper->vhostDirectory($vhost, false);
291
292
        // Find all existing PHP configurations
293
        foreach (glob($availableVhost.DIRECTORY_SEPARATOR.'{fpm-*.conf,*_fpm.include}', GLOB_BRACE) as $fpmConfig) {
294
            // If the previous PHP configuration cannot be removed
295
            if (!unlink($fpmConfig)) {
296
                throw new \RuntimeException(
297
                    sprintf('Cannot remove PHP configuration "%s"', basename($fpmConfig)),
298
                    1476019274
299
                );
300
            }
301
        }
302
303
        // Re-persist the virtual host
304
        $this->persistVhost($account, $vhost);
305
306
        // Reload apache
307
        $this->registerWebserverReload($vhost->getType());
308
309
        // Reload PHP
310
        $this->registerPhpReload();
311
    }
312
313
    /**
314
     * Configure a protocol based port for a virtual host
315
     *
316
     * @param AccountInterface $account Account
317
     * @param VhostInterface $vhost Virtual host
318
     * @return void
319
     */
320
    public function portVhost(AccountInterface $account, VhostInterface $vhost)
321
    {
322
        // Re-persist the virtual host
323
        $this->persistVhost($account, $vhost);
324
325
        // TODO: Issue SSL certificate if needed
326
327
        // Reload apache
328
        $this->registerWebserverReload($vhost->getType());
329
    }
330
331
    /**
332
     * Add a secondary domain to a virtual host
333
     *
334
     * @param AccountInterface $account Account
335
     * @param VhostInterface $vhost Virtual host
336
     * @return void
337
     */
338
    public function domainVhost(AccountInterface $account, VhostInterface $vhost)
339
    {
340
        // Re-persist the virtual host
341
        $this->persistVhost($account, $vhost);
342
343
        // TODO: Re-issue SSL certificate in case of domain changes
344
345
        // Reload apache
346
        $this->registerWebserverReload($vhost->getType());
347
    }
348
349
    /**
350
     * Register a webserver reload
351
     *
352
     * @param string $type Webserver type
353
     */
354
    protected function registerWebserverReload($type)
355
    {
356
        $this->reloadWebserver[$type] = true;
357
    }
358
359
    /**
360
     * Flag PHP for reloading
361
     */
362
    protected function registerPhpReload()
363
    {
364
        $this->reloadPhp = true;
365
    }
366
367
    /**
368
     * Prepare a directory
369
     *
370
     * @param string $directory Directory
371
     * @return string Prepared directory
372
     */
373
    protected function prepareDirectory($directory)
374
    {
375
        if (!is_dir($directory) && !mkdir($directory, 0777, true)) {
376
            throw new \RuntimeException(sprintf('Could not prepare directory "%s"', $directory), 1476014854);
377
        }
378
        return $directory;
379
    }
380
381
    /**
382
     * Recursively delete a directory
383
     *
384
     * @param string $directory Directory
385
     * @throws \RuntimeException If a file or directory cannot be deleted
386
     */
387
    protected function deleteDirectory($directory)
388
    {
389
        $files = array_diff(scandir($directory), array('.', '..'));
390
        foreach ($files as $file) {
391
            // Recursive call if it's a directory
392
            if (is_dir($directory.DIRECTORY_SEPARATOR.$file)) {
393
                $this->deleteDirectory($directory.DIRECTORY_SEPARATOR.$file);
394
                continue;
395
            }
396
397
            // Delete the file
398
            if (!unlink($directory.DIRECTORY_SEPARATOR.$file)) {
399
                throw new \RuntimeException(
400
                    sprintf('Could not delete file "%s"', $directory.DIRECTORY_SEPARATOR.$file),
401
                    1476017676
402
                );
403
            }
404
        }
405
406
        // Delete the directory
407
        if (!rmdir($directory)) {
408
            throw new \RuntimeException(sprintf('Could not delete directory "%s"', $directory), 1476017747);
409
        }
410
    }
411
412
    /**
413
     * Persist a virtual host
414
     *
415
     * @param AccountInterface $account Account
416
     * @param VhostInterface $vhost Virtual host
417
     */
418
    protected function persistVhost(AccountInterface $account, VhostInterface $vhost)
419
    {
420
        // Persist all virtual host files
421
        $this->persistenceAdapterFactory
422
            ->makeVhostPersistenceAdapterStrategy($vhost->getType())
423
            ->persist($account, $vhost);
424
    }
425
}
426