1<?php 2 3namespace TYPO3\CMS\Install\Updates; 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 18use Symfony\Component\Console\Output\OutputInterface; 19use TYPO3\CMS\Core\Database\ConnectionPool; 20use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction; 21use TYPO3\CMS\Core\Utility\GeneralUtility; 22 23/** 24 * Remove all backend users starting with _cli_ 25 * @internal This class is only meant to be used within EXT:install and is not part of the TYPO3 Core API. 26 */ 27class CommandLineBackendUserRemovalUpdate implements UpgradeWizardInterface, ChattyInterface, RepeatableInterface, ConfirmableInterface 28{ 29 /** 30 * @var OutputInterface 31 */ 32 protected $output; 33 34 /** 35 * @var Confirmation 36 */ 37 protected $confirmation; 38 39 public function __construct() 40 { 41 $this->confirmation = new Confirmation( 42 'Are you sure?', 43 'The following backend users will be removed: ' . implode(', ', $this->getUnneededCommandLineUsers()), 44 true 45 ); 46 } 47 48 /** 49 * @return string Unique identifier of this updater 50 */ 51 public function getIdentifier(): string 52 { 53 return 'commandLineBackendUserRemovalUpdate'; 54 } 55 56 /** 57 * @return string Title of this updater 58 */ 59 public function getTitle(): string 60 { 61 return 'Remove unneeded CLI backend users'; 62 } 63 64 /** 65 * @return string Longer description of this updater 66 */ 67 public function getDescription(): string 68 { 69 return 'The command line interface does not need to have custom _cli_* backend users anymore.' 70 . ' They can safely be deleted.'; 71 } 72 73 /** 74 * Checks if an update is needed 75 * 76 * @return bool Whether an update is needed (TRUE) or not (FALSE) 77 */ 78 public function updateNecessary(): bool 79 { 80 $needsExecution = false; 81 $usersFound = $this->getUnneededCommandLineUsers(); 82 if (!empty($usersFound)) { 83 $needsExecution = true; 84 } 85 return $needsExecution; 86 } 87 88 /** 89 * @param OutputInterface $output 90 */ 91 public function setOutput(OutputInterface $output): void 92 { 93 $this->output = $output; 94 } 95 96 /** 97 * @return string[] All new fields and tables must exist 98 */ 99 public function getPrerequisites(): array 100 { 101 return [ 102 DatabaseUpdatedPrerequisite::class, 103 ]; 104 } 105 106 /** 107 * Performs the database update to set all be_users starting with _CLI_* to deleted 108 * 109 * @return bool 110 */ 111 public function executeUpdate(): bool 112 { 113 $usersFound = $this->getUnneededCommandLineUsers(); 114 foreach ($usersFound as $userUid => $username) { 115 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('be_users'); 116 $queryBuilder->update('be_users') 117 ->where( 118 $queryBuilder->expr()->eq( 119 'uid', 120 $queryBuilder->createNamedParameter($userUid, \PDO::PARAM_INT) 121 ) 122 ) 123 // "false" is set as third parameter to have the final 124 // value in $databaseQueries and not a statement placeholder 125 ->set('deleted', 1, false) 126 ->execute(); 127 } 128 $this->output->writeln('The following backend users have been deleted:'); 129 foreach ($usersFound as $user) { 130 $this->output->writeln('* ' . $user); 131 } 132 return true; 133 } 134 135 /** 136 * Find all backend users starting with _CLI_ that are not deleted yet. 137 * 138 * @return array a list of uids 139 */ 140 protected function getUnneededCommandLineUsers(): array 141 { 142 $commandLineUsers = []; 143 144 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) 145 ->getQueryBuilderForTable('be_users'); 146 $queryBuilder->getRestrictions() 147 ->removeAll() 148 ->add(GeneralUtility::makeInstance(DeletedRestriction::class)); 149 150 $result = $queryBuilder 151 ->select('uid', 'username') 152 ->from('be_users') 153 ->where( 154 // Using query builder is complicated in this case. Get it straight, no user input is involved. 155 'LOWER(username) LIKE \'_cli_%\'', 156 $queryBuilder->expr()->neq( 157 'username', 158 $queryBuilder->createNamedParameter('_cli_', \PDO::PARAM_STR) 159 ) 160 ) 161 ->execute(); 162 163 while ($row = $result->fetch()) { 164 $commandLineUsers[$row['uid']] = $row['username']; 165 } 166 167 return $commandLineUsers; 168 } 169 170 /** 171 * Return a confirmation message instance 172 * 173 * @return Confirmation 174 */ 175 public function getConfirmation(): Confirmation 176 { 177 return $this->confirmation; 178 } 179} 180