[BUGFIX] Do not depend on a database connection in CLI authentication
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Authentication / CommandLineUserAuthentication.php
1 <?php
2 namespace TYPO3\CMS\Core\Authentication;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Core\Environment;
18 use TYPO3\CMS\Core\Crypto\PasswordHashing\PasswordHashFactory;
19 use TYPO3\CMS\Core\Crypto\Random;
20 use TYPO3\CMS\Core\Database\ConnectionPool;
21 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
22 use TYPO3\CMS\Core\Utility\GeneralUtility;
23
24 /**
25 * TYPO3 backend user authentication on a CLI level
26 * Auto-logs in, only allowed on CLI
27 */
28 class CommandLineUserAuthentication extends BackendUserAuthentication
29 {
30
31 /**
32 * The username of the CLI user (there is only one)
33 * @var string
34 */
35 protected $username = '_cli_';
36
37 /**
38 * Constructor, only allowed in CLI mode
39 *
40 * @throws \RuntimeException
41 */
42 public function __construct()
43 {
44 if (!Environment::isCli()) {
45 throw new \RuntimeException('Creating a CLI-based user object on non-CLI level is not allowed', 1483971165);
46 }
47 if (!$this->isUserAllowedToLogin()) {
48 throw new \RuntimeException('Login Error: TYPO3 is in maintenance mode at the moment. Only administrators are allowed access.', 1483971855);
49 }
50 $this->dontSetCookie = true;
51 parent::__construct();
52 }
53
54 /**
55 * Replacement for AbstactUserAuthentication::start()
56 *
57 * We do not need support for sessions, cookies, $_GET-modes, the postUserLookup hook or
58 * a database connectiona during CLI Bootstrap
59 */
60 public function start()
61 {
62 $this->logger->debug('## Beginning of auth logging.');
63 // svConfig is unused, but we set it, as the property is public and might be used by extensions
64 $this->svConfig = $GLOBALS['TYPO3_CONF_VARS']['SVCONF']['auth'] ?? [];
65 }
66
67 /**
68 * Replacement for AbstactUserAuthentication::checkAuthentication()
69 *
70 * Not required in CLI mode, therefore empty.
71 */
72 public function checkAuthentication()
73 {
74 }
75
76 /**
77 * Logs-in the _CLI_ user. It does not need to check for credentials.
78 *
79 * @throws \RuntimeException when the user could not log in or it is an admin
80 */
81 public function authenticate()
82 {
83 // check if a _CLI_ user exists, if not, create one
84 $this->setBeUserByName($this->username);
85 if (!$this->user['uid']) {
86 // create a new BE user in the database
87 if (!$this->checkIfCliUserExists()) {
88 $this->createCliUser();
89 } else {
90 throw new \RuntimeException('No backend user named "_cli_" could be authenticated, maybe this user is "hidden"?', 1484050401);
91 }
92 $this->setBeUserByName($this->username);
93 }
94 if (!$this->user['uid']) {
95 throw new \RuntimeException('No backend user named "_cli_" could be created.', 1476107195);
96 }
97 // The groups are fetched and ready for permission checking in this initialization.
98 $this->fetchGroupData();
99 $this->backendSetUC();
100 // activate this functionality for DataHandler
101 $this->uc['recursiveDelete'] = true;
102 }
103
104 /**
105 * Logs in the TYPO3 Backend user "_cli_"
106 *
107 * @param bool $proceedIfNoUserIsLoggedIn if this option is set, then there won't be a redirect to the login screen of the Backend - used for areas in the backend which do not need user rights like the login page.
108 */
109 public function backendCheckLogin($proceedIfNoUserIsLoggedIn = false)
110 {
111 $this->authenticate();
112 }
113
114 /**
115 * Determines whether a CLI backend user is allowed to access TYPO3.
116 * Only when adminOnly is off (=0), and only allowed for admins and CLI users (=2)
117 *
118 * @return bool Whether the CLI user is allowed to access TYPO3
119 */
120 protected function isUserAllowedToLogin()
121 {
122 return in_array((int)$GLOBALS['TYPO3_CONF_VARS']['BE']['adminOnly'], [0, 2], true);
123 }
124
125 /**
126 * Check if a user with username "_cli_" exists. Deleted users are left out
127 * but hidden and start / endtime restricted users are considered.
128 *
129 * @return bool true if the user exists
130 */
131 protected function checkIfCliUserExists()
132 {
133 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('be_users');
134 $queryBuilder->getRestrictions()
135 ->removeAll()
136 ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
137 $count = $queryBuilder
138 ->count('*')
139 ->from('be_users')
140 ->where($queryBuilder->expr()->eq('username', $queryBuilder->createNamedParameter('_cli_')))
141 ->execute()
142 ->fetchColumn(0);
143 return (bool)$count;
144 }
145
146 /**
147 * Create a record in the DB table be_users called "_cli_" with no other information
148 */
149 protected function createCliUser()
150 {
151 $userFields = [
152 'username' => $this->username,
153 'password' => $this->generateHashedPassword(),
154 'admin' => 1,
155 'tstamp' => $GLOBALS['EXEC_TIME'],
156 'crdate' => $GLOBALS['EXEC_TIME']
157 ];
158
159 $databaseConnection = GeneralUtility::makeInstance(ConnectionPool::class)
160 ->getConnectionForTable('be_users');
161 $databaseConnection->insert('be_users', $userFields);
162 }
163
164 /**
165 * This function returns a salted hashed key.
166 *
167 * @return string a random password
168 */
169 protected function generateHashedPassword()
170 {
171 $cryptoService = GeneralUtility::makeInstance(Random::class);
172 $password = $cryptoService->generateRandomBytes(20);
173 $hashInstance = GeneralUtility::makeInstance(PasswordHashFactory::class)->getDefaultHashInstance('BE');
174 return $hashInstance->getHashedPassword($password);
175 }
176 }