Merge branch '3.0'
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / acp / form / RescueModeForm.class.php
CommitLineData
dfef03a8
AE
1<?php
2namespace wcf\acp\form;
3use wcf\data\application\Application;
4use wcf\data\application\ApplicationAction;
5use wcf\data\application\ApplicationEditor;
6use wcf\data\application\ApplicationList;
7use wcf\data\user\authentication\failure\UserAuthenticationFailure;
8use wcf\data\user\authentication\failure\UserAuthenticationFailureAction;
9use wcf\data\user\User;
10use wcf\form\AbstractCaptchaForm;
11use wcf\system\application\ApplicationHandler;
12use wcf\system\exception\IllegalLinkException;
13use wcf\system\exception\NamedUserException;
14use wcf\system\exception\UserInputException;
15use wcf\system\style\StyleHandler;
16use wcf\system\user\authentication\EmailUserAuthentication;
17use wcf\system\user\authentication\UserAuthenticationFactory;
18use wcf\system\WCF;
19use wcf\system\WCFACP;
20use wcf\util\FileUtil;
21use wcf\util\HeaderUtil;
22use wcf\util\StringUtil;
23use wcf\util\UserUtil;
24
25/**
26 * Shows the rescue mode form.
27 *
28 * @author Alexander Ebert
c839bd49 29 * @copyright 2001-2018 WoltLab GmbH
dfef03a8 30 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
e71525e4 31 * @package WoltLabSuite\Core\Acp\Form
dfef03a8
AE
32 */
33class RescueModeForm extends AbstractCaptchaForm {
34 /**
893aace3 35 * @var Application[]
dfef03a8
AE
36 */
37 public $applications;
38
39 /**
fea72e74 40 * @var string[][]
dfef03a8
AE
41 */
42 public $applicationValues = [];
43
44 /**
45 * login password
46 * @var string
47 */
48 public $password = '';
49
50 /**
893aace3 51 * @var User
dfef03a8
AE
52 */
53 public $user;
54
55 /**
56 * login username
57 * @var string
58 */
59 public $username = '';
60
61 /**
62 * @inheritDoc
63 */
64 public $useCaptcha = false;
65
66 /**
67 * @inheritDoc
68 */
69 public function __run() {
70 if (!WCFACP::inRescueMode()) {
71 // redirect to currently active application's ACP
72 HeaderUtil::redirect(ApplicationHandler::getInstance()->getActiveApplication()->getPageURL() . 'acp/');
73 exit;
74 }
75
76 parent::__run();
77 }
78
79 /**
80 * @inheritDoc
81 */
82 public function readParameters() {
83 parent::readParameters();
84
85 // request style generation to prevent issues when using the proxy parameter
86 StyleHandler::getInstance()->getStylesheet(true);
87
88 if (isset($_GET['proxy'])) {
89 switch ($_GET['proxy']) {
90 case 'css':
91 $file = WCF_DIR . 'acp/style/style.css';
92
93 header('Content-Type: text/css');
94 break;
95
96 case 'logo':
97 $file = WCF_DIR . 'images/default-logo.png';
98
99 header('Content-Type: image/png');
100 break;
101
102 default:
103 throw new IllegalLinkException();
104 break;
105 }
106
107 header('Expires: '.gmdate('D, d M Y H:i:s', time() + 3600).' GMT');
108 header('Last-Modified: Mon, 26 Jul 1997 05:00:00 GMT');
109 header('Cache-Control: public, max-age=3600');
110
111 readfile($file);
112 exit;
113 }
114
115 // check authentication failures
116 if (ENABLE_USER_AUTHENTICATION_FAILURE) {
117 $failures = UserAuthenticationFailure::countIPFailures(UserUtil::getIpAddress());
118 if (USER_AUTHENTICATION_FAILURE_IP_BLOCK && $failures >= USER_AUTHENTICATION_FAILURE_IP_BLOCK) {
119 throw new NamedUserException(WCF::getLanguage()->getDynamicVariable('wcf.user.login.blocked'));
120 }
121 if (USER_AUTHENTICATION_FAILURE_IP_CAPTCHA && $failures >= USER_AUTHENTICATION_FAILURE_IP_CAPTCHA) {
122 $this->useCaptcha = true;
123 }
124 else if (USER_AUTHENTICATION_FAILURE_USER_CAPTCHA) {
125 if (isset($_POST['username'])) {
126 $user = User::getUserByUsername(StringUtil::trim($_POST['username']));
127 if (!$user->userID) $user = User::getUserByEmail(StringUtil::trim($_POST['username']));
128
129 if ($user->userID) {
130 $failures = UserAuthenticationFailure::countUserFailures($user->userID);
131 if (USER_AUTHENTICATION_FAILURE_USER_CAPTCHA && $failures >= USER_AUTHENTICATION_FAILURE_USER_CAPTCHA) {
132 $this->useCaptcha = true;
133 }
134 }
135 }
136 }
137 }
138
139 // read applications
140 $applicationList = new ApplicationList();
141 $applicationList->readObjects();
142 $this->applications = $applicationList->getObjects();
143 }
144
145 /**
146 * @inheritDoc
147 */
148 public function readFormParameters() {
149 parent::readFormParameters();
150
151 if (isset($_POST['username'])) $this->username = StringUtil::trim($_POST['username']);
152 if (isset($_POST['password'])) $this->password = $_POST['password'];
153 if (isset($_POST['applicationValues']) && is_array($_POST['applicationValues'])) $this->applicationValues = $_POST['applicationValues'];
154 }
155
156 /**
157 * Validates the user access data.
158 */
159 protected function validateUser() {
160 try {
161 $this->user = UserAuthenticationFactory::getInstance()->getUserAuthentication()->loginManually($this->username, $this->password);
162 }
163 catch (UserInputException $e) {
164 if ($e->getField() == 'username') {
165 try {
166 $this->user = EmailUserAuthentication::getInstance()->loginManually($this->username, $this->password);
167 }
168 catch (UserInputException $e2) {
169 if ($e2->getField() == 'username') throw $e;
170 throw $e2;
171 }
172 }
173 else {
174 throw $e;
175 }
176 }
177
178 // simulate login in order to access permissions
179 WCF::getSession()->disableUpdate();
180 WCF::getSession()->changeUser($this->user, true);
181
6476e7a1 182 if (!WCF::getSession()->getPermission('admin.configuration.canManageApplication')) {
dfef03a8
AE
183 throw new UserInputException('username', 'notAuthorized');
184 }
185 }
186
187 protected function validateApplications() {
188 $usedPaths = [];
189 foreach ($this->applications as $application) {
190 $packageID = $application->packageID;
191
192 $domainName = $this->applicationValues[$packageID]['domainName'];
193 $domainName = preg_replace('~^https?://~', '', $domainName);
194 $domainName = FileUtil::removeTrailingSlash($domainName);
195 $domainName = StringUtil::trim($domainName);
196
197 if (empty($domainName)) {
198 throw new UserInputException("application_{$packageID}_domainName");
199 }
200 else if (preg_match('~[/#\?&]~', $domainName)) {
201 throw new UserInputException("application_{$packageID}_domainName", 'containsPath');
202 }
203
204 $domainPath = FileUtil::addLeadingSlash(FileUtil::addTrailingSlash($this->applicationValues[$packageID]['domainPath']));
205
206 $this->applicationValues[$packageID]['domainName'] = $domainName;
207 $this->applicationValues[$packageID]['domainPath'] = $domainPath;
208
209 if (isset($usedPaths[$domainName])) {
d38697d0 210 if (isset($usedPaths[$domainName][$domainPath])) {
dfef03a8
AE
211 WCF::getTPL()->assign('conflictApplication', $this->applications[$usedPaths[$domainName][$domainPath]]->getPackage());
212 throw new UserInputException("application_{$packageID}_domainPath", 'conflict');
213 }
214 }
215 else {
216 $usedPaths[$domainName] = [];
217 }
218
219 $usedPaths[$domainName][$domainPath] = $packageID;
220 }
221 }
222
223 /**
224 * @inheritDoc
225 */
226 public function submit() {
227 parent::submit();
228
229 // save authentication failure
230 if (ENABLE_USER_AUTHENTICATION_FAILURE) {
231 if ($this->errorField == 'username' || $this->errorField == 'password') {
232 $action = new UserAuthenticationFailureAction([], 'create', [
233 'data' => [
234 'environment' => 'admin',
63b9817b 235 'userID' => $this->user !== null ? $this->user->userID : null,
dfef03a8
AE
236 'username' => $this->username,
237 'time' => TIME_NOW,
238 'ipAddress' => UserUtil::getIpAddress(),
239 'userAgent' => UserUtil::getUserAgent()
240 ]
241 ]);
242 $action->executeAction();
243
244 if ($this->captchaObjectType) {
245 $this->captchaObjectType->getProcessor()->reset();
246 }
247 }
248 }
249 }
250
251 /**
252 * @inheritDoc
253 */
254 public function validate() {
255 // bypass security token validation
256 $_POST['t'] = WCF::getSession()->getSecurityToken();
257
258 parent::validate();
259
260 // error handling
261 if (empty($this->username)) {
262 throw new UserInputException('username');
263 }
264
265 if (empty($this->password)) {
266 throw new UserInputException('password');
267 }
268
269 $this->validateUser();
270 $this->validateApplications();
271 }
272
273 /**
274 * @inheritDoc
275 */
276 public function save() {
277 parent::save();
278
279 // update applications
280 foreach ($this->applications as $application) {
281 $applicationEditor = new ApplicationEditor($application);
282 $applicationEditor->update([
283 'domainName' => $this->applicationValues[$application->packageID]['domainName'],
284 'domainPath' => $this->applicationValues[$application->packageID]['domainPath'],
285 'cookieDomain' => $this->applicationValues[$application->packageID]['domainName']
286 ]);
287 }
288
289 // rebuild cookie domain and paths
290 $applicationAction = new ApplicationAction([], 'rebuild');
291 $applicationAction->executeAction();
292
293 // reload currently active application to avoid outdated cache data
294 $application = ApplicationHandler::getInstance()->getActiveApplication();
295 $application = new Application($application->packageID);
296
297 // redirect to ACP of currently active application
298 HeaderUtil::redirect($application->getPageURL() . 'acp/');
299 exit;
300 }
301
302 /**
303 * @inheritDoc
304 */
305 public function readData() {
306 parent::readData();
307
308 // get preferred username
309 if (empty($_POST)) {
310 if (isset($_COOKIE[COOKIE_PREFIX.'userID'])) {
311 $user = new User(intval($_COOKIE[COOKIE_PREFIX.'userID']));
312 if ($user->userID) $this->username = $user->username;
313 }
314
315 foreach ($this->applications as $application) {
316 $this->applicationValues[$application->packageID] = [
317 'domainName' => $application->domainName,
318 'domainPath' => $application->domainPath
319 ];
320 }
321 }
322 }
323
324 /**
325 * @inheritDoc
326 */
327 public function assignVariables() {
328 parent::assignVariables();
329
330 WCF::getTPL()->assign([
331 'applications' => $this->applications,
332 'applicationValues' => $this->applicationValues,
333 'pageURL' => WCFACP::getRescueModePageURL() . 'acp/index.php?rescue-mode/',
334 'password' => $this->password,
335 'username' => $this->username
336 ]);
337 }
338}