Completed
Push — master ( c6c96a...d012cb )
by Fèvre
04:09
created

Installer::createDatabaseConfig()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 9
Ratio 100 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 9
loc 9
rs 9.6666
cc 2
eloc 6
nc 2
nop 2
1
<?php
2
/**
3
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
4
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
5
 *
6
 * Licensed under The MIT License
7
 * For full copyright and license information, please see the LICENSE.txt
8
 * Redistributions of files must retain the above copyright notice.
9
 *
10
 * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
11
 * @link      http://cakephp.org CakePHP(tm) Project
12
 * @since     3.0.0
13
 * @license   http://www.opensource.org/licenses/mit-license.php MIT License
14
 */
15
namespace App\Console;
16
17
use Cake\Auth\DefaultPasswordHasher;
18
use Cake\Utility\Security;
19
use Composer\Script\Event;
20
use Exception;
21
22
/**
23
 * Provides installation hooks for when this application is installed via
24
 * composer. Customize this class to suit your needs.
25
 */
26
class Installer
27
{
28
29
    /**
30
     * Does some routine installation tasks so people don't have to.
31
     *
32
     * @param \Composer\Script\Event $event The composer event object.
33
     *
34
     * @return void
35
     */
36
    public static function postInstall(Event $event)
37
    {
38
        $io = $event->getIO();
39
40
        $rootDir = dirname(dirname(__DIR__));
41
        static::createAppConfig($rootDir, $io);
42
        static::createWritableDirectories($rootDir, $io);
43
        static::createRecaptchaConfig($rootDir, $io);
44
        static::createDatabaseConfig($rootDir, $io);
45
        static::setDatabaseName($rootDir, $io);
46
47
        // ask if the permissions should be changed
48
        if ($io->isInteractive()) {
49
            $validator = function ($arg) {
50
                if (in_array($arg, ['Y', 'y', 'N', 'n'])) {
51
                    return $arg;
52
                }
53
                throw new Exception('This is not a valid answer. Please choose Y or n.');
54
            };
55
            $setFolderPermissions = $io->askAndValidate(
56
                '<info>Set Folder Permissions ? (Default to Y)</info> [<comment>Y,n</comment>]? ',
57
                $validator,
58
                10,
59
                'Y'
60
            );
61
62
            if (in_array($setFolderPermissions, ['Y', 'y'])) {
63
                static::setFolderPermissions($rootDir, $io);
64
            }
65
        } else {
66
            static::setFolderPermissions($rootDir, $io);
67
        }
68
69
        $newKey = static::setSecuritySalt($rootDir, $io);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $newKey is correct as static::setSecuritySalt($rootDir, $io) (which targets App\Console\Installer::setSecuritySalt()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

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

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

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

Loading history...
70
        static::setAccountPassword($rootDir, $io, $newKey);
71
72
        if (class_exists('\Cake\Codeception\Console\Installer')) {
73
            \Cake\Codeception\Console\Installer::customizeCodeceptionBinary($event);
74
        }
75
    }
76
77
    /**
78
     * Create the config/app.php file if it does not exist.
79
     *
80
     * @param string $dir The application's root directory.
81
     * @param \Composer\IO\IOInterface $io IO interface to write to console.
82
     *
83
     * @return void
84
     */
85 View Code Duplication
    public static function createAppConfig($dir, $io)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
86
    {
87
        $appConfig = $dir . '/config/app.php';
88
        $defaultConfig = $dir . '/config/app.default.php';
89
        if (!file_exists($appConfig)) {
90
            copy($defaultConfig, $appConfig);
91
            $io->write('Created `config/app.php` file');
92
        }
93
    }
94
95
    /**
96
     * Create the config/database.php file if it does not exist.
97
     *
98
     * @param string $dir The application's root directory.
99
     * @param \Composer\IO\IOInterface $io IO interface to write to console.
100
     *
101
     * @return void
102
     */
103 View Code Duplication
    public static function createDatabaseConfig($dir, $io)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
104
    {
105
        $databaseConfig = $dir . '/config/database.php';
106
        $defaultConfig = $dir . '/config/database.default.php';
107
        if (!file_exists($databaseConfig)) {
108
            copy($defaultConfig, $databaseConfig);
109
            $io->write('Created `config/database.php` file');
110
        }
111
    }
112
113
    /**
114
     * Create the `logs` and `tmp` directories.
115
     *
116
     * @param string $dir The application's root directory.
117
     * @param \Composer\IO\IOInterface $io IO interface to write to console.
118
     * @return void
119
     */
120
    public static function createWritableDirectories($dir, $io)
121
    {
122
        $paths = [
123
            'logs',
124
            'tmp',
125
            'tmp/cache',
126
            'tmp/cache/models',
127
            'tmp/cache/persistent',
128
            'tmp/cache/views',
129
            'tmp/sessions',
130
            'tmp/tests'
131
        ];
132
133
        foreach ($paths as $path) {
134
            $path = $dir . '/' . $path;
135
            if (!file_exists($path)) {
136
                mkdir($path);
137
                $io->write('Created `' . $path . '` directory');
138
            }
139
        }
140
    }
141
142
    /**
143
     * Create the config/recaptcha.php file if it does not exist.
144
     *
145
     * @param string $dir The application's root directory.
146
     * @param \Composer\IO\IOInterface $io IO interface to write to console.
147
     *
148
     * @return void
149
     */
150 View Code Duplication
    public static function createRecaptchaConfig($dir, $io)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
151
    {
152
        $appConfig = $dir . '/config/recaptcha.php';
153
        $defaultConfig = $dir . '/config/recaptcha.default.php';
154
        if (!file_exists($appConfig)) {
155
            copy($defaultConfig, $appConfig);
156
            $io->write('Created `config/recaptcha.php` file');
157
        }
158
    }
159
160
    /**
161
     * Set globally writable permissions on the "tmp" and "logs" directory.
162
     *
163
     * This is not the most secure default, but it gets people up and running quickly.
164
     *
165
     * @param string $dir The application's root directory.
166
     * @param \Composer\IO\IOInterface $io IO interface to write to console.
167
     * @return void
168
     */
169
    public static function setFolderPermissions($dir, $io)
170
    {
171
        // Change the permissions on a path and output the results.
172
        $changePerms = function ($path, $perms, $io) {
173
            // Get permission bits from stat(2) result.
174
            $currentPerms = fileperms($path) & 0777;
175
            if (($currentPerms & $perms) == $perms) {
176
                return;
177
            }
178
179
            $res = chmod($path, $currentPerms | $perms);
180
            if ($res) {
181
                $io->write('Permissions set on ' . $path);
182
            } else {
183
                $io->write('Failed to set permissions on ' . $path);
184
            }
185
        };
186
187
        $walker = function ($dir, $perms, $io) use (&$walker, $changePerms) {
188
            $files = array_diff(scandir($dir), ['.', '..']);
189
            foreach ($files as $file) {
190
                $path = $dir . '/' . $file;
191
192
                if (!is_dir($path)) {
193
                    continue;
194
                }
195
196
                $changePerms($path, $perms, $io);
197
                $walker($path, $perms, $io);
198
            }
199
        };
200
201
        $worldWritable = bindec('0000000111');
202
        $walker($dir . '/tmp', $worldWritable, $io);
203
        $changePerms($dir . '/tmp', $worldWritable, $io);
204
        $changePerms($dir . '/logs', $worldWritable, $io);
205
    }
206
207
    /**
208
     * Set the datasources.default.database value in the application's config file.
209
     *
210
     * @param string $dir The application's root directory.
211
     * @param \Composer\IO\IOInterface $io IO interface to write to console.
212
     *
213
     * @return void
214
     */
215 View Code Duplication
    public static function setDatabaseName($dir, $io)
216
    {
217
        $config = $dir . '/config/database.php';
218
        $content = file_get_contents($config);
219
220
        $databaseName = $io->ask('What is your new database name ? ', 'xeta');
221
        $content = str_replace('__DATABASE__', $databaseName, $content, $count);
222
223
        if ($count == 0) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $count of type integer|null to 0; this is ambiguous as not only 0 == 0 is true, but null == 0 is true, too. Consider using a strict comparison ===.
Loading history...
224
            $io->write('No Datasources.default.database placeholder to replace.');
225
226
            return;
227
        }
228
229
        $result = file_put_contents($config, $content);
230
        if ($result) {
231
            $io->write('Updated Datasources.default.database value in config/app.php');
232
233
            return;
234
        }
235
        $io->write('Unable to update Datasources.default.database value.');
236
    }
237
238
    /**
239
     * Set the security.salt value in the application's config file.
240
     *
241
     * @param string $dir The application's root directory.
242
     * @param \Composer\IO\IOInterface $io IO interface to write to console.
243
     * @return void
244
     */
245 View Code Duplication
    public static function setSecuritySalt($dir, $io)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
246
    {
247
        $config = $dir . '/config/app.php';
248
        $content = file_get_contents($config);
249
250
        $newKey = hash('sha256', Security::randomBytes(64));
251
        $content = str_replace('__SALT__', $newKey, $content, $count);
252
253
        if ($count == 0) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $count of type integer|null to 0; this is ambiguous as not only 0 == 0 is true, but null == 0 is true, too. Consider using a strict comparison ===.
Loading history...
254
            $io->write('No Security.salt placeholder to replace.');
255
256
            return;
257
        }
258
259
        $result = file_put_contents($config, $content);
260
        if ($result) {
261
            $io->write('Updated Security.salt value in config/app.php');
262
263
            return;
264
        }
265
        $io->write('Unable to update Security.salt value.');
266
    }
