Issues (195)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

Alpha/Controller/InstallController.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Alpha\Controller;
4
5
use Alpha\Util\Logging\Logger;
6
use Alpha\Util\Config\ConfigProvider;
7
use Alpha\Util\Http\Response;
8
use Alpha\Util\Service\ServiceFactory;
9
use Alpha\Model\ActiveRecord;
10
use Alpha\Model\Rights;
11
use Alpha\Model\Person;
12
use Alpha\Model\Type\DEnum;
13
use Alpha\Model\Type\DEnumItem;
14
use Alpha\Exception\FailedIndexCreateException;
15
use Alpha\Exception\FailedLookupCreateException;
16
use Alpha\Controller\Front\FrontController;
17
use Alpha\View\View;
18
19
/**
20
 * Controller used install the database.
21
 *
22
 * @since 1.0
23
 *
24
 * @author John Collins <[email protected]>
25
 * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
26
 * @copyright Copyright (c) 2018, John Collins (founder of Alpha Framework).
27
 * All rights reserved.
28
 *
29
 * <pre>
30
 * Redistribution and use in source and binary forms, with or
31
 * without modification, are permitted provided that the
32
 * following conditions are met:
33
 *
34
 * * Redistributions of source code must retain the above
35
 *   copyright notice, this list of conditions and the
36
 *   following disclaimer.
37
 * * Redistributions in binary form must reproduce the above
38
 *   copyright notice, this list of conditions and the
39
 *   following disclaimer in the documentation and/or other
40
 *   materials provided with the distribution.
41
 * * Neither the name of the Alpha Framework nor the names
42
 *   of its contributors may be used to endorse or promote
43
 *   products derived from this software without specific
44
 *   prior written permission.
45
 *
46
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
47
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
48
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
49
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
50
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
51
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
52
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
53
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
54
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
56
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
57
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
58
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
59
 * </pre>
60
 */
