diff --git a/lib/Auth/Process/PrivacyideaAuthProc.php b/lib/Auth/Process/PrivacyideaAuthProc.php index b9cbec4f73594e7f2fe5e159e9792ba5a0c8f887..53de1bfd3cc6447757cf7cd9d79d518d7b2fbee4 100644 --- a/lib/Auth/Process/PrivacyideaAuthProc.php +++ b/lib/Auth/Process/PrivacyideaAuthProc.php @@ -61,7 +61,8 @@ class PrivacyideaAuthProc extends ProcessingFilter $state['privacyidea:privacyidea:ui']['messageOverride'] = $this->authProcConfig['messageOverride'] ?? null; // If set in config, allow to check the IP of the client and to control the 2FA depending on the client IP. - // It can be used to configure that a user does not need to provide a second factor when logging in from the local network. + // It can be used to configure that a user does not need to provide a second factor + // when logging in from the local network. if (!empty($this->authProcConfig['excludeClientIPs'])) { $ip = Utils::getClientIP(); if ($this->matchIP($ip, $this->authProcConfig['excludeClientIPs'])) { @@ -70,7 +71,8 @@ class PrivacyideaAuthProc extends ProcessingFilter } } - // If set to true in config, selectively disable the privacyIDEA authentication using the entityID and/or SAML attributes. + // If set to true in config, selectively disable the privacyIDEA authentication + // using the entityID and/or SAML attributes. // The skipping will be done in self::isPrivacyIDEADisabled if (!empty($this->authProcConfig['checkEntityID']) && $this->authProcConfig['checkEntityID'] === true) { $stateId = State::saveState($state, 'privacyidea:privacyidea'); @@ -85,8 +87,10 @@ class PrivacyideaAuthProc extends ProcessingFilter } // SSO check if authentication should be skipped - if (array_key_exists('SSO', $this->authProcConfig) - && $this->authProcConfig['SSO'] === true) { + if ( + array_key_exists('SSO', $this->authProcConfig) + && $this->authProcConfig['SSO'] === true + ) { if (Utils::checkForValidSSO($state)) { Logger::debug('privacyIDEA: SSO data valid - logging in..'); ProcessingChain::resumeProcessing($state); @@ -108,8 +112,12 @@ class PrivacyideaAuthProc extends ProcessingFilter } // Check if triggerChallenge or a call with a static pass to /validate/check should be done - if (!empty($this->authProcConfig['doTriggerChallenge']) && $this->authProcConfig['doTriggerChallenge'] === true) { - // Call /validate/triggerchallenge with the service account from the configuration to trigger all token of the user + if ( + !empty($this->authProcConfig['doTriggerChallenge']) + && $this->authProcConfig['doTriggerChallenge'] === true + ) { + // Call /validate/triggerchallenge with the service account from the configuration + // to trigger all token of the user $stateId = State::saveState($state, 'privacyidea:privacyidea'); if (!$this->pi->serviceAccountAvailable()) { Logger::error( @@ -127,7 +135,10 @@ class PrivacyideaAuthProc extends ProcessingFilter $stateId = Utils::processPIResponse($stateId, $response); } } - } elseif (!empty($this->authProcConfig['tryFirstAuthentication']) && $this->authProcConfig['tryFirstAuthentication'] === true) { + } elseif ( + !empty($this->authProcConfig['tryFirstAuthentication']) + && $this->authProcConfig['tryFirstAuthentication'] === true + ) { // Call /validate/check with a static pass from the configuration // This could already end the authentication with the "passOnNoToken" policy, or it could trigger challenges $response = Utils::authenticatePI($state, [ @@ -311,8 +322,8 @@ class PrivacyideaAuthProc extends ProcessingFilter if (!empty($matchedAttrs)) { $ret = true; Logger::debug('privacyidea:checkEntityID: Requesting entityID in ' . - 'list, but excluded by at least one attribute regexp "' . $attrKey . - '" = "' . $matchedAttrs[0] . '".'); + 'list, but excluded by at least one attribute regexp "' . $attrKey . + '" = "' . $matchedAttrs[0] . '".'); break; } } diff --git a/lib/Auth/Source/PrivacyideaAuthSource.php b/lib/Auth/Source/PrivacyideaAuthSource.php index d0168a48990e875b1e330d9d222d4654dd3c8e0c..3f25e25c21ade6916390724edcdb176625baaff4 100644 --- a/lib/Auth/Source/PrivacyideaAuthSource.php +++ b/lib/Auth/Source/PrivacyideaAuthSource.php @@ -75,9 +75,11 @@ class PrivacyideaAuthSource extends UserPassBase Logger::debug('privacyIDEA AuthSource authenticate'); // SSO check if authentication should be skipped - if (array_key_exists('SSO', $this->authSourceConfig) && + if ( + array_key_exists('SSO', $this->authSourceConfig) && $this->authSourceConfig['SSO'] === true && - Utils::checkForValidSSO($state)) { + Utils::checkForValidSSO($state) + ) { $session = Session::getSessionFromRequest(); $attributes = $session->getData('privacyidea:privacyidea', 'attributes'); Logger('privacyIDEA: SSO retrieved attributes from session: ' . print_r($attributes, true)); @@ -144,8 +146,10 @@ class PrivacyideaAuthSource extends UserPassBase $state['privacyidea:privacyidea']['username'] = $username; $stateId = State::saveState($state, 'privacyidea:privacyidea'); - if (array_key_exists('doTriggerChallenge', $source->authSourceConfig) - && $source->authSourceConfig['doTriggerChallenge'] === true) { + if ( + array_key_exists('doTriggerChallenge', $source->authSourceConfig) + && $source->authSourceConfig['doTriggerChallenge'] === true + ) { if (!empty($username) && $source->pi->serviceAccountAvailable()) { try { $response = $source->pi->triggerChallenge($username); @@ -153,8 +157,10 @@ class PrivacyideaAuthSource extends UserPassBase Utils::handlePrivacyIDEAException($e, $state); } } - } elseif (array_key_exists('doSendPassword', $source->authSourceConfig) - && $source->authSourceConfig['doSendPassword'] === true) { + } elseif ( + array_key_exists('doSendPassword', $source->authSourceConfig) + && $source->authSourceConfig['doSendPassword'] === true + ) { if (!empty($username)) { try { $response = $source->pi->validateCheck($username, $password); @@ -219,7 +225,8 @@ class PrivacyideaAuthSource extends UserPassBase /* * In order to be able to register a logout handler for the session (mandatory for SSO to work), * the authority is required in the session's authData. - * The authority can be put there by invoking Session::doLogin, which should be done by the LoginCompletedHandler. + * The authority can be put there by invoking Session::doLogin, + * which should be done by the LoginCompletedHandler. * To be able to do something after Session::doLogin, the LoginCompletedHandler has to be replaced with * an implementation that writes the SSO data and attributes in this case (AuthSource) to the session. */ diff --git a/lib/Auth/Utils.php b/lib/Auth/Utils.php index 43ceb5b71baac783774d92409e5e02fcd134402d..302fad662d33da64060b16eb6da07daea4bdcb82 100644 --- a/lib/Auth/Utils.php +++ b/lib/Auth/Utils.php @@ -66,7 +66,8 @@ class Utils if ($formParams['mode'] === 'push') { try { if ($pi->pollTransaction($transactionID)) { - // If the authentication has been confirmed on the phone, the authentication has to be finalized with a + // If the authentication has been confirmed on the phone, + // the authentication has to be finalized with a // call to /validate/check with an empty pass // https://privacyidea.readthedocs.io/en/latest/tokens/authentication_modes.html#outofband-mode $response = $pi->validateCheck($username, '', $transactionID); @@ -92,7 +93,8 @@ class Utils if (empty($origin) || empty($webAuthnSignResponse)) { Logger::error( - 'privacyIDEA: Incomplete data for WebAuthn authentication: WebAuthnSignResponse or Origin is missing!' + 'privacyIDEA: Incomplete data for WebAuthn authentication: ' + . 'WebAuthnSignResponse or Origin is missing!' ); } else { try { @@ -136,7 +138,8 @@ class Utils $authorities = $session->getAuthorities(); if (empty($authorities)) { Logger::error( - 'privacyIDEA: Cannot use SSO because there is no authority configured to register the logout handler for!' + 'privacyIDEA: Cannot use SSO because there is no authority' + . 'configured to register the logout handler for!' ); return; @@ -300,7 +303,8 @@ class Utils State::saveState($state, 'privacyidea:privacyidea'); ProcessingChain::resumeProcessing($state); } elseif ($state['privacyidea:privacyidea']['authenticationMethod'] === 'authsource') { - // For AuthSource, the attributes required by saml need to be present, so check for that before completing + // For AuthSource, the attributes required by saml need to be present, + // so check for that before completing PrivacyideaAuthSource::checkAuthenticationComplete($state, $response, $config); } } elseif (!empty($response->errorCode)) { @@ -326,7 +330,8 @@ class Utils */ public static function getClientIP() { - $result = ($_SERVER['HTTP_X_FORWARDED_FOR'] ?? null) ?: ($_SERVER['REMOTE_ADDR'] ?? null) ?: ($_SERVER['HTTP_CLIENT_IP'] ?? null); + $result = ($_SERVER['HTTP_X_FORWARDED_FOR'] ?? null) ?: ($_SERVER['REMOTE_ADDR'] ?? null) + ?: ($_SERVER['HTTP_CLIENT_IP'] ?? null); Logger::debug('privacyIDEA: client ip: ' . $result); return $result; diff --git a/templates/LoginForm.php b/templates/LoginForm.php index e247003e98dcf1f96db3e176636777ddb0cf6f9a..9a011a1c43b18cc273cd6036af2e11d2ec5e4466 100644 --- a/templates/LoginForm.php +++ b/templates/LoginForm.php @@ -1,4 +1,6 @@ -<?php declare(strict_types=1); +<?php + +declare(strict_types=1); use SimpleSAML\Module; @@ -15,11 +17,8 @@ if ($this->data['errorCode'] !== null) { ?> <div class="error-dialog"> - <img src="/<?php - echo htmlspecialchars( - $this->data['baseurlpath'], - ENT_QUOTES - ); ?>resources/icons/experience/gtk-dialog-error.48x48.png" + <img src="/<?php echo htmlspecialchars($this->data['baseurlpath'], ENT_QUOTES); + ?>resources/icons/experience/gtk-dialog-error.48x48.png" class="float-l erroricon" alt="gtk-dialog-error"/> <h2><?php echo $this->t('{login:error_header}'); ?></h2> <p> @@ -46,7 +45,7 @@ if ($this->data['errorCode'] !== null) { } elseif ($this->data['step'] < 2) { echo '<h2>' . htmlspecialchars($this->t('{privacyidea:privacyidea:login_title}')) . '</h2>'; } -?> + ?> <form action="FormReceiver.php" method="POST" id="piLoginForm" name="piLoginForm" class="loginForm"> <div class="form-panel first valid" id="gaia_firstform"> @@ -54,54 +53,58 @@ if ($this->data['errorCode'] !== null) { <div class="input-wrapper focused"> <div class="identifier-shown"> <?php - if ($this->data['forceUsername']) { - if (!empty($this->data['username'])) { - ?> + if ($this->data['forceUsername']) { + if (!empty($this->data['username'])) { + ?> <h3><?php echo htmlspecialchars($this->data['username']); ?></h3> - <?php - } ?> + <?php + } ?> <input type="hidden" id="username" name="username" - value="<?php echo htmlspecialchars($this->data['username'] ?? '', ENT_QUOTES); ?>"/> + value="<?php + echo htmlspecialchars($this->data['username'] ?? '', ENT_QUOTES); ?>"/> <?php - } elseif ($this->data['step'] <= 1) { - ?> + } elseif ($this->data['step'] <= 1) { + ?> <p> <label for="username" class="sr-only"> <?php echo $this->t('{login:username}'); ?> </label> <input type="text" id="username" tabindex="1" name="username" autofocus - value="<?php echo htmlspecialchars($this->data['username'], ENT_QUOTES); ?>" - placeholder="<?php echo htmlspecialchars($this->t('{login:username}'), ENT_QUOTES); ?>" + value="<?php + echo htmlspecialchars($this->data['username'], ENT_QUOTES); ?>" + placeholder="<?php + echo htmlspecialchars($this->t('{login:username}'), ENT_QUOTES); ?>" /> </p> <?php - } + } // Remember username in authproc - if (!$this->data['authProcFilterScenario']) { - if ($this->data['rememberUsernameEnabled'] || $this->data['rememberMeEnabled']) { - $rowspan = 1; - } elseif (array_key_exists('organizations', $this->data)) { - $rowspan = 3; - } else { - $rowspan = 2; - } - if ($this->data['rememberUsernameEnabled'] || $this->data['rememberMeEnabled']) { - if ($this->data['rememberUsernameEnabled']) { - echo str_repeat("\t", 4); - echo '<input type="checkbox" id="rememberUsername" tabindex="4" name="rememberUsername" - value="Yes" '; - echo $this->data['rememberUsernameChecked'] ? 'checked="Yes" /> ' : '/> '; - echo htmlspecialchars($this->t('{login:remember_username}')); - } - if ($this->data['rememberMeEnabled']) { - echo str_repeat("\t", 4); - echo '<input type="checkbox" id="rememberMe" tabindex="4" name="rememberMe" value="Yes" '; - echo $this->data['rememberMeChecked'] ? 'checked="Yes" /> ' : '/> '; - echo htmlspecialchars($this->t('{login:remember_me}')); - } - } - } ?> + if (!$this->data['authProcFilterScenario']) { + if ($this->data['rememberUsernameEnabled'] || $this->data['rememberMeEnabled']) { + $rowspan = 1; + } elseif (array_key_exists('organizations', $this->data)) { + $rowspan = 3; + } else { + $rowspan = 2; + } + if ($this->data['rememberUsernameEnabled'] || $this->data['rememberMeEnabled']) { + if ($this->data['rememberUsernameEnabled']) { + echo str_repeat("\t", 4); + echo '<input type="checkbox" id="rememberUsername" tabindex="4"' + . ' name="rememberUsername" value="Yes" '; + echo $this->data['rememberUsernameChecked'] ? 'checked="Yes" /> ' : '/> '; + echo htmlspecialchars($this->t('{login:remember_username}')); + } + if ($this->data['rememberMeEnabled']) { + echo str_repeat("\t", 4); + echo '<input type="checkbox" id="rememberMe" tabindex="4"' + . ' name="rememberMe" value="Yes" '; + echo $this->data['rememberMeChecked'] ? 'checked="Yes" /> ' : '/> '; + echo htmlspecialchars($this->t('{login:remember_me}')); + } + } + } ?> <!-- Pass and OTP fields --> <?php if (!$this->data['authProcFilterScenario']) { ?> @@ -109,21 +112,22 @@ if ($this->data['errorCode'] !== null) { <?php echo $this->t('{privacyidea:privacyidea:password}'); ?> </label> <input id="password" name="password" tabindex="1" type="password" value="" class="text" - placeholder="<?php echo htmlspecialchars($this->data['passHint'], ENT_QUOTES); ?>"/> + placeholder="<?php + echo htmlspecialchars($this->data['passHint'], ENT_QUOTES); ?>"/> <?php } ?> <?php if ($this->data['step'] > 1) { ?> <p id="message" role="alert"><?php - $messageOverride = $this->data['messageOverride'] ?? null; - if ($messageOverride === null || is_string($messageOverride)) { - echo htmlspecialchars( - $messageOverride ?? $this->data['message'] ?? '', - ENT_QUOTES - ); - } elseif (is_callable($messageOverride)) { - echo call_user_func($messageOverride, $this->data['message'] ?? ''); - } - ?></p> + $messageOverride = $this->data['messageOverride'] ?? null; + if ($messageOverride === null || is_string($messageOverride)) { + echo htmlspecialchars( + $messageOverride ?? $this->data['message'] ?? '', + ENT_QUOTES + ); + } elseif (is_callable($messageOverride)) { + echo call_user_func($messageOverride, $this->data['message'] ?? ''); + } + ?></p> <?php } ?> <?php if ($this->data['step'] > 1) { ?> @@ -131,7 +135,8 @@ if ($this->data['errorCode'] !== null) { <label for="otp" class="sr-only"> <?php echo $this->t('{privacyidea:privacyidea:otp}'); ?> </label> - <input id="otp" name="otp" type="password" placeholder="<?php echo htmlspecialchars($this->data['otpHint'], ENT_QUOTES); ?>" + <input id="otp" name="otp" type="password" placeholder="<?php + echo htmlspecialchars($this->data['otpHint'], ENT_QUOTES); ?>" <?php if (($this->data['otpAvailable'] ?? true) && $this->data['noAlternatives']) { echo ' autofocus'; } ?>> @@ -139,14 +144,16 @@ if ($this->data['errorCode'] !== null) { <?php } ?> <p> - <button id="submitButton" tabindex="1" class="rc-button rc-button-submit" type="submit" name="Submit" value="1"> + <button id="submitButton" tabindex="1" class="rc-button rc-button-submit" + type="submit" name="Submit" value="1"> <?php echo htmlspecialchars($this->t('{login:login_button}'), ENT_QUOTES); ?> </button> </p> <!-- Undefined index is suppressed and the default is used for these values --> <input id="mode" type="hidden" name="mode" value="otp" - data-preferred="<?php echo htmlspecialchars($this->data['mode'], ENT_QUOTES); ?>"/> + data-preferred="<?php + echo htmlspecialchars($this->data['mode'], ENT_QUOTES); ?>"/> <input id="pushAvailable" type="hidden" name="pushAvailable" value="<?php echo ($this->data['pushAvailable'] ?? false) ? 'true' : ''; ?>"/> @@ -155,35 +162,45 @@ if ($this->data['errorCode'] !== null) { value="<?php echo ($this->data['otpAvailable'] ?? true) ? 'true' : ''; ?>"/> <input id="webAuthnSignRequest" type="hidden" name="webAuthnSignRequest" - value='<?php echo htmlspecialchars($this->data['webAuthnSignRequest'] ?? '', ENT_QUOTES); ?>'/> + value='<?php + echo htmlspecialchars($this->data['webAuthnSignRequest'] ?? '', ENT_QUOTES); + ?>'/> <input id="u2fSignRequest" type="hidden" name="u2fSignRequest" - value='<?php echo htmlspecialchars($this->data['u2fSignRequest'] ?? '', ENT_QUOTES); ?>'/> + value='<?php + echo htmlspecialchars($this->data['u2fSignRequest'] ?? '', ENT_QUOTES); ?>'/> <input id="modeChanged" type="hidden" name="modeChanged" value=""/> <input id="step" type="hidden" name="step" - value="<?php echo htmlspecialchars(strval(($this->data['step'] ?? null) ?: 2), ENT_QUOTES); ?>"/> + value="<?php + echo htmlspecialchars(strval(($this->data['step'] ?? null) ?: 2), ENT_QUOTES); + ?>"/> <input id="webAuthnSignResponse" type="hidden" name="webAuthnSignResponse" value=""/> <input id="u2fSignResponse" type="hidden" name="u2fSignResponse" value=""/> <input id="origin" type="hidden" name="origin" value=""/> <input id="loadCounter" type="hidden" name="loadCounter" - value="<?php echo htmlspecialchars(strval(($this->data['loadCounter'] ?? null) ?: 1), ENT_QUOTES); ?>"/> + value="<?php + echo htmlspecialchars( + strval(($this->data['loadCounter'] ?? null) ?: 1), + ENT_QUOTES + ); ?>"/> <!-- Additional input to persist the message --> <input type="hidden" name="message" - value="<?php echo htmlspecialchars($this->data['message'] ?? '', ENT_QUOTES); ?>"/> + value="<?php + echo htmlspecialchars($this->data['message'] ?? '', ENT_QUOTES); ?>"/> <?php // If enrollToken load QR Code - if (isset($this->data['tokenQR'])) { - echo htmlspecialchars($this->t('{privacyidea:privacyidea:scan_token_qr}')); ?> + if (isset($this->data['tokenQR'])) { + echo htmlspecialchars($this->t('{privacyidea:privacyidea:scan_token_qr}')); ?> <div class="tokenQR"> <?php echo '<img src="' . $this->data['tokenQR'] . '" />'; ?> </div> <?php - } -?> + } + ?> </div> <?php @@ -191,7 +208,8 @@ if ($this->data['errorCode'] !== null) { if (array_key_exists('organizations', $this->data)) { ?> <div class="identifier-shown"> - <label for="organization"><?php echo htmlspecialchars($this->t('{login:organization}')); ?></label> + <label for="organization"><?php + echo htmlspecialchars($this->t('{login:organization}')); ?></label> <select id="organization" name="organization" tabindex="3"> <?php if (array_key_exists('selectedOrg', $this->data)) { @@ -200,25 +218,25 @@ if ($this->data['errorCode'] !== null) { $selectedOrg = null; } - foreach ($this->data['organizations'] as $orgId => $orgDesc) { - if (is_array($orgDesc)) { - $orgDesc = $this->t($orgDesc); - } - - if ($orgId === $selectedOrg) { - $selected = 'selected="selected" '; - } else { - $selected = ''; - } - - echo '<option ' . $selected . 'value="' . htmlspecialchars( - $orgId, - ENT_QUOTES - ) . '">' . htmlspecialchars($orgDesc) . '</option>'; - } ?> + foreach ($this->data['organizations'] as $orgId => $orgDesc) { + if (is_array($orgDesc)) { + $orgDesc = $this->t($orgDesc); + } + + if ($orgId === $selectedOrg) { + $selected = 'selected="selected" '; + } else { + $selected = ''; + } + + echo '<option ' . $selected . 'value="' . htmlspecialchars( + $orgId, + ENT_QUOTES + ) . '">' . htmlspecialchars($orgDesc) . '</option>'; + } ?> </select> </div> - <?php + <?php } ?> </div> <!-- focused --> </div> <!-- slide-out--> @@ -256,7 +274,8 @@ if ($this->data['errorCode'] !== null) { // Logout if (($this->data['showLogout'] ?? true) && isset($this->data['LogoutURL'])) { ?> <p> - <a href="<?php echo htmlspecialchars($this->data['LogoutURL']); ?>"><?php echo $this->t('{status:logout}'); ?></a> + <a href="<?php + echo htmlspecialchars($this->data['LogoutURL']); ?>"><?php echo $this->t('{status:logout}'); ?></a> </p> <?php } ?> </div> <!-- End of login --> @@ -282,7 +301,8 @@ if (!empty($this->data['links'])) { <meta id="privacyidea-step" name="privacyidea-step" content="<?php echo $this->data['step']; ?>"> - <meta id="privacyidea-translations" name="privacyidea-translations" content="<?php echo htmlspecialchars(json_encode($this->data['translations'])); ?>"> + <meta id="privacyidea-translations" name="privacyidea-translations" content="<?php + echo htmlspecialchars(json_encode($this->data['translations'])); ?>"> <script src="<?php echo htmlspecialchars(Module::getModuleUrl('privacyidea/js/loginform.js'), ENT_QUOTES); ?>"> </script> diff --git a/www/FormBuilder.php b/www/FormBuilder.php index b72a2a33f951bffcdf4a691a45aa965cafd8546b..c32ad2e1499d61e47edd39bcc4996a8c2837f058 100644 --- a/www/FormBuilder.php +++ b/www/FormBuilder.php @@ -38,7 +38,10 @@ $tpl = new Template(Configuration::getInstance(), 'privacyidea:LoginForm.php'); $tpl->data['errorCode'] = null; $tpl->data['errorMessage'] = null; -if (!empty($state['privacyidea:privacyidea']['errorCode']) || !empty($state['privacyidea:privacyidea']['errorMessage'])) { +if ( + !empty($state['privacyidea:privacyidea']['errorCode']) + || !empty($state['privacyidea:privacyidea']['errorMessage']) +) { $tpl->data['errorCode'] = ($state['privacyidea:privacyidea']['errorCode'] ?? null) ?: ''; $state['privacyidea:privacyidea']['errorCode'] = ''; $tpl->data['errorMessage'] = $tpl->t('{privacyidea:privacyidea:error_message}'); diff --git a/www/FormReceiver.php b/www/FormReceiver.php index 41a91cafd687ceb3ebb6accce694ae2d63bf3bef..4b82a7b84ea050507c5a6308798a46cbba35389c 100644 --- a/www/FormReceiver.php +++ b/www/FormReceiver.php @@ -79,7 +79,8 @@ if ($state['privacyidea:privacyidea']['authenticationMethod'] === 'authprocess') $params = $sessionHandler->getCookieParams(); $params['expire'] = time(); - $params['expire'] += (isset($_REQUEST['rememberUsername']) && $_REQUEST['rememberUsername'] === 'Yes' ? 31536000 : -300); + $params['expire'] += (isset($_REQUEST['rememberUsername']) && $_REQUEST['rememberUsername'] === 'Yes' + ? 31536000 : -300); HTTP::setCookie($source->getAuthId() . '-username', $username, $params, false); }