Completed
Push — master ( d1e7e8...3d9931 )
by
unknown
15:20 queued 01:03
created

PermissionsCheck::checkUpdate()   A

Complexity

Conditions 2
Paths 3

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 9
nc 3
nop 0
dl 0
loc 12
rs 9.9666
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the TYPO3 CMS project.
7
 *
8
 * It is free software; you can redistribute it and/or modify it under
9
 * the terms of the GNU General Public License, either version 2
10
 * of the License, or any later version.
11
 *
12
 * For the full copyright and license information, please read the
13
 * LICENSE.txt file that was distributed with this source code.
14
 *
15
 * The TYPO3 project - inspiring people to share!
16
 */
17
18
namespace TYPO3\CMS\Install\Database;
19
20
use Doctrine\DBAL\Schema\AbstractSchemaManager;
21
use TYPO3\CMS\Core\Database\Connection;
22
use TYPO3\CMS\Core\Database\ConnectionPool;
23
use TYPO3\CMS\Core\Utility\GeneralUtility;
24
use TYPO3\CMS\Install\Configuration\Exception;
25
26
/**
27
 * Check all required permissions within the install process.
28
 * @internal This is NOT an API class, it is for internal use in the install tool only.
29
 */
30
class PermissionsCheck
31
{
32
    private $testTableName = 't3install_test_table';
33
    private $messages = [];
34
35
    public function checkCreateAndDrop(): self
36
    {
37
        $tableCreated = $this->checkCreateTable($this->testTableName);
38
        if (!$tableCreated) {
39
            $this->messages[] = 'The database user needs CREATE permissions.';
40
        }
41
        $tableDropped = $this->checkDropTable($this->testTableName);
42
        if (!$tableDropped) {
43
            $this->messages[] = 'The database user needs DROP permissions.';
44
        }
45
        if ($tableCreated && !$tableDropped) {
46
            $this->messages[] = sprintf('Attention: A test table with name "%s" was created but could not be deleted, please remove the table manually!', $this->testTableName);
47
        }
48
        if (!$tableCreated || !$tableDropped) {
49
            throw new Exception('A test table could not be created or dropped, skipping all further checks now', 1590850369);
50
        }
51
        return $this;
52
    }
53
54
    public function checkAlter(): self
55
    {
56
        $this->checkCreateTable($this->testTableName);
57
        $connection = $this->getConnection();
58
        $schemaCurrent = $this->getSchemaManager()->createSchema();
59
        $schemaNew = $this->getSchemaManager()->createSchema();
60
        $schemaCurrent
61
            ->getTable($this->testTableName)
62
            ->addColumn('index_test', 'integer', ['unsigned' => true]);
63
        $platform = $connection->getDatabasePlatform();
64
        try {
65
            foreach ($schemaNew->getMigrateToSql($schemaCurrent, $platform) as $query) {
66
                $connection->executeQuery($query);
67
            }
68
        } catch (\Exception $e) {
69
            $this->messages[] = 'The database user needs ALTER permission';
70
        }
71
        $this->checkDropTable($this->testTableName);
72
        return $this;
73
    }
74
75
    public function checkIndex(): self
76
    {
77
        if ($this->checkCreateTable($this->testTableName)) {
78
            $connection = $this->getConnection();
79
            $schemaCurrent = $this->getSchemaManager()->createSchema();
80
            $schemaNew = $this->getSchemaManager()->createSchema();
81
            $testTable = $schemaCurrent->getTable($this->testTableName);
82
            $testTable->addColumn('index_test', 'integer', ['unsigned' => true]);
83
            $testTable->addIndex(['index_test'], 'test_index');
84
            $platform = $connection->getDatabasePlatform();
85
            try {
86
                foreach ($schemaNew->getMigrateToSql($schemaCurrent, $platform) as $query) {
87
                    $connection->executeQuery($query);
88
                }
89
            } catch (\Exception $e) {
90
                $this->checkDropTable($this->testTableName);
91
                $this->messages[] = 'The database user needs INDEX permission';
92
            }
93
            $this->checkDropTable($this->testTableName);
94
        }
95
        return $this;
96
    }
97
98
    public function checkCreateTemporaryTable(): self
99
    {
100
        $this->checkCreateTable($this->testTableName);
101
        $connection = $this->getConnection();
102
        try {
103
            $sql = 'CREATE TEMPORARY TABLE %s AS (SELECT id FROM %s )';
104
            $connection->exec(sprintf($sql, $this->testTableName . '_tmp', $this->testTableName));
105
        } catch (\Exception $e) {
106
            $this->messages[] = 'The database user needs CREATE TEMPORARY TABLE permission';
107
        }
108
        $this->checkDropTable($this->testTableName);
109
        return $this;
110
    }
111
112
    public function checkLockTable(): self
113
    {
114
        $this->checkCreateTable($this->testTableName);
115
        $connection = $this->getConnection();
116
        try {
117
            $connection->exec(sprintf('LOCK TABLES %s WRITE', $this->testTableName));
118
            $connection->exec('UNLOCK TABLES;');
119
        } catch (\Exception $e) {
120
            $this->messages[] = 'The database user needs LOCK TABLE permission';
121
        }
122
        $this->checkDropTable($this->testTableName);
123
        return $this;
124
    }
125
126
    public function checkSelect(): self
127
    {
128
        $this->checkCreateTable($this->testTableName);
129
        $connection = $this->getConnection();
130
        try {
131
            $connection->insert($this->testTableName, ['id' => 1]);
132
            $connection->select(['id'], $this->testTableName);
133
        } catch (\Exception $e) {
134
            $this->messages[] = 'The database user needs SELECT permission';
135
        }
136
        $this->checkDropTable($this->testTableName);
137
        return $this;
138
    }
139
140
    public function checkInsert(): self
141
    {
142
        $this->checkCreateTable($this->testTableName);
143
        $connection = $this->getConnection();
144
        try {
145
            $connection->insert($this->testTableName, ['id' => 1]);
146
        } catch (\Exception $e) {
147
            $this->messages[] = 'The database user needs INSERT permission';
148
        }
149
        $this->checkDropTable($this->testTableName);
150
        return $this;
151
    }
152
153
    public function checkUpdate(): self
154
    {
155
        $this->checkCreateTable($this->testTableName);
156
        $connection = $this->getConnection();
157
        try {
158
            $connection->insert($this->testTableName, ['id' => 1]);
159
            $connection->update($this->testTableName, ['id' => 2], ['id' => 1]);
160
        } catch (\Exception $e) {
161
            $this->messages[] = 'The database user needs UPDATE permission';
162
        }
163
        $this->checkDropTable($this->testTableName);
164
        return $this;
165
    }
166
167
    public function checkDelete(): self
168
    {
169
        $this->checkCreateTable($this->testTableName);
170
        $connection = $this->getConnection();
171
        try {
172
            $connection->insert($this->testTableName, ['id' => 1]);
173
            $connection->delete($this->testTableName, ['id' => 1]);
174
        } catch (\Exception $e) {
175
            $this->messages[] = 'The database user needs DELETE permission';
176
        }
177
        $this->checkDropTable($this->testTableName);
178
        return $this;
179
    }
180
181
    public function getMessages(): array
182
    {
183
        return $this->messages;
184
    }
185
186
    private function checkCreateTable(string $tablename): bool
187
    {
188
        $connection = $this->getConnection();
189
        $schema = $connection->getSchemaManager()->createSchema();
190
        $testTable = $schema->createTable($tablename);
191
        $testTable->addColumn('id', 'integer', ['unsigned' => true]);
192
        $testTable->setPrimaryKey(['id']);
193
        $platform = $connection->getDatabasePlatform();
194
        try {
195
            foreach ($schema->toSql($platform) as $query) {
196
                $connection->executeQuery($query);
197
            }
198
        } catch (\Exception $e) {
199
            return false;
200
        }
201
        return true;
202
    }
203
204
    private function checkDropTable(string $tablename): bool
205
    {
206
        $connection = $this->getConnection();
207
        try {
208
            $schemaCurrent = $connection->getSchemaManager()->createSchema();
209
            $schemaNew = $connection->getSchemaManager()->createSchema();
210
211
            $schemaNew->dropTable($tablename);
212
            $platform = $connection->getDatabasePlatform();
213
            foreach ($schemaCurrent->getMigrateToSql($schemaNew, $platform) as $query) {
214
                $connection->executeQuery($query);
215
            }
216
        } catch (\Exception $e) {
217
            return false;
218
        }
219
        return true;
220
    }
221
222
    private function getConnection(): Connection
223
    {
224
        return GeneralUtility::makeInstance(ConnectionPool::class)
225
            ->getConnectionByName(ConnectionPool::DEFAULT_CONNECTION_NAME);
226
    }
227
228
    private function getSchemaManager(): AbstractSchemaManager
229
    {
230
        return $this->getConnection()->getSchemaManager();
231
    }
232
}
233