CommandLineUserAuthentication.php 6 KB
Newer Older
1
<?php
2

3
4
5
6
7
8
9
10
11
12
13
14
15
/*
 * This file is part of the TYPO3 CMS project.
 *
 * It is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License, either version 2
 * of the License, or any later version.
 *
 * For the full copyright and license information, please read the
 * LICENSE.txt file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

16
17
namespace TYPO3\CMS\Core\Authentication;

18
use TYPO3\CMS\Core\Core\Environment;
19
use TYPO3\CMS\Core\Crypto\PasswordHashing\PasswordHashFactory;
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
use TYPO3\CMS\Core\Crypto\Random;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
use TYPO3\CMS\Core\Utility\GeneralUtility;

/**
 * TYPO3 backend user authentication on a CLI level
 * Auto-logs in, only allowed on CLI
 */
class CommandLineUserAuthentication extends BackendUserAuthentication
{

    /**
     * The username of the CLI user (there is only one)
     * @var string
     */
    protected $username = '_cli_';

    /**
     * Constructor, only allowed in CLI mode
40
41
     *
     * @throws \RuntimeException
42
43
44
     */
    public function __construct()
    {
45
        if (!Environment::isCli()) {
46
47
            throw new \RuntimeException('Creating a CLI-based user object on non-CLI level is not allowed', 1483971165);
        }
48
49
50
        if (!$this->isUserAllowedToLogin()) {
            throw new \RuntimeException('Login Error: TYPO3 is in maintenance mode at the moment. Only administrators are allowed access.', 1483971855);
        }
51
52
53
54
        $this->dontSetCookie = true;
        parent::__construct();
    }

55
    /**
56
     * Replacement for AbstractUserAuthentication::start()
57
58
     *
     * We do not need support for sessions, cookies, $_GET-modes, the postUserLookup hook or
59
     * a database connection during CLI Bootstrap
60
61
62
     */
    public function start()
    {
63
        // do nothing
64
65
66
    }

    /**
67
     * Replacement for AbstractUserAuthentication::checkAuthentication()
68
69
70
71
72
     *
     * Not required in CLI mode, therefore empty.
     */
    public function checkAuthentication()
    {
73
        // do nothing
74
75
    }

76
77
78
79
80
81
82
83
    /**
     * On CLI there is no session and no switched user
     */
    public function getOriginalUserIdWhenInSwitchUserMode(): ?int
    {
        return null;
    }

84
85
86
87
88
89
90
91
92
    /**
     * Logs-in the _CLI_ user. It does not need to check for credentials.
     *
     * @throws \RuntimeException when the user could not log in or it is an admin
     */
    public function authenticate()
    {
        // check if a _CLI_ user exists, if not, create one
        $this->setBeUserByName($this->username);
93
        if (empty($this->user['uid'])) {
94
            // create a new BE user in the database
95
            if (!$this->checkIfCliUserExists()) {
96
97
98
99
100
101
                $this->createCliUser();
            } else {
                throw new \RuntimeException('No backend user named "_cli_" could be authenticated, maybe this user is "hidden"?', 1484050401);
            }
            $this->setBeUserByName($this->username);
        }
102
        if (empty($this->user['uid'])) {
103
104
105
106
107
108
109
110
            throw new \RuntimeException('No backend user named "_cli_" could be created.', 1476107195);
        }
        // The groups are fetched and ready for permission checking in this initialization.
        $this->fetchGroupData();
        $this->backendSetUC();
    }

    /**
111
     * Logs in the TYPO3 Backend user "_cli_"
112
113
114
115
116
117
118
119
120
121
122
123
124
     *
     * @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.
     */
    public function backendCheckLogin($proceedIfNoUserIsLoggedIn = false)
    {
        $this->authenticate();
    }

    /**
     * Determines whether a CLI backend user is allowed to access TYPO3.
     * Only when adminOnly is off (=0), and only allowed for admins and CLI users (=2)
     *
     * @return bool Whether the CLI user is allowed to access TYPO3
125
     * @internal
126
     */
127
    public function isUserAllowedToLogin()
128
129
130
131
132
133
134
    {
        return in_array((int)$GLOBALS['TYPO3_CONF_VARS']['BE']['adminOnly'], [0, 2], true);
    }

    /**
     * Check if a user with username "_cli_" exists. Deleted users are left out
     * but hidden and start / endtime restricted users are considered.
135
136
     *
     * @return bool true if the user exists
137
     */
138
    protected function checkIfCliUserExists()
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
    {
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('be_users');
        $queryBuilder->getRestrictions()
            ->removeAll()
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
        $count = $queryBuilder
            ->count('*')
            ->from('be_users')
            ->where($queryBuilder->expr()->eq('username', $queryBuilder->createNamedParameter('_cli_')))
            ->execute()
            ->fetchColumn(0);
        return (bool)$count;
    }

    /**
     * Create a record in the DB table be_users called "_cli_" with no other information
     */
    protected function createCliUser()
    {
        $userFields = [
            'username' => $this->username,
            'password' => $this->generateHashedPassword(),
            'admin'    => 1,
162
163
            'tstamp'   => $GLOBALS['EXEC_TIME'] ?? time(),
            'crdate'   => $GLOBALS['EXEC_TIME'] ?? time(),
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
        ];

        $databaseConnection = GeneralUtility::makeInstance(ConnectionPool::class)
            ->getConnectionForTable('be_users');
        $databaseConnection->insert('be_users', $userFields);
    }

    /**
     * This function returns a salted hashed key.
     *
     * @return string a random password
     */
    protected function generateHashedPassword()
    {
        $cryptoService = GeneralUtility::makeInstance(Random::class);
        $password = $cryptoService->generateRandomBytes(20);
180
        $hashInstance = GeneralUtility::makeInstance(PasswordHashFactory::class)->getDefaultHashInstance('BE');
Christian Kuhn's avatar
Christian Kuhn committed
181
        return $hashInstance->getHashedPassword($password);
182
183
    }
}