Passed
Push — master ( 3b03ef...f3e3ab )
by
unknown
15:15
created

getOriginalUserIdWhenInSwitchUserMode()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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