267
268
    /**
269
     * Set up the admin and member password for the database.
270
     *
271
     * @param string $dir The application's root directory.
272
     * @param \Composer\IO\IOInterface $io IO interface to write to console.
273
     * @param string $newKey The new security.salt.
274
     *
275
     * @return void
276
     */
277
    public static function setAccountPassword($dir, $io, $newKey = null)
278
    {
279
        if ($newKey == null) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $newKey of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
280
            $io->write('The new Security.salt value is empty in config/app.php, can\'t set up the password.');
281
282
            return;
283
        }
284
285
        $database = $dir . '/config/Schema/xeta.sql';
286
        $content = file_get_contents($database);
287
288
        $adminPass = 'administrator';
289
        $memberPass = 'testaccount';
290
291
        $hasher = new DefaultPasswordHasher();
292
293
        $replacement = [
294
            $hasher->hash($adminPass),
295
            $hasher->hash($memberPass),
296
        ];
297
298
        $search = [
299
            '__ADMINPASSWORD__',
300
            '__MEMBERPASSWORD__'
301
        ];
302
303
        $content = str_replace($search, $replacement, $content, $count);
304
305
        if ($count != 2) {
306
            $io->write('Error, there was no password to replace.');
307
308
            return;
309
        }
310
311
        $result = file_put_contents($database, $content);
312
313
        if ($result) {
314
            $io->write('Set up Admin & Member passwords successfully !');
315
316
            return;
317
        }
318
319
        $io->write('Unable to set up Admin & Member passwords.');
320
    }
321
}
322