61
class InstallController extends Controller implements ControllerInterface
62
{
63
    /**
64
     * Trace logger.
65
     *
66
     * @var \Alpha\Util\Logging\Logger
67
     *
68
     * @since 1.0
69
     */
70
    private static $logger = null;
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
71
72
    /**
73
     * the constructor.
74
     *
75
     * @since 1.0
76
     */
77
    public function __construct()
78
    {
79
        self::$logger = new Logger('InstallController');
80
        self::$logger->debug('>>__construct()');
81
82
        $config = ConfigProvider::getInstance();
83
84
        parent::__construct('Public');
85
86
        // set up the title and meta details
87
        $this->setTitle('Installing '.$config->get('app.title'));
88
89
        self::$logger->debug('<<__construct');
90
    }
91
92
    /**
93
     * Handle GET requests.
94
     *
95
     * @param \Alpha\Util\Http\Request $request
96
     *
97
     * @return \Alpha\Util\Http\Response
98
     *
99
     * @since 1.0
100
     */
101
    public function doGET($request)
102
    {
103
        self::$logger->debug('>>doGET($request=['.var_export($request, true).'])');
104
105
        $config = ConfigProvider::getInstance();
106
107
        $sessionProvider = $config->get('session.provider.name');
108
        $session = ServiceFactory::getInstance($sessionProvider, 'Alpha\Util\Http\Session\SessionProviderInterface');
109
110
        // if there is nobody logged in, we will send them off to the Login controller to do so before coming back here
111
        if ($session->get('currentUser') === false) {
112
            self::$logger->info('Nobody logged in, invoking Login controller...');
113
114
            $controller = new LoginController();
115
            $controller->setName('LoginController');
116
            $controller->setRequest($request);
117
            $controller->setUnitOfWork(array('Alpha\Controller\LoginController', 'Alpha\Controller\InstallController'));
118
119
            self::$logger->debug('<<__construct');
120
121
            return $controller->doGET($request);
122
        }
123
124
        $sessionProvider = $config->get('session.provider.name');
125
        $session = ServiceFactory::getInstance($sessionProvider, 'Alpha\Util\Http\Session\SessionProviderInterface');
126
127
        $body = View::displayPageHead($this);
128
129
        $body .= '<h1>Installing the '.$config->get('app.title').' application</h1>';
130
131
        try {
132
            $body .= $this->createApplicationDirs();
133
        } catch (\Exception $e) {
134
            $body .= View::displayErrorMessage($e->getMessage());
135
            $body .= View::displayErrorMessage('Aborting.');
136
137
            return new Response(500, $body, array('Content-Type' => 'text/html'));
138
        }
139
140
        ActiveRecord::createDatabase();
141
142
        // start a new database transaction
143
        ActiveRecord::begin();
144
145
        /*
146
         * Create DEnum tables
147
         */
148
        $DEnum = new DEnum();
149
        $DEnumItem = new DEnumItem();
150
151
        try {
152
            $body .= '<p>Attempting to create the DEnum tables...';
153
            if (!$DEnum->checkTableExists()) {
154
                $DEnum->makeTable();
155
            }
156
            self::$logger->info('Created the ['.$DEnum->getTableName().'] table successfully');
157
158
            if (!$DEnumItem->checkTableExists()) {
159
                $DEnumItem->makeTable();
160
            }
161
            self::$logger->info('Created the ['.$DEnumItem->getTableName().'] table successfully');
162
163
            $body .= View::displayUpdateMessage('DEnums set up successfully.');
164
        } catch (\Exception $e) {
165
            $body .= View::displayErrorMessage($e->getMessage());
166
            $body .= View::displayErrorMessage('Aborting.');
167
            self::$logger->error($e->getMessage());
168
            ActiveRecord::rollback();
169
170
            return new Response(500, $body, array('Content-Type' => 'text/html'));
171
        }
172
173
        /*
174
         * Loop over each business object in the system, and create a table for it
175
         */
176
        $classNames = ActiveRecord::getRecordClassNames();
177
        $loadedClasses = array();
178
179
        foreach ($classNames as $classname) {
180
            array_push($loadedClasses, $classname);
181
        }
182
183
        foreach ($loadedClasses as $classname) {
184
            try {
185
                $body .= '<p>Attempting to create the table for the class ['.$classname.']...';
186
187
                try {
188
                    $Record = new $classname();
189
190
                    if (!$Record->checkTableExists()) {
191
                        $Record->makeTable();
192
                    } else {
193
                        if ($Record->checkTableNeedsUpdate()) {
194
                            $missingFields = $Record->findMissingFields();
195
196
                            $count = count($missingFields);
197
198
                            for ($i = 0; $i < $count; ++$i) {
199
                                $Record->addProperty($missingFields[$i]);
200
                            }
201
                        }
202
                    }
203
                } catch (FailedIndexCreateException $eice) {
204
                    // this are safe to ignore for now as they will be auto-created later once all of the tables are in place
205
                    self::$logger->warn($eice->getMessage());
206
                } catch (FailedLookupCreateException $elce) {
207
                    // this are safe to ignore for now as they will be auto-created later once all of the tables are in place
208
                    self::$logger->warn($elce->getMessage());
209
                }
210
211
                self::$logger->info('Created the ['.$Record->getTableName().'] table successfully');
212
                $body .= View::displayUpdateMessage('Created the ['.$Record->getTableName().'] table successfully');
213
            } catch (\Exception $e) {
214
                $body .= View::displayErrorMessage($e->getMessage());
215
                $body .= View::displayErrorMessage('Aborting.');
216
                self::$logger->error($e->getMessage());
217
                ActiveRecord::rollback();
218
219
                return new Response(500, $body, array('Content-Type' => 'text/html'));
220
            }
221
        }
222
223
        $body .= View::displayUpdateMessage('All business object tables created successfully!');
224
225
        /*
226
         * Create the Admin and Standard groups
227
         */
228
        $adminGroup = new Rights();
229
        $adminGroup->set('name', 'Admin');
230
        $standardGroup = new Rights();
231
        $standardGroup->set('name', 'Standard');
232
233
        try {
234
            try {
235
                $body .= '<p>Attempting to create the Admin and Standard groups...';
236
                $adminGroup->save();
237
                $standardGroup->save();
238
239
                self::$logger->info('Created the Admin and Standard rights groups successfully');
240
                $body .= View::displayUpdateMessage('Created the Admin and Standard rights groups successfully');
241
            } catch (FailedIndexCreateException $eice) {
242
                // this are safe to ignore for now as they will be auto-created later once all of the tables are in place
243
                self::$logger->warn($eice->getMessage());
244
            } catch (FailedLookupCreateException $elce) {
245
                // this are safe to ignore for now as they will be auto-created later once all of the tables are in place
246
                self::$logger->warn($elce->getMessage());
247
            }
248
        } catch (\Exception $e) {
249
            $body .= View::displayErrorMessage($e->getMessage());
250
            $body .= View::displayErrorMessage('Aborting.');
251
            self::$logger->error($e->getMessage());
252
            ActiveRecord::rollback();
253
254
            return new Response(500, $body, array('Content-Type' => 'text/html'));
255
        }
256
257
        /*
258
         * Save the admin user to the database in the right group
259
         */
260
        try {
261
            try {
262
                $body .= '<p>Attempting to save the Admin account...';
263
                $admin = new Person();
264
                $admin->set('username', 'Admin');
265
                $admin->set('email', $session->get('currentUser')->get('email'));
266
                $admin->set('password', $session->get('currentUser')->get('password'));
267
                $admin->save();
268
                self::$logger->info('Created the admin user account ['.$session->get('currentUser')->get('email').'] successfully');
269
270
                $adminGroup->loadByAttribute('name', 'Admin');
271
272
                $lookup = $adminGroup->getMembers()->getLookup();
273
                $lookup->setValue(array($admin->getID(), $adminGroup->getID()));
274
                $lookup->save();
275
276
                self::$logger->info('Added the admin account to the Admin group successfully');
277
                $body .= View::displayUpdateMessage('Added the admin account to the Admin group successfully');
278
            } catch (FailedIndexCreateException $eice) {
279
                // this are safe to ignore for now as they will be auto-created later once all of the tables are in place
280
                self::$logger->warn($eice->getMessage());
281
            } catch (FailedLookupCreateException $elce) {
282
                // this are safe to ignore for now as they will be auto-created later once all of the tables are in place
283
                self::$logger->warn($elce->getMessage());
284
            }
285
        } catch (\Exception $e) {
286
            $body .= View::displayErrorMessage($e->getMessage());
287
            $body .= View::displayErrorMessage('Aborting.');
288
            self::$logger->error($e->getMessage());
289
            ActiveRecord::rollback();
290
291
            return new Response(500, $body, array('Content-Type' => 'text/html'));
292
        }
293
294
        $body .= '<br><p align="center"><a href="'.FrontController::generateSecureURL('act=Alpha\Controller\ListActiveRecordsController').'">Administration Home Page</a></p><br>';
295
        $body .= View::displayPageFoot($this);
296
297
        // commit
298
        ActiveRecord::commit();
299
300
        self::$logger->info('Finished installation!');
301
        self::$logger->info('Installed the application');
302
        self::$logger->debug('<<doGET');
303
304
        return new Response(200, $body, array('Content-Type' => 'text/html'));
305
    }
306
307
    /**
308
     * Create the directories required by the application.
309
     *
310
     * @return string
311
     *
312
     * @since 2.0
313
     */
314
    public function createApplicationDirs()
315
    {
316
        self::$logger->debug('>>createApplicationDirs()');
317
318
        $config = ConfigProvider::getInstance();
319
320
        $body = '';
321
322
        // set the umask first before attempt mkdir
323
        umask(0);
324
325
        /*
326
         * Create the logs directory, then instantiate a new logger
327
         */
328
        $logsDir = $config->get('app.file.store.dir').'logs';
329
330
        $body .= '<p>Attempting to create the logs directory <em>'.$logsDir.'</em>...';
331
332
        if (!file_exists($logsDir)) {
333
            mkdir($logsDir, 0774);
334
        }
335
336
        self::$logger = new Logger('InstallController');
337
        self::$logger->info('Started installation process!');
338
        self::$logger->info('Logs directory ['.$logsDir.'] successfully created');
339
        $body .= View::displayUpdateMessage('Logs directory ['.$logsDir.'] successfully created');
340
341
        /*
342
         * Create the src directory and sub-directories
343
         */
344
        $srcDir = $config->get('app.root').'src';
345
346
        $body .= '<p>Attempting to create the src directory <em>'.$srcDir.'</em>...';
347
348
        if (!file_exists($srcDir)) {
349
            mkdir($srcDir, 0774);
350
        }
351
352
        self::$logger->info('Source directory ['.$srcDir.'] successfully created');
353
        $body .= View::displayUpdateMessage('Source directory ['.$srcDir.'] successfully created');
354
355
        $srcDir = $config->get('app.root').'src/Model';
356
357
        if (!file_exists($srcDir)) {
358
            mkdir($srcDir, 0774);
359
        }
360
361
        self::$logger->info('Source directory ['.$srcDir.'] successfully created');
362
        $body .= View::displayUpdateMessage('Source directory ['.$srcDir.'] successfully created');
363
364
        $srcDir = $config->get('app.root').'src/View';
365
366
        if (!file_exists($srcDir)) {
367
            mkdir($srcDir, 0774);
368
        }
369
370
        self::$logger->info('Source directory ['.$srcDir.'] successfully created');
371
        $body .= View::displayUpdateMessage('Source directory ['.$srcDir.'] successfully created');
372
373
        /*
374
         * Create the attachments directory
375
         */
376
        $attachmentsDir = $config->get('app.file.store.dir').'attachments';
377
378
        $body .= '<p>Attempting to create the attachments directory <em>'.$attachmentsDir.'</em>...';
379
380
        if (!file_exists($attachmentsDir)) {
381
            mkdir($attachmentsDir, 0774);
382
        }
383
384
        self::$logger->info('Attachments directory ['.$attachmentsDir.'] successfully created');
385
        $body .= View::displayUpdateMessage('Attachments directory ['.$attachmentsDir.'] successfully created');
386
387
        /*
388
         * Create the cache directory and sub-directories
389
         */
390
        $cacheDir = $config->get('app.file.store.dir').'cache';
391
        $filesDir = $config->get('app.file.store.dir').'cache/files';
392
        $imagesDir = $config->get('app.file.store.dir').'cache/images';
393
        $xlsDir = $config->get('app.file.store.dir').'cache/xls';
394
395
        // cache
396
        $body .= '<p>Attempting to create the cache directory <em>'.$cacheDir.'</em>...';
397
        if (!file_exists($cacheDir)) {
398
            mkdir($cacheDir, 0774);
399
        }
400
401
        self::$logger->info('Cache directory ['.$cacheDir.'] successfully created');
402
        $body .= View::displayUpdateMessage('Cache directory ['.$cacheDir.'] successfully created');
403
404
        // cache/files
405
        $body .= '<p>Attempting to create the files cache directory <em>'.$filesDir.'</em>...';
406
        if (!file_exists($filesDir)) {
407
            mkdir($filesDir, 0774);
408
        }
409
410
        self::$logger->info('Cache directory ['.$filesDir.'] successfully created');
411
        $body .= View::displayUpdateMessage('Cache directory ['.$filesDir.'] successfully created');
412
413
        // cache/images
414
        $body .= '<p>Attempting to create the cache directory <em>'.$imagesDir.'</em>...';
415
        if (!file_exists($imagesDir)) {
416
            mkdir($imagesDir, 0774);
417
        }
418
419
        self::$logger->info('Cache directory ['.$imagesDir.'] successfully created');
420
        $body .= View::displayUpdateMessage('Cache directory ['.$imagesDir.'] successfully created');
421
422
        // cache/xls
423
        $body .= '<p>Attempting to create the cache directory <em>'.$xlsDir.'</em>...';
424
        if (!file_exists($xlsDir)) {
425
            mkdir($xlsDir, 0774);
426
        }
427
428
        self::$logger->info('Cache directory ['.$xlsDir.'] successfully created');
429
        $body .= View::displayUpdateMessage('Cache directory ['.$xlsDir.'] successfully created');
430
431
        self::$logger->debug('<<createApplicationDirs');
432
433
        return $body;
434
    }
435
436
    /**
437
     * Custom version of the check rights method that only checks for a session for the config admin username/password,
438
     * when the system database is not set-up.
439
     *
440
     * @return boolean|null
441
     *
442
     * @since 1.0
443
     */
444
    public function checkRights()
445
    {
446
        self::$logger->debug('>>checkRights()');
447
448
        $config = ConfigProvider::getInstance();
449
        $sessionProvider = $config->get('session.provider.name');
450
        $session = ServiceFactory::getInstance($sessionProvider, 'Alpha\Util\Http\Session\SessionProviderInterface');
451
452
        if ($this->getVisibility() == 'Public') {
453
            self::$logger->debug('<<checkRights [true]');
454
455
            return true;
456
        }
457
458
        if (ActiveRecord::isInstalled()) {
459
            self::$logger->debug('<<checkRights [false]');
460
461
            return false;
462
        }
463
464
        // the person is logged in?
465
        if ($session->get('currentUser') !== false) {
466
            if ($session->get('currentUser')->get('email') == $config->get('app.install.username')) {
467
                self::$logger->debug('<<checkRights [true]');
468
469
                return true;
470
            }
471
        }
472
    }
473
}
474