+
+
+ +{* content above was taken from 'header.tpl' *} + +
+

{lang}wcf.acp.rescueMode{/lang}

+
+ +

{lang}wcf.acp.rescueMode.description{/lang}

+ +{include file='formError'} + + +
+
+

{lang}wcf.acp.rescueMode.credentials{/lang}

+ {lang}wcf.acp.rescueMode.credentials.description{/lang} +
+ + +
+
+ + {if $errorField == 'username'} + + {if $errorType == 'empty'} + {lang}wcf.global.form.error.empty{/lang} + {elseif $errorType == 'notAuthorized'} + {lang}wcf.acp.rescueMode.username.notAuthorized{/lang} + {else} + {lang}wcf.user.username.error.{@$errorType}{/lang} + {/if} + + {/if} +
+ + + +
+
+ + {if $errorField == 'password'} + + {if $errorType == 'empty'} + {lang}wcf.global.form.error.empty{/lang} + {else} + {lang}wcf.user.password.error.{@$errorType}{/lang} + {/if} + + {/if} +
+ +
+ + {include file='captcha'} + +
+
+

{lang}wcf.acp.rescueMode.application{/lang}

+ {lang}wcf.acp.rescueMode.application.description{/lang} +
+ + {foreach from=$applications item=application} + {capture assign=applicationSectionDomain}application_{@$application->packageID}_domainName{/capture} + {capture assign=applicationSectionPath}application_{@$application->packageID}_domainPath{/capture} + + +
+
+
+ {lang}wcf.acp.application.domainName{/lang} + +
+ {if $errorField == $applicationSectionDomain} + + {if $errorType == 'empty'} + {lang}wcf.global.form.error.empty{/lang} + {else} + {lang}wcf.acp.application.domainName.error.{@$errorType}{/lang} + {/if} + + {/if} +
+
+
+ {lang}wcf.acp.application.domainPath{/lang} + +
+ {if $errorField == $applicationSectionPath} + + {if $errorType == 'empty'} + {lang}wcf.global.form.error.empty{/lang} + {else} + {lang}wcf.acp.application.domainPath.error.{@$errorType}{/lang} + {/if} + + {/if} +
+ + {/foreach} +
+ +
+ + + {* do not use the security token here because we cannot rely on working cookies *} +
+ + +{include file='footer'} diff --git a/wcfsetup/install/files/lib/acp/form/RescueModeForm.class.php b/wcfsetup/install/files/lib/acp/form/RescueModeForm.class.php new file mode 100644 index 0000000000..9f3e16d17d --- /dev/null +++ b/wcfsetup/install/files/lib/acp/form/RescueModeForm.class.php @@ -0,0 +1,340 @@ + + * @package com.woltlab.wcf + * @subpackage acp.form + * @category Community Framework + */ +class RescueModeForm extends AbstractCaptchaForm { + /** + * @var Application[] + */ + public $applications; + + /** + * @var string[] + */ + public $applicationValues = []; + + /** + * login password + * @var string + */ + public $password = ''; + + /** + * @var User + */ + public $user; + + /** + * login username + * @var string + */ + public $username = ''; + + /** + * @inheritDoc + */ + public $useCaptcha = false; + + /** + * @inheritDoc + */ + public function __run() { + if (!WCFACP::inRescueMode()) { + // redirect to currently active application's ACP + HeaderUtil::redirect(ApplicationHandler::getInstance()->getActiveApplication()->getPageURL() . 'acp/'); + exit; + } + + parent::__run(); + } + + /** + * @inheritDoc + */ + public function readParameters() { + parent::readParameters(); + + // request style generation to prevent issues when using the proxy parameter + StyleHandler::getInstance()->getStylesheet(true); + + if (isset($_GET['proxy'])) { + switch ($_GET['proxy']) { + case 'css': + $file = WCF_DIR . 'acp/style/style.css'; + + header('Content-Type: text/css'); + break; + + case 'logo': + $file = WCF_DIR . 'images/default-logo.png'; + + header('Content-Type: image/png'); + break; + + default: + throw new IllegalLinkException(); + break; + } + + header('Expires: '.gmdate('D, d M Y H:i:s', time() + 3600).' GMT'); + header('Last-Modified: Mon, 26 Jul 1997 05:00:00 GMT'); + header('Cache-Control: public, max-age=3600'); + + readfile($file); + exit; + } + + // check authentication failures + if (ENABLE_USER_AUTHENTICATION_FAILURE) { + $failures = UserAuthenticationFailure::countIPFailures(UserUtil::getIpAddress()); + if (USER_AUTHENTICATION_FAILURE_IP_BLOCK && $failures >= USER_AUTHENTICATION_FAILURE_IP_BLOCK) { + throw new NamedUserException(WCF::getLanguage()->getDynamicVariable('wcf.user.login.blocked')); + } + if (USER_AUTHENTICATION_FAILURE_IP_CAPTCHA && $failures >= USER_AUTHENTICATION_FAILURE_IP_CAPTCHA) { + $this->useCaptcha = true; + } + else if (USER_AUTHENTICATION_FAILURE_USER_CAPTCHA) { + if (isset($_POST['username'])) { + $user = User::getUserByUsername(StringUtil::trim($_POST['username'])); + if (!$user->userID) $user = User::getUserByEmail(StringUtil::trim($_POST['username'])); + + if ($user->userID) { + $failures = UserAuthenticationFailure::countUserFailures($user->userID); + if (USER_AUTHENTICATION_FAILURE_USER_CAPTCHA && $failures >= USER_AUTHENTICATION_FAILURE_USER_CAPTCHA) { + $this->useCaptcha = true; + } + } + } + } + } + + // read applications + $applicationList = new ApplicationList(); + $applicationList->readObjects(); + $this->applications = $applicationList->getObjects(); + } + + /** + * @inheritDoc + */ + public function readFormParameters() { + parent::readFormParameters(); + + if (isset($_POST['username'])) $this->username = StringUtil::trim($_POST['username']); + if (isset($_POST['password'])) $this->password = $_POST['password']; + if (isset($_POST['applicationValues']) && is_array($_POST['applicationValues'])) $this->applicationValues = $_POST['applicationValues']; + } + + /** + * Validates the user access data. + */ + protected function validateUser() { + try { + $this->user = UserAuthenticationFactory::getInstance()->getUserAuthentication()->loginManually($this->username, $this->password); + } + catch (UserInputException $e) { + if ($e->getField() == 'username') { + try { + $this->user = EmailUserAuthentication::getInstance()->loginManually($this->username, $this->password); + } + catch (UserInputException $e2) { + if ($e2->getField() == 'username') throw $e; + throw $e2; + } + } + else { + throw $e; + } + } + + // simulate login in order to access permissions + WCF::getSession()->disableUpdate(); + WCF::getSession()->changeUser($this->user, true); + + if (!WCF::getSession()->getPermission('admin.system.canManageApplication')) { + throw new UserInputException('username', 'notAuthorized'); + } + } + + protected function validateApplications() { + $usedPaths = []; + foreach ($this->applications as $application) { + $packageID = $application->packageID; + + $domainName = $this->applicationValues[$packageID]['domainName']; + $domainName = preg_replace('~^https?://~', '', $domainName); + $domainName = FileUtil::removeTrailingSlash($domainName); + $domainName = StringUtil::trim($domainName); + + if (empty($domainName)) { + throw new UserInputException("application_{$packageID}_domainName"); + } + else if (preg_match('~[/#\?&]~', $domainName)) { + throw new UserInputException("application_{$packageID}_domainName", 'containsPath'); + } + + $domainPath = FileUtil::addLeadingSlash(FileUtil::addTrailingSlash($this->applicationValues[$packageID]['domainPath'])); + + $this->applicationValues[$packageID]['domainName'] = $domainName; + $this->applicationValues[$packageID]['domainPath'] = $domainPath; + + if (isset($usedPaths[$domainName])) { + if(isset($usedPaths[$domainName][$domainPath])) { + WCF::getTPL()->assign('conflictApplication', $this->applications[$usedPaths[$domainName][$domainPath]]->getPackage()); + throw new UserInputException("application_{$packageID}_domainPath", 'conflict'); + } + } + else { + $usedPaths[$domainName] = []; + } + + $usedPaths[$domainName][$domainPath] = $packageID; + } + } + + /** + * @inheritDoc + */ + public function submit() { + parent::submit(); + + // save authentication failure + if (ENABLE_USER_AUTHENTICATION_FAILURE) { + if ($this->errorField == 'username' || $this->errorField == 'password') { + $action = new UserAuthenticationFailureAction([], 'create', [ + 'data' => [ + 'environment' => 'admin', + 'userID' => ($this->user !== null ? $this->user->userID : null), + 'username' => $this->username, + 'time' => TIME_NOW, + 'ipAddress' => UserUtil::getIpAddress(), + 'userAgent' => UserUtil::getUserAgent() + ] + ]); + $action->executeAction(); + + if ($this->captchaObjectType) { + $this->captchaObjectType->getProcessor()->reset(); + } + } + } + } + + /** + * @inheritDoc + */ + public function validate() { + // bypass security token validation + $_POST['t'] = WCF::getSession()->getSecurityToken(); + + parent::validate(); + + // error handling + if (empty($this->username)) { + throw new UserInputException('username'); + } + + if (empty($this->password)) { + throw new UserInputException('password'); + } + + $this->validateUser(); + $this->validateApplications(); + } + + /** + * @inheritDoc + */ + public function save() { + parent::save(); + + // update applications + foreach ($this->applications as $application) { + $applicationEditor = new ApplicationEditor($application); + $applicationEditor->update([ + 'domainName' => $this->applicationValues[$application->packageID]['domainName'], + 'domainPath' => $this->applicationValues[$application->packageID]['domainPath'], + 'cookieDomain' => $this->applicationValues[$application->packageID]['domainName'] + ]); + } + + // rebuild cookie domain and paths + $applicationAction = new ApplicationAction([], 'rebuild'); + $applicationAction->executeAction(); + + // reload currently active application to avoid outdated cache data + $application = ApplicationHandler::getInstance()->getActiveApplication(); + $application = new Application($application->packageID); + + // redirect to ACP of currently active application + HeaderUtil::redirect($application->getPageURL() . 'acp/'); + exit; + } + + /** + * @inheritDoc + */ + public function readData() { + parent::readData(); + + // get preferred username + if (empty($_POST)) { + if (isset($_COOKIE[COOKIE_PREFIX.'userID'])) { + $user = new User(intval($_COOKIE[COOKIE_PREFIX.'userID'])); + if ($user->userID) $this->username = $user->username; + } + + foreach ($this->applications as $application) { + $this->applicationValues[$application->packageID] = [ + 'domainName' => $application->domainName, + 'domainPath' => $application->domainPath + ]; + } + } + } + + /** + * @inheritDoc + */ + public function assignVariables() { + parent::assignVariables(); + + WCF::getTPL()->assign([ + 'applications' => $this->applications, + 'applicationValues' => $this->applicationValues, + 'pageURL' => WCFACP::getRescueModePageURL() . 'acp/index.php?rescue-mode/', + 'password' => $this->password, + 'username' => $this->username + ]); + } +} diff --git a/wcfsetup/install/files/lib/acp/page/IndexPage.class.php b/wcfsetup/install/files/lib/acp/page/IndexPage.class.php index e34de6bea3..93bb2d0ab7 100755 --- a/wcfsetup/install/files/lib/acp/page/IndexPage.class.php +++ b/wcfsetup/install/files/lib/acp/page/IndexPage.class.php @@ -2,14 +2,13 @@ namespace wcf\acp\page; use wcf\page\AbstractPage; use wcf\system\package\PackageInstallationDispatcher; -use wcf\system\request\RequestHandler; use wcf\system\WCF; /** * Shows the welcome page in admin control panel. * * @author Marcel Werk - * @copyright 2001-2015 WoltLab GmbH + * @copyright 2001-2016 WoltLab GmbH * @license GNU Lesser General Public License * @package com.woltlab.wcf * @subpackage acp.page @@ -18,22 +17,22 @@ use wcf\system\WCF; class IndexPage extends AbstractPage { /** * server information - * @var array + * @var string[] */ - public $server = array(); + public $server = []; /** - * @see \wcf\page\IPage::readData() + * @inheritDoc */ public function readData() { parent::readData(); - $this->server = array( + $this->server = [ 'os' => PHP_OS, 'webserver' => (isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : ''), 'mySQLVersion' => WCF::getDB()->getVersion(), 'load' => '' - ); + ]; // get load if (function_exists('sys_getloadavg')) { @@ -45,7 +44,7 @@ class IndexPage extends AbstractPage { } /** - * @see \wcf\page\IPage::assignVariables() + * @inheritDoc */ public function assignVariables() { parent::assignVariables(); @@ -61,15 +60,14 @@ class IndexPage extends AbstractPage { $usersAwaitingApproval = $row['count']; } - WCF::getTPL()->assign(array( - 'inRescueMode' => RequestHandler::getInstance()->inRescueMode(), + WCF::getTPL()->assign([ 'server' => $this->server, 'usersAwaitingApproval' => $usersAwaitingApproval - )); + ]); } /** - * @see \wcf\page\IPage::show() + * @inheritDoc */ public function show() { // check package installation queue @@ -77,9 +75,7 @@ class IndexPage extends AbstractPage { $queueID = PackageInstallationDispatcher::checkPackageInstallationQueue(); if ($queueID) { - WCF::getTPL()->assign(array( - 'queueID' => $queueID - )); + WCF::getTPL()->assign(['queueID' => $queueID]); WCF::getTPL()->display('packageInstallationSetup'); exit; } diff --git a/wcfsetup/install/files/lib/system/WCFACP.class.php b/wcfsetup/install/files/lib/system/WCFACP.class.php index 9513e839b6..a9c1316f1e 100644 --- a/wcfsetup/install/files/lib/system/WCFACP.class.php +++ b/wcfsetup/install/files/lib/system/WCFACP.class.php @@ -25,6 +25,18 @@ use wcf\util\HeaderUtil; * @category Community Framework */ class WCFACP extends WCF { + /** + * rescue mode + * @var boolean + */ + protected static $inRescueMode; + + /** + * URL to WCF within rescue mode + * @var string + */ + protected static $rescueModePageURL; + /** * Calls all init functions of the WCF and the WCFACP class. */ @@ -56,6 +68,47 @@ class WCFACP extends WCF { EventHandler::getInstance()->fireAction($this, 'initialized'); } + /** + * Returns true if ACP is currently in rescue mode. + * + * @return boolean + */ + public static function inRescueMode() { + if (self::$inRescueMode === null) { + self::$inRescueMode = false; + + if (isset($_SERVER['HTTP_HOST'])) { + self::$inRescueMode = true; + + foreach (ApplicationHandler::getInstance()->getApplications() as $application) { + if ($application->domainName === $_SERVER['HTTP_HOST']) { + self::$inRescueMode = false; + break; + } + } + + if (self::$inRescueMode) { + self::$rescueModePageURL = RouteHandler::getProtocol() . $_SERVER['HTTP_HOST'] . RouteHandler::getPath(['acp']); + } + } + } + + return self::$inRescueMode; + } + + /** + * Returns URL for rescue mode page. + * + * @return string + */ + public static function getRescueModePageURL() { + if (self::inRescueMode()) { + return self::$rescueModePageURL; + } + + return ''; + } + /** * Does the user authentication. */ @@ -63,7 +116,16 @@ class WCFACP extends WCF { // this is a work-around since neither RequestHandler // nor RouteHandler are populated right now $pathInfo = RouteHandler::getPathInfo(); - if (empty($pathInfo) || !preg_match('~^/?(acp-?captcha|login|logout)/~i', $pathInfo)) { + + if (self::inRescueMode()) { + if (!preg_match('~^/?rescue-mode/~', $pathInfo)) { + $redirectURI = self::$rescueModePageURL . 'acp/index.php?rescue-mode/'; + + HeaderUtil::redirect($redirectURI); + exit; + } + } + else if (empty($pathInfo) || !preg_match('~^/?(acp-?captcha|login|logout)/~i', $pathInfo)) { if (WCF::getUser()->userID == 0) { // work-around for AJAX-requests within ACP if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') { @@ -76,18 +138,10 @@ class WCFACP extends WCF { throw new SystemException("You have aborted the installation, therefore this installation is unusable. You are required to reinstall the software."); } - // fallback for unknown host (rescue mode) - if ($application->domainName != $_SERVER['HTTP_HOST']) { - $pageURL = RouteHandler::getProtocol() . $_SERVER['HTTP_HOST'] . RouteHandler::getPath(array('acp')); - } - else { - $pageURL = $application->getPageURL(); - } - // drop session id $redirectURI = preg_replace('~[&\?]s=[a-f0-9]{40}(&|$)~', '', WCF::getSession()->requestURI); - $path = $pageURL . 'acp/index.php?login/' . SID_ARG_2ND_NOT_ENCODED . '&url=' . rawurlencode(RouteHandler::getProtocol() . $_SERVER['HTTP_HOST'] . $redirectURI); + $path = $application->getPageURL() . 'acp/index.php?login/' . SID_ARG_2ND_NOT_ENCODED . '&url=' . rawurlencode(RouteHandler::getProtocol() . $_SERVER['HTTP_HOST'] . $redirectURI); HeaderUtil::redirect($path); exit; diff --git a/wcfsetup/install/files/lib/system/request/RequestHandler.class.php b/wcfsetup/install/files/lib/system/request/RequestHandler.class.php index 00f8383965..df6ae2b03b 100644 --- a/wcfsetup/install/files/lib/system/request/RequestHandler.class.php +++ b/wcfsetup/install/files/lib/system/request/RequestHandler.class.php @@ -26,13 +26,13 @@ class RequestHandler extends SingletonFactory { * active request object * @var Request */ - protected $activeRequest = null; + protected $activeRequest; /** * true, if current domain mismatch any known domain * @var boolean */ - protected $inRescueMode = true; + protected $inRescueMode = false; /** * indicates if the request is an acp request @@ -44,29 +44,7 @@ class RequestHandler extends SingletonFactory { * @see \wcf\system\SingletonFactory::init() */ protected function init() { - if (isset($_SERVER['HTTP_HOST'])) { - foreach (ApplicationHandler::getInstance()->getApplications() as $application) { - if ($application->domainName == $_SERVER['HTTP_HOST']) { - $this->inRescueMode = false; - break; - } - } - - // check if WCF is running as standalone - if ($this->inRescueMode() && PACKAGE_ID == 1) { - if (ApplicationHandler::getInstance()->getWCF()->domainName == $_SERVER['HTTP_HOST']) { - $this->inRescueMode = false; - } - } - } - else { - // when using cli, no rescue mode is provided - $this->inRescueMode = false; - } - - if (class_exists('wcf\system\WCFACP', false)) { - $this->isACPRequest = true; - } + $this->isACPRequest = class_exists('wcf\system\WCFACP', false); } /** diff --git a/wcfsetup/install/files/style/layout/form.scss b/wcfsetup/install/files/style/layout/form.scss index 6a0e65574f..a1dbd514e1 100644 --- a/wcfsetup/install/files/style/layout/form.scss +++ b/wcfsetup/install/files/style/layout/form.scss @@ -189,21 +189,12 @@ textarea { margin-bottom: 5px; } - > .inputPrefix { - flex: 0 0 auto; - - &.button { - border-radius: 0; - margin-right: 5px; - } - } - + > .inputPrefix, > .inputSuffix { flex: 0 0 auto; &.button { border-radius: 0; - margin-left: 5px; } &:not(.button) { @@ -211,13 +202,24 @@ textarea { border: 1px solid $wcfButtonBorder; color: $wcfButtonText; cursor: default; - margin-left: 5px; padding: 3px 5px; } } + > .inputPrefix { + margin-right: 5px; + } + + > .inputSuffix { + margin-left: 5px; + } + input { flex: 1 auto; + + & + .inputPrefix { + margin-left: 5px; + } } } diff --git a/wcfsetup/install/lang/de.xml b/wcfsetup/install/lang/de.xml index 01f81406c3..133bcd7544 100644 --- a/wcfsetup/install/lang/de.xml +++ b/wcfsetup/install/lang/de.xml @@ -1317,6 +1317,16 @@ GmbH=Gesellschaft mit beschränkter Haftung]]> + + + + + + + + + + diff --git a/wcfsetup/install/lang/en.xml b/wcfsetup/install/lang/en.xml index 315e2071b7..1e795cf6a4 100644 --- a/wcfsetup/install/lang/en.xml +++ b/wcfsetup/install/lang/en.xml @@ -1316,6 +1316,16 @@ GmbH=Gesellschaft mit beschränkter Haftung]]> + + + + + + + + + +