[!!!][FEATURE] BE Login form API 34/39234/29
authorFrank Nägler <typo3@naegler.net>
Sun, 3 May 2015 11:41:53 +0000 (13:41 +0200)
committerAnja Leichsenring <aleichsenring@ab-softlab.de>
Fri, 5 Jun 2015 05:04:21 +0000 (07:04 +0200)
With the new login screen (introduced in 7.2) the possibility to
override or extend the login template was removed.
The old globals ``$GLOBALS['TBE_STYLES']['htmlTemplates']`` was
removed without replacement.

With this patch we introduce a new way to extend the login form and
add a BE login form API.

Resolves: #66669
Releases: master
Change-Id: I36ab4cdabbab55f370d1fd19b7212cf2f858db57
Reviewed-on: http://review.typo3.org/39234
Reviewed-by: Markus Klein <markus.klein@typo3.org>
Tested-by: Markus Klein <markus.klein@typo3.org>
Reviewed-by: Frank Nägler <typo3@naegler.net>
Tested-by: Frank Nägler <typo3@naegler.net>
Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
29 files changed:
typo3/index.php
typo3/sysext/backend/Classes/Controller/LoginController.php
typo3/sysext/backend/Classes/LoginProvider/LoginProviderInterface.php [new file with mode: 0644]
typo3/sysext/backend/Classes/LoginProvider/UsernamePasswordLoginProvider.php [new file with mode: 0644]
typo3/sysext/backend/Resources/Private/Language/locallang.xlf
typo3/sysext/backend/Resources/Private/Layouts/Login.html [new file with mode: 0644]
typo3/sysext/backend/Resources/Private/Partials/Login/InterfaceSelector.html [new file with mode: 0644]
typo3/sysext/backend/Resources/Private/Partials/LoginNews.html [new file with mode: 0644]
typo3/sysext/backend/Resources/Private/Templates/Login.html [deleted file]
typo3/sysext/backend/Resources/Private/Templates/UserPassLoginForm.html [new file with mode: 0644]
typo3/sysext/backend/Resources/Public/JavaScript/Login.js
typo3/sysext/backend/Resources/Public/JavaScript/UserPassLogin.js [new file with mode: 0644]
typo3/sysext/backend/Tests/Unit/Controller/LoginControllerTest.php [new file with mode: 0644]
typo3/sysext/backend/ext_localconf.php
typo3/sysext/compatibility6/Migrations/Code/ClassAliasMap.php
typo3/sysext/compatibility6/Migrations/Code/LegacyClassesForIde.php
typo3/sysext/core/Documentation/Changelog/master/Breaking-66669-BackendLoginControllerRefactored.rst [new file with mode: 0644]
typo3/sysext/core/Documentation/Changelog/master/Feature-66669-BeLoginFormAPI.rst [new file with mode: 0644]
typo3/sysext/lang/locallang_login.xlf
typo3/sysext/openid/Classes/LoginProvider/OpenIdLoginProvider.php [new file with mode: 0644]
typo3/sysext/openid/Resources/Private/Language/locallang.xlf
typo3/sysext/openid/Resources/Private/Templates/OpenidLogin.html [new file with mode: 0644]
typo3/sysext/openid/ext_localconf.php
typo3/sysext/rsaauth/Classes/Hook/LoginFormHook.php [deleted file]
typo3/sysext/rsaauth/Classes/Slot/UsernamePasswordProviderSlot.php [new file with mode: 0644]
typo3/sysext/rsaauth/Resources/Public/JavaScript/BackendLoginFormRsaEncryption.js
typo3/sysext/rsaauth/ext_localconf.php
typo3/sysext/t3skin/Resources/Private/Styles/TYPO3/_login_screen.less
typo3/sysext/t3skin/Resources/Public/Css/visual/t3skin.css

index 14882c2..be35223 100644 (file)
@@ -22,4 +22,3 @@ require __DIR__ . '/init.php';
 
 $loginController = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Controller\LoginController::class);
 $loginController->main();
-$loginController->printContent();
index a2b3146..4b3df5d 100644 (file)
@@ -14,15 +14,16 @@ namespace TYPO3\CMS\Backend\Controller;
  * The TYPO3 project - inspiring people to share!
  */
 
+use TYPO3\CMS\Backend\Exception;
+use TYPO3\CMS\Backend\LoginProvider\LoginProviderInterface;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
 use TYPO3\CMS\Core\Database\DatabaseConnection;
+use TYPO3\CMS\Core\FormProtection\BackendFormProtection;
 use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
-use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\HttpUtility;
 use TYPO3\CMS\Core\Utility\PathUtility;
-use TYPO3\CMS\Extbase\Object\ObjectManager;
 use TYPO3\CMS\Fluid\View\StandaloneView;
 use TYPO3\CMS\Lang\LanguageService;
 
@@ -30,197 +31,115 @@ use TYPO3\CMS\Lang\LanguageService;
  * Script Class for rendering the login form
  *
  * @author Kasper Skårhøj <kasperYYYY@typo3.com>
+ * @author Frank Nägler <typo3@naegler.net>
  */
 class LoginController {
-       /**
-        * The URL to redirect to after login.
-        *
-        * @var string
-        */
-       public $redirect_url;
-
-       /**
-        * Defines which interface to load (from interface selector)
-        *
-        * @var string
-        */
-       public $GPinterface;
 
        /**
-        * preset username
+        * The URL to redirect to after login.
         *
         * @var string
         */
-       public $u;
+       protected $redirectUrl;
 
        /**
-        * preset password
+        * Set to the redirect URL of the form (may be redirect_url or "backend.php")
         *
         * @var string
         */
-       public $p;
+       protected $redirectToURL;
 
        /**
-        * OpenID URL submitted by form
+        * the active login provider identifier
         *
         * @var string
         */
-       protected $openIdUrl;
+       protected $loginProviderIdentifier = NULL;
 
        /**
-        * If "L" is "OUT", then any logged in used is logged out. If redirect_url is given, we redirect to it
+        * List of registered and sorted login providers
         *
-        * @var string
+        * @var array
         */
-       public $L;
+       protected $loginProviders = [];
 
        /**
-        * Login-refresh boolean; The backend will call this script
+        * Login-refresh bool; The backend will call this script
         * with this value set when the login is close to being expired
         * and the form needs to be redrawn.
         *
         * @var bool
         */
-       public $loginRefresh;
+       protected $loginRefresh;
 
        /**
         * Value of forms submit button for login.
         *
         * @var string
         */
-       public $commandLI;
-
-       /**
-        * Set to the redirect URL of the form (may be redirect_url or "backend.php")
-        *
-        * @var string
-        */
-       public $redirectToURL;
-
-       /**
-        * Content accumulation
-        *
-        * @var string
-        */
-       public $content;
-
-       /**
-        * A selector box for selecting value for "interface" may be rendered into this variable
-        *
-        * @var string
-        */
-       public $interfaceSelector;
-
-       /**
-        * A selector box for selecting value for "interface" may be rendered into this variable
-        * this will have an onchange action which will redirect the user to the selected interface right away
-        *
-        * @var string
-        */
-       public $interfaceSelector_jump;
-
-       /**
-        * A hidden field, if the interface is not set.
-        *
-        * @var string
-        */
-       public $interfaceSelector_hidden;
-
-       /**
-        * Additional hidden fields to be placed at the login form
-        *
-        * @var string
-        */
-       public $addFields_hidden = '';
+       protected $submitValue;
 
        /**
-        * Sets the level of security
-        *
-        * 'normal' = clear-text
-        * password/username from form in $formfield_uident.
-        *
-        * @var string
+        * @var StandaloneView
         */
-       public $loginSecurityLevel = 'normal';
+       protected $view;
 
        /**
-        * Constructor
+        * Initialize the login box. Will also react on a &L=OUT flag and exit.
         */
        public function __construct() {
-               $this->init();
-       }
+               $this->validateAndSortLoginProviders();
 
-       /**
-        * Initialize the login box. Will also react on a &L=OUT flag and exit.
-        *
-        * @return void
-        */
-       public function init() {
                // We need a PHP session session for most login levels
                session_start();
-               $this->redirect_url = GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GP('redirect_url'));
-               $this->GPinterface = GeneralUtility::_GP('interface');
-               // Grabbing preset username and password, for security reasons this feature only works if SSL is used
-               if (GeneralUtility::getIndpEnv('TYPO3_SSL')) {
-                       $this->u = GeneralUtility::_GP('u');
-                       $this->p = GeneralUtility::_GP('p');
-                       $this->openIdUrl = GeneralUtility::_GP('openid_url');
-               }
-               // If "L" is "OUT", then any logged in is logged out. If redirect_url is given, we redirect to it
-               $this->L = GeneralUtility::_GP('L');
-               // Login
-               $this->loginRefresh = GeneralUtility::_GP('loginRefresh');
+               $this->redirectUrl = GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GP('redirect_url'));
+               $this->loginProviderIdentifier = $this->detectLoginProvider();
+
+               $this->loginRefresh = (bool)GeneralUtility::_GP('loginRefresh');
                // Value of "Login" button. If set, the login button was pressed.
-               $this->commandLI = GeneralUtility::_GP('commandLI');
-               // Sets the level of security from conf vars
-               $this->loginSecurityLevel = trim($GLOBALS['TYPO3_CONF_VARS']['BE']['loginSecurityLevel']) ?: 'normal';
+               $this->submitValue = GeneralUtility::_GP('commandLI');
+
                // Try to get the preferred browser language
-               $preferredBrowserLanguage = $this->getLanguageService()->csConvObj->getPreferredClientLanguage(GeneralUtility::getIndpEnv('HTTP_ACCEPT_LANGUAGE'));
+               $preferredBrowserLanguage = $this->getLanguageService()->csConvObj
+                       ->getPreferredClientLanguage(GeneralUtility::getIndpEnv('HTTP_ACCEPT_LANGUAGE'));
+
                // If we found a $preferredBrowserLanguage and it is not the default language and no be_user is logged in
                // initialize $this->getLanguageService() again with $preferredBrowserLanguage
                if ($preferredBrowserLanguage !== 'default' && empty($this->getBackendUserAuthentication()->user['uid'])) {
                        $this->getLanguageService()->init($preferredBrowserLanguage);
                }
+
+               $this->getLanguageService()->includeLLFile('EXT:lang/locallang_login.xlf');
+
                // Setting the redirect URL to "backend.php" if no alternative input is given
-               $this->redirectToURL = $this->redirect_url ?: 'backend.php';
-               // Do a logout if the command is set
-               if ($this->L == 'OUT' && is_object($this->getBackendUserAuthentication())) {
+               $this->redirectToURL = $this->redirectUrl ?: 'backend.php';
+
+               // If "L" is "OUT", then any logged in is logged out. If redirect_url is given, we redirect to it
+               if (GeneralUtility::_GP('L') === 'OUT' && is_object($this->getBackendUserAuthentication())) {
                        $this->getBackendUserAuthentication()->logoff();
-                       HttpUtility::redirect($this->redirect_url);
+                       HttpUtility::redirect($this->redirectUrl);
                }
+
+               $this->view = $this->getFluidTemplateObject();
        }
 
        /**
         * Main function - creating the login/logout form
         *
+        * @throws Exception
         * @return void
         */
        public function main() {
-               // Initialize template object:
-               $view = $this->getFluidTemplateObject('EXT:backend/Resources/Private/Templates/Login.html');
-
                /** @var $pageRenderer \TYPO3\CMS\Core\Page\PageRenderer */
                $pageRenderer = $this->getDocumentTemplate()->getPageRenderer();
-               $pageRenderer->loadJquery();
+               $pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Login');
 
                // support placeholders for IE9 and lower
                $clientInfo = GeneralUtility::clientInfo();
-               if ($clientInfo['BROWSER'] == 'msie' && $clientInfo['VERSION'] <= 9) {
+               if ($clientInfo['BROWSER'] === 'msie' && $clientInfo['VERSION'] <= 9) {
                        $pageRenderer->addJsLibrary('placeholders', 'sysext/core/Resources/Public/JavaScript/Contrib/placeholders.jquery.min.js');
                }
 
-               $pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Login');
-               if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/index.php']['loginScriptHook'])) {
-                       foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/index.php']['loginScriptHook'] as $function) {
-                               $params = array();
-                               $javaScriptCode = GeneralUtility::callUserFunction($function, $params, $this);
-                               if ($javaScriptCode) {
-                                       $this->getDocumentTemplate()->JScode .= $javaScriptCode;
-                                       break;
-                               }
-                       }
-               }
-
                // Checking, if we should make a redirect.
                // Might set JavaScript in the header to close window.
                $this->checkRedirect();
@@ -262,7 +181,7 @@ class LoginController {
                } elseif (!empty($GLOBALS['TBE_STYLES']['logo_login'])) {
                        // Fallback to old TBE_STYLES login logo
                        $logo = $GLOBALS['TBE_STYLES']['logo_login'];
-                       GeneralUtility::deprecationLog('$GLOBALS["TBE_STYLES"]["logo_login"] is deprecated since TYPO3 CMS 7 and will be removed in TYPO3 CMS 8, please head to the backend extension configuration instead.');
+                       GeneralUtility::deprecationLog('$GLOBALS["TBE_STYLES"]["logo_login"] is deprecated since TYPO3 CMS 7 and will be removed in TYPO3 CMS 8, please use the backend extension\'s configuration instead.');
                } else {
                        // Use TYPO3 logo depending on highlight color
                        if (!empty($extConf['loginHighlightColor'])) {
@@ -277,127 +196,115 @@ class LoginController {
                $logo = $this->getUriForFileName($logo);
 
                // Start form
-               $formType = empty($this->getBackendUserAuthentication()->user['uid']) ? 'loginForm' : 'logoutForm';
-               $view->assignMultiple(array(
-                       'formTag' => $this->startForm(),
-                       'labelPrefixPath' => 'LLL:EXT:lang/locallang_login.xlf:',
+               $formType = empty($this->getBackendUserAuthentication()->user['uid']) ? 'LoginForm' : 'LogoutForm';
+               $this->view->assignMultiple(array(
                        'backendUser' => $this->getBackendUserAuthentication()->user,
                        'hasLoginError' => $this->isLoginInProgress(),
-                       'presetUsername' => $this->u,
-                       'presetPassword' => $this->p,
-                       'presetOpenId' => $this->openIdUrl,
                        'formType' => $formType,
                        'logo' => $logo,
                        'images' => array(
                                'capslock' => $this->getUriForFileName('EXT:backend/Resources/Public/Images/icon_capslock.svg'),
                                'typo3' => $this->getUriForFileName('EXT:backend/Resources/Public/Images/typo3_orange.svg'),
                        ),
-                       'isOpenIdLoaded' => ExtensionManagementUtility::isLoaded('openid'),
                        'copyright' => BackendUtility::TYPO3_copyRightNotice(),
-                       'loginNewsItems' => $this->getSystemNews()
+                       'loginNewsItems' => $this->getSystemNews(),
+                       'loginProviderIdentifier' => $this->loginProviderIdentifier,
+                       'loginProviders' => $this->loginProviders
                ));
 
                // Initialize interface selectors:
                $this->makeInterfaceSelectorBox();
-               $view->assignMultiple(array(
-                       'interfaceSelector' => $this->interfaceSelector,
-                       'interfaceSelectorJump' => $this->interfaceSelector_jump
-               ));
 
-               // Starting page:
-               $this->content .= $this->getDocumentTemplate()->startPage('TYPO3 CMS Login: ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'], FALSE);
-               // Add Content:
-               $this->content .= $view->render();
-               $this->content .= $this->getDocumentTemplate()->endPage();
-       }
+               /** @var LoginProviderInterface $loginProvider */
+               $loginProvider = GeneralUtility::makeInstance($this->loginProviders[$this->loginProviderIdentifier]['provider']);
+               $loginProvider->render($this->view, $pageRenderer, $this);
 
-       /**
-        * Outputting the accumulated content to screen
-        *
-        * @return void
-        */
-       public function printContent() {
-               echo $this->content;
+               $content = $this->getDocumentTemplate()->startPage('TYPO3 CMS Login: ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'], FALSE);
+               $content .= $this->view->render();
+               $content .= $this->getDocumentTemplate()->endPage();
+
+               echo $content;
        }
 
-       /*****************************
-        *
-        * Various functions
-        *
-        ******************************/
        /**
         * Checking, if we should perform some sort of redirection OR closing of windows.
         *
+        * Do redirect:
+        *
+        * If a user is logged in AND
+        *   a) if either the login is just done (isLoginInProgress) or
+        *   b) a loginRefresh is done
+        *
         * @throws \RuntimeException
         * @throws \UnexpectedValueException
         */
-       public function checkRedirect() {
+       protected function checkRedirect() {
+               if (empty($this->getBackendUserAuthentication()->user['uid']) && $this->isLoginInProgress()) {
+                       // Wrong password, wait for 5 seconds
+                       sleep(5);
+                       return;
+               }
+               if (!$this->isLoginInProgress() && !$this->loginRefresh) {
+                       return;
+               }
                /*
-                * Do redirect:
-                *
-                * If a user is logged in AND
-                *   a) if either the login is just done (isLoginInProgress) or
-                *   b) a loginRefresh is done or c) the interface-selector is NOT enabled
-                *      (If it is on the other hand, it should not just load an interface,
-                *      because people has to choose then...)
+                * If no cookie has been set previously, we tell people that this is a problem.
+                * This assumes that a cookie-setting script (like this one) has been hit at
+                * least once prior to this instance.
                 */
-               if (!empty($this->getBackendUserAuthentication()->user['uid'])
-                       && ($this->isLoginInProgress() || $this->loginRefresh || !$this->interfaceSelector)) {
-                       /*
-                        * If no cookie has been set previously we tell people that this is a problem.
-                        * This assumes that a cookie-setting script (like this one) has been hit at
-                        * least once prior to this instance.
-                        */
-                       if (!$_COOKIE[BackendUserAuthentication::getCookieName()]) {
-                               if ($this->commandLI == 'setCookie') {
-                                       /*
-                                        * we tried it a second time but still no cookie
-                                        * 26/4 2005: This does not work anymore, because the saving of challenge values
-                                        * in $_SESSION means the system will act as if the password was wrong.
-                                        */
-                                       throw new \RuntimeException('Login-error: Yeah, that\'s a classic. No cookies, no TYPO3. ' .
-                                               'Please accept cookies from TYPO3 - otherwise you\'ll not be able to use the system.', 1294586846);
-                               } else {
-                                       // try it once again - that might be needed for auto login
-                                       $this->redirectToURL = 'index.php?commandLI=setCookie';
-                               }
-                       }
-                       $redirectToUrl = (string)$this->getBackendUserAuthentication()->getTSConfigVal('auth.BE.redirectToURL');
-                       if (!empty($redirectToUrl)) {
-                               $this->redirectToURL = $redirectToUrl;
-                               $this->GPinterface = '';
+               if (!$_COOKIE[BackendUserAuthentication::getCookieName()]) {
+                       if ($this->submitValue === 'setCookie') {
+                               /*
+                                * we tried it a second time but still no cookie
+                                * 26/4 2005: This does not work anymore, because the saving of challenge values
+                                * in $_SESSION means the system will act as if the password was wrong.
+                                */
+                               throw new \RuntimeException('Login-error: Yeah, that\'s a classic. No cookies, no TYPO3. ' .
+                                       'Please accept cookies from TYPO3 - otherwise you\'ll not be able to use the system.', 1294586846);
+                       } else {
+                               // try it once again - that might be needed for auto login
+                               $this->redirectToURL = 'index.php?commandLI=setCookie';
                        }
-                       // store interface
-                       $this->getBackendUserAuthentication()->uc['interfaceSetup'] = $this->GPinterface;
-                       $this->getBackendUserAuthentication()->writeUC();
-                       // Based on specific setting of interface we set the redirect script:
-                       switch ($this->GPinterface) {
-                               case 'backend':
-                                       $this->redirectToURL = 'backend.php';
-                                       break;
+               }
+               $redirectToUrl = (string)$this->getBackendUserAuthentication()->getTSConfigVal('auth.BE.redirectToURL');
+               if (empty($redirectToUrl)) {
+                       // Based on the interface we set the redirect script
+                       switch (GeneralUtility::_GP('interface')) {
                                case 'frontend':
+                                       $interface = 'frontend';
                                        $this->redirectToURL = '../';
                                        break;
+                               case 'backend':
+                                       $interface = 'backend';
+                                       $this->redirectToURL = 'backend.php';
+                                       break;
+                               default:
+                                       $interface = '';
                        }
-                       /** @var $formProtection \TYPO3\CMS\Core\FormProtection\BackendFormProtection */
-                       $formProtection = FormProtectionFactory::get();
-                       // If there is a redirect URL AND if loginRefresh is not set...
-                       if (!$this->loginRefresh) {
-                               $formProtection->storeSessionTokenInRegistry();
-                               HttpUtility::redirect($this->redirectToURL);
-                       } else {
-                               $formProtection->setSessionTokenFromRegistry();
-                               $formProtection->persistSessionToken();
-                               $this->getDocumentTemplate()->JScode .= $this->getDocumentTemplate()->wrapScriptTags('
-                                       if (parent.opener && parent.opener.TYPO3 && parent.opener.TYPO3.LoginRefresh) {
-                                               parent.opener.TYPO3.LoginRefresh.startTask();
-                                               parent.close();
-                                       }
-                               ');
-                       }
-               } elseif (empty($this->getBackendUserAuthentication()->user['uid']) && $this->isLoginInProgress()) {
-                       // Wrong password, wait for 5 seconds
-                       sleep(5);
+               } else {
+                       $this->redirectToURL = $redirectToUrl;
+                       $interface = '';
+               }
+               // store interface
+               $this->getBackendUserAuthentication()->uc['interfaceSetup'] = $interface;
+               $this->getBackendUserAuthentication()->writeUC();
+
+               $formProtection = FormProtectionFactory::get();
+               if (!$formProtection instanceof BackendFormProtection) {
+                       throw new \RuntimeException('The Form Protection retrieved does not match the expected one.', 1432080411);
+               }
+               if ($this->loginRefresh) {
+                       $formProtection->setSessionTokenFromRegistry();
+                       $formProtection->persistSessionToken();
+                       $this->getDocumentTemplate()->JScode .= $this->getDocumentTemplate()->wrapScriptTags('
+                               if (parent.opener && parent.opener.TYPO3 && parent.opener.TYPO3.LoginRefresh) {
+                                       parent.opener.TYPO3.LoginRefresh.startTask();
+                                       parent.close();
+                               }
+                       ');
+               } else {
+                       $formProtection->storeSessionTokenInRegistry();
+                       HttpUtility::redirect($this->redirectToURL);
                }
        }
 
@@ -407,40 +314,30 @@ class LoginController {
         * @return void
         */
        public function makeInterfaceSelectorBox() {
-               // Reset variables:
-               $this->interfaceSelector = '';
-               $this->interfaceSelector_hidden = '';
-               $this->interfaceSelector_jump = '';
                // If interfaces are defined AND no input redirect URL in GET vars:
-               if ($GLOBALS['TYPO3_CONF_VARS']['BE']['interfaces'] && ($this->isLoginInProgress() || !$this->redirect_url)) {
+               if ($GLOBALS['TYPO3_CONF_VARS']['BE']['interfaces'] && ($this->isLoginInProgress() || !$this->redirectUrl)) {
                        $parts = GeneralUtility::trimExplode(',', $GLOBALS['TYPO3_CONF_VARS']['BE']['interfaces']);
-                       // Only if more than one interface is defined will we show the selector:
                        if (count($parts) > 1) {
-                               // Initialize:
-                               $labels = array(
-                                       'backend' => $this->getLanguageService()->getLL('interface.backend'),
-                                       'frontend' => $this->getLanguageService()->getLL('interface.frontend')
+                               // Only if more than one interface is defined we will show the selector
+                               $interfaces = array(
+                                       'backend' => array(
+                                               'label' => $this->getLanguageService()->getLL('interface.backend'),
+                                               'jumpScript' => 'backend.php',
+                                               'interface' => 'backend'
+                                       ),
+                                       'frontend' => array(
+                                               'label' => $this->getLanguageService()->getLL('interface.frontend'),
+                                               'jumpScript' => '../',
+                                               'interface' => 'frontend'
+                                       )
                                );
-                               $jumpScript = array(
-                                       'backend' => 'backend.php',
-                                       'frontend' => '../'
-                               );
-                               // Traverse the interface keys:
-                               foreach ($parts as $valueStr) {
-                                       $this->interfaceSelector .= '
-                                                       <option value="' . htmlspecialchars($valueStr) . '"' . (GeneralUtility::_GP('interface') == htmlspecialchars($valueStr) ? ' selected="selected"' : '') . '>' . htmlspecialchars($labels[$valueStr]) . '</option>';
-                                       $this->interfaceSelector_jump .= '
-                                                       <option value="' . htmlspecialchars($jumpScript[$valueStr]) . '">' . htmlspecialchars($labels[$valueStr]) . '</option>';
-                               }
-                               $this->interfaceSelector = '
-                                               <select id="t3-interfaceselector" name="interface" class="form-control input-login t3js-login-interface-field" tabindex="3">' . $this->interfaceSelector . '
-                                               </select>';
-                               $this->interfaceSelector_jump = '
-                                               <select id="t3-interfaceselector" name="interface" class="form-control input-login t3js-login-interface-field" tabindex="3" onchange="window.location.href=this.options[this.selectedIndex].value;">' . $this->interfaceSelector_jump . '
-                                               </select>';
-                       } elseif (!$this->redirect_url) {
-                               // If there is only ONE interface value set and no redirect_url is present:
-                               $this->interfaceSelector_hidden = '<input type="hidden" name="interface" value="' . trim($GLOBALS['TYPO3_CONF_VARS']['BE']['interfaces']) . '" />';
+
+                               $this->view->assign('showInterfaceSelector', TRUE);
+                               $this->view->assign('interfaces', $interfaces);
+                       } elseif (!$this->redirectUrl) {
+                               // If there is only ONE interface value set and no redirect_url is present
+                               $this->view->assign('showInterfaceSelector', FALSE);
+                               $this->view->assign('interface', $parts[0]);
                        }
                }
        }
@@ -466,30 +363,6 @@ class LoginController {
        }
 
        /**
-        * Returns the form tag
-        *
-        * @return string Opening form tag string
-        */
-       public function startForm() {
-               $form = '<form action="index.php" id="typo3-login-form" method="post" name="loginform">';
-               if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/index.php']['loginFormHook'])) {
-                       foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/index.php']['loginFormHook'] as $function) {
-                               $params = array();
-                               $formCode = GeneralUtility::callUserFunction($function, $params, $this);
-                               if ($formCode) {
-                                       $form = $formCode;
-                                       break;
-                               }
-                       }
-               }
-               return $form . '<input type="hidden" name="login_status" value="login" />' .
-                       '<input type="hidden" id="t3-field-userident" class="t3js-login-userident-field" name="userident" value="" />' .
-                       '<input type="hidden" name="redirect_url" value="' . htmlspecialchars($this->redirectToURL) . '" />' .
-                       '<input type="hidden" name="loginRefresh" value="' . htmlspecialchars($this->loginRefresh) . '" />' .
-                       $this->interfaceSelector_hidden . $this->addFields_hidden;
-       }
-
-       /**
         * Returns the uri of a relative reference, resolves the "EXT:" prefix
         * (way of referring to files inside extensions) and checks that the file is inside
         * the PATH_site of the TYPO3 installation
@@ -499,10 +372,11 @@ class LoginController {
         * @internal
         */
        private function getUriForFileName($filename) {
-               $urlPrefix = '';
                if (strpos($filename, '://')) {
-                       $urlPrefix = '';
-               } elseif (strpos($filename, 'EXT:') === 0) {
+                       return $filename;
+               }
+               $urlPrefix = '';
+               if (strpos($filename, 'EXT:') === 0) {
                        $absoluteFilename = GeneralUtility::getFileAbsFileName($filename);
                        $filename = '';
                        if ($absoluteFilename !== '') {
@@ -521,35 +395,87 @@ class LoginController {
         */
        protected function isLoginInProgress() {
                $username = GeneralUtility::_GP('username');
-               return !empty($username) || !empty($this->commandLI);
+               return !empty($username) || !empty($this->submitValue);
        }
 
        /**
-        * Get the ObjectManager
+        * returns a new standalone view, shorthand function
         *
-        * @return ObjectManager
+        * @return StandaloneView
         */
-       protected function getObjectManager() {
-               return GeneralUtility::makeInstance(ObjectManager::class);
+       protected function getFluidTemplateObject() {
+               /** @var StandaloneView $view */
+               $view = GeneralUtility::makeInstance(StandaloneView::class);
+               $view->setLayoutRootPaths(array(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Layouts')));
+               $view->setPartialRootPaths(array(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Partials')));
+               $view->setTemplateRootPaths(array(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates')));
+
+               $view->getRequest()->setControllerExtensionName('Backend');
+               return $view;
        }
 
        /**
-        * returns a new standalone view, shorthand function
+        * Validates the registered login providers
         *
-        * @param string $templatePathAndFileName optional the path to set the template path and filename
-        *
-        * @return StandaloneView
+        * @throws \RuntimeException
         */
-       protected function getFluidTemplateObject($templatePathAndFileName = NULL) {
-               $this->getLanguageService()->includeLLFile('EXT:backend/Resources/Private/Language/locallang.xlf');
-               $this->getLanguageService()->includeLLFile('EXT:lang/locallang_login.xlf');
+       protected function validateAndSortLoginProviders() {
+               if (
+                       !isset($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['backend']['loginProviders'])
+                       || !is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['backend']['loginProviders'])
+                       || empty($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['backend']['loginProviders'])
+               ) {
+                       throw new \RuntimeException('No login providers are registered in $GLOBALS[\'TYPO3_CONF_VARS\'][\'EXTCONF\'][\'backend\'][\'loginProviders\'].', 1433417281);
+               }
+               $providers = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['backend']['loginProviders'];
+               foreach ($providers as $identifier => $configuration) {
+                       if (empty($configuration) || !is_array($configuration)) {
+                               throw new \RuntimeException('Missing configuration for login provider "' . $identifier . '".', 1433416043);
+                       }
+                       if (!is_string($configuration['provider']) || empty($configuration['provider']) || !class_exists($configuration['provider']) || !is_subclass_of($configuration['provider'], LoginProviderInterface::class)) {
+                               throw new \RuntimeException('The login provider "' . $identifier . '" defines an invalid provider. Ensure the class exists and implements the "' . LoginProviderInterface::class . '".', 1433416043);
+                       }
+                       if (empty($configuration['label'])) {
+                               throw new \RuntimeException('Missing label definition for login provider "' . $identifier . '".', 1433416044);
+                       }
+                       if (empty($configuration['icon-class'])) {
+                               throw new \RuntimeException('Missing icon definition for login provider "' . $identifier . '".', 1433416045);
+                       }
+                       if (!isset($configuration['sorting'])) {
+                               throw new \RuntimeException('Missing sorting definition for login provider "' . $identifier . '".', 1433416046);
+                       }
+               }
+               // sort providers
+               uasort($providers, function($a, $b) {
+                       return $b['sorting'] - $a['sorting'];
+               });
+               $this->loginProviders = $providers;
+       }
 
-               $view = GeneralUtility::makeInstance(StandaloneView::class);
-               if ($templatePathAndFileName) {
-                       $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($templatePathAndFileName));
+       /**
+        * Detect the login provider, get from request or choose the
+        * first one as default
+        *
+        * @return string
+        */
+       protected function detectLoginProvider() {
+               $loginProvider = GeneralUtility::_GP('loginProvider');
+               if ((empty($loginProvider) || !isset($this->loginProviders[$loginProvider])) && !empty($_COOKIE['be_lastLoginProvider'])) {
+                       $loginProvider = $_COOKIE['be_lastLoginProvider'];
                }
-               $view->getRequest()->setControllerExtensionName('backend');
-               return $view;
+               if (empty($loginProvider) || !isset($this->loginProviders[$loginProvider])) {
+                       reset($this->loginProviders);
+                       $loginProvider = key($this->loginProviders);
+               }
+               setcookie('be_lastLoginProvider', $loginProvider);
+               return $loginProvider;
+       }
+
+       /**
+        * @return string
+        */
+       public function getLoginProviderIdentifier() {
+               return $this->loginProviderIdentifier;
        }
 
        /**
diff --git a/typo3/sysext/backend/Classes/LoginProvider/LoginProviderInterface.php b/typo3/sysext/backend/Classes/LoginProvider/LoginProviderInterface.php
new file mode 100644 (file)
index 0000000..5e192b4
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+namespace TYPO3\CMS\Backend\LoginProvider;
+
+/*
+ * 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!
+ */
+
+use TYPO3\CMS\Backend\Controller\LoginController;
+use TYPO3\CMS\Core\Page\PageRenderer;
+use TYPO3\CMS\Fluid\View\StandaloneView;
+
+/**
+ * Interface for Backend Login providers
+ */
+interface LoginProviderInterface {
+
+       /**
+        * Render the login HTML
+        *
+        * Implement this method and set the template for your form.
+        * This is also the right place to assign data to the view
+        * and add necessary JavaScript resources to the page renderer.
+        *
+        * A good example is EXT:openid
+        *
+        * Example:
+        *    $view->setTemplatePathAndFilename($pathAndFilename);
+        *    $view->assign('foo', 'bar');
+        *
+        * @param StandaloneView $view
+        * @param PageRenderer $pageRenderer
+        * @param LoginController $loginController
+        *
+        * @return void
+        */
+       public function render(StandaloneView $view, PageRenderer $pageRenderer, LoginController $loginController);
+
+}
diff --git a/typo3/sysext/backend/Classes/LoginProvider/UsernamePasswordLoginProvider.php b/typo3/sysext/backend/Classes/LoginProvider/UsernamePasswordLoginProvider.php
new file mode 100644 (file)
index 0000000..24fba6a
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+namespace TYPO3\CMS\Backend\LoginProvider;
+
+/*
+ * 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!
+ */
+
+use TYPO3\CMS\Backend\Controller\LoginController;
+use TYPO3\CMS\Core\Page\PageRenderer;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Extbase\Object\ObjectManager;
+use TYPO3\CMS\Extbase\SignalSlot\Dispatcher;
+use TYPO3\CMS\Fluid\View\StandaloneView;
+
+/**
+ * Class UsernamePasswordLoginProvider
+ */
+class UsernamePasswordLoginProvider implements LoginProviderInterface {
+
+       const SIGNAL_getPageRenderer = 'getPageRenderer';
+
+       /**
+        * @param StandaloneView $view
+        * @param PageRenderer $pageRenderer
+        * @param LoginController $loginController
+        * @throws \UnexpectedValueException
+        */
+       public function render(StandaloneView $view, PageRenderer $pageRenderer, LoginController $loginController) {
+
+               GeneralUtility::makeInstance(ObjectManager::class)->get(Dispatcher::class)->dispatch(__CLASS__, self::SIGNAL_getPageRenderer, array($pageRenderer));
+
+               $pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/UserPassLogin');
+
+               $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/UserPassLoginForm.html'));
+               if (GeneralUtility::getIndpEnv('TYPO3_SSL')) {
+                       $view->assign('presetUsername', GeneralUtility::_GP('u'));
+                       $view->assign('presetPassword', GeneralUtility::_GP('p'));
+               }
+       }
+
+}
index 45fc7e3..8f3c7d2 100644 (file)
@@ -34,6 +34,54 @@ Have a nice day.</source>
                        <trans-unit id="foldertreeview.noFolders.message">
                                <source>You do not have access to any folder. Please ask your administrator to fix access permissions for your account.</source>
                        </trans-unit>
+                       <trans-unit id="login.link">
+                               <source>Login with username and password</source>
+                       </trans-unit>
+                       <trans-unit id="login.username">
+                               <source>Username</source>
+                       </trans-unit>
+                       <trans-unit id="login.password">
+                               <source>Password</source>
+                       </trans-unit>
+                       <trans-unit id="login.error.capslock">
+                               <source>Attention: Caps lock enabled!</source>
+                       </trans-unit>
+                       <trans-unit id="login.interface">
+                               <source>Interface</source>
+                       </trans-unit>
+                       <trans-unit id="login.news.previous">
+                               <source>Previous</source>
+                       </trans-unit>
+                       <trans-unit id="login.news.next">
+                               <source>Next</source>
+                       </trans-unit>
+                       <trans-unit id="login.error.message">
+                               <source>Your login attempt did not succeed</source>
+                       </trans-unit>
+                       <trans-unit id="login.error.description">
+                               <source>Make sure to spell your username and password correctly, including upper/lowercase characters.</source>
+                       </trans-unit>
+                       <trans-unit id="login.error.javascript">
+                               <source>Please activate JavaScript!</source>
+                       </trans-unit>
+                       <trans-unit id="login.error.cookies">
+                               <source>Please activate Cookies!</source>
+                       </trans-unit>
+                       <trans-unit id="login.process">
+                               <source>Verifying Login Data ...</source>
+                       </trans-unit>
+                       <trans-unit id="login.submit">
+                               <source>Login</source>
+                       </trans-unit>
+                       <trans-unit id="login.copyrightLink">
+                               <source>More about TYPO3</source>
+                       </trans-unit>
+                       <trans-unit id="login.typo3Logo">
+                               <source>TYPO3 logo</source>
+                       </trans-unit>
+                       <trans-unit id="login.donate">
+                               <source>Donate</source>
+                       </trans-unit>
                </body>
        </file>
 </xliff>
diff --git a/typo3/sysext/backend/Resources/Private/Layouts/Login.html b/typo3/sysext/backend/Resources/Private/Layouts/Login.html
new file mode 100644 (file)
index 0000000..9454a6d
--- /dev/null
@@ -0,0 +1,112 @@
+<div class="typo3-login">
+       <div class="typo3-login-container">
+               <div class="typo3-login-wrap">
+                       <div class="panel panel-lg panel-login">
+                               <div class="panel-body">
+                                       <div class="typo3-login-logo">
+                                               <img src="{logo}" class="typo3-login-image" alt="" />
+                                       </div>
+                                       <f:if condition="{formType} == 'LoginForm'">
+                                               <f:then>
+                                                       <f:if condition="{hasLoginError}">
+                                                               <div class="t3js-login-error" id="t3-login-error">
+                                                                       <div class="alert alert-danger">
+                                                                               <strong><f:translate key="login.error.message" /></strong>
+                                                                               <p><f:translate key="login.error.description" /></p>
+                                                                       </div>
+                                                               </div>
+                                                       </f:if>
+                                                       <noscript>
+                                                               <f:be.infobox message="{f:translate(key: 'login.error.javascript')}" state="2" />
+                                                       </noscript>
+                                                       <div class="hidden t3js-login-error-nocookies">
+                                                               <f:be.infobox message="{f:translate(key: 'login.error.cookies')}" state="2" />
+                                                       </div>
+                                                       <div class="typo3-login-form t3js-login-formfields">
+                                                               <form action="?loginProvider={loginProviderIdentifier}" method="post" name="loginform" id="typo3-login-form">
+                                                                       <f:form.hidden name="login_status" value="login" />
+                                                                       <f:form.hidden name="userident" id="t3-field-userident" class="t3js-login-userident-field" value="" />
+                                                                       <f:form.hidden name="redirect_url" value="{redirectUrl}" />
+                                                                       <f:form.hidden name="loginRefresh" value="{loginRefresh}" />
+
+                                                                       <f:render partial="Login/InterfaceSelector" arguments="{_all}" />
+
+                                                                       <f:render section="loginFormFields" />
+
+                                                                       <div class="form-group" id="t3-login-submit-section">
+                                                                               <button class="btn btn-block btn-login t3js-login-submit" id="t3-login-submit" type="submit" name="commandLI" data-loading-text="<i class='fa fa-circle-o-notch fa-spin'></i> {f:translate(key: 'login.process')}" autocomplete="off">
+                                                                                       <f:translate key="login.submit" />
+                                                                               </button>
+                                                                       </div>
+                                                                       <ul class="list-unstyled typo3-login-links">
+                                                                               <f:for each="{loginProviders}" as="provider" key="providerKey">
+                                                                                       <f:if condition="{provider.label}">
+                                                                                               <f:if condition="{loginProviderIdentifier} != {providerKey}">
+                                                                                                       <li class="t3js-loginprovider-switch" data-providerkey="{providerKey}"><a href="?loginProvider={providerKey}"><i class="fa fa-fw {provider.icon-class}"></i> <span><f:translate key="{provider.label}" /></span></a></li>
+                                                                                               </f:if>
+                                                                                       </f:if>
+                                                                               </f:for>
+                                                                       </ul>
+                                                               </form>
+                                                       </div>
+                                               </f:then>
+                                               <f:else>
+                                                       <form action="../../../../../../index.php" method="post" name="loginform">
+                                                               <input type="hidden" name="login_status" value="logout" />
+                                                               <div class="t3-login-box-body">
+                                                                       <div class="t3-login-logout-form">
+                                                                               <div class="t3-login-username">
+                                                                                       <div class="t3-login-label t3-username">
+                                                                                               <f:translate key="login.username" />
+                                                                                       </div>
+                                                                                       <div class="t3-username-current">
+                                                                                               {backendUser.username}
+                                                                                       </div>
+                                                                               </div>
+                                                                               <f:if condition="{showInterfaceSelector}">
+                                                                                       <f:then>
+                                                                                               <div class="t3-login-interface">
+                                                                                                       <div class="t3-login-label t3-interface-selector">
+                                                                                                               <f:translate key="login.interface" />
+                                                                                                       </div>
+                                                                                                       <f:form.select name="users" options="{interfaces}" optionValueField="jumpScript" optionLabelField="label" />
+                                                                                               </div>
+                                                                                       </f:then>
+                                                                                       <f:else>
+                                                                                               <f:if condition="{interface}">
+                                                                                                       <f:form.hidden name="interface" value="{interface}" />
+                                                                                               </f:if>
+                                                                                       </f:else>
+                                                                               </f:if>
+                                                                               <input type="hidden" name="p_field" value="" />
+                                                                               <input class="btn btn-block btn-lg" type="submit" name="commandLO" value="{f:translate(key: 'login.submit')}" id="t3-login-submit" />
+                                                                       </div>
+                                                               </div>
+                                                       </form>
+                                               </f:else>
+                                       </f:if>
+                               </div>
+                               <f:render partial="LoginNews" arguments="{_all}" />
+                               <div class="panel-footer">
+                                       <div class="login-copyright-wrap">
+                                               <a href="#loginCopyright" class="typo3-login-copyright-link collapsed" data-toggle="collapse" aria-expanded="false" aria-controls="loginCopyright">
+                                                       <span><f:translate key="login.copyrightLink" /></span>
+                                                       <img src="{images.typo3}" alt="{f:translate(key: 'login.typo3Logo')}" width="70" height="20" />
+                                               </a>
+                                               <div id="loginCopyright" class="collapse">
+                                                       <div class="typo3-login-copyright-text">
+                                                               <p>
+                                                                       <f:format.raw>{copyright}</f:format.raw>
+                                                               </p>
+                                                               <ul class="list-unstyled">
+                                                                       <li><a href="http://typo3.org" target="_blank" class="t3-login-link-typo3"><i class="fa fa-external-link"></i> TYPO3.org</a></li>
+                                                                       <li><a href="http://typo3.org/donate/" target="_blank" class="t3-login-link-donate"><i class="fa fa-external-link"></i> <f:translate key="login.donate" /></a></li>
+                                                               </ul>
+                                                       </div>
+                                               </div>
+                                       </div>
+                               </div>
+                       </div>
+               </div>
+       </div>
+</div>
diff --git a/typo3/sysext/backend/Resources/Private/Partials/Login/InterfaceSelector.html b/typo3/sysext/backend/Resources/Private/Partials/Login/InterfaceSelector.html
new file mode 100644 (file)
index 0000000..5cfce60
--- /dev/null
@@ -0,0 +1,17 @@
+<f:if condition="{showInterfaceSelector}">
+       <f:then>
+               <div class="form-group t3js-login-interface-section" id="t3-login-interface-section">
+                       <div class="form-control-wrap">
+                               <div class="form-control-holder">
+                                       <label for="t3-interfaces"><f:translate key="login.interface" /></label>
+                                       <f:form.select id="t3-interfaces" class="form-control" name="interface" options="{interfaces}" optionValueField="interface" optionLabelField="label" />
+                               </div>
+                       </div>
+               </div>
+       </f:then>
+       <f:else>
+               <f:if condition="{interface}">
+                       <f:form.hidden name="interface" value="{interface}" />
+               </f:if>
+       </f:else>
+</f:if>
diff --git a/typo3/sysext/backend/Resources/Private/Partials/LoginNews.html b/typo3/sysext/backend/Resources/Private/Partials/LoginNews.html
new file mode 100644 (file)
index 0000000..7b677c9
--- /dev/null
@@ -0,0 +1,21 @@
+<f:if condition="{loginNewsItems}">
+       <div class="panel-carousel carousel slide t3js-login-news-carousel" id="loginNews" data-interval="0" data-ride="carousel">
+               <div class="carousel-inner" role="listbox">
+                       <f:for each="{loginNewsItems}" as="item" iteration="loginNewsIterator">
+                               <div class="item{f:if(condition: loginNewsIterator.isFirst,then: ' active')}">
+                                       <h3 class="typo3-login-news-heading">{item.header}</h3>
+                                       <f:format.html>{item.content}</f:format.html>
+                                       <span class="text-muted">{item.date}</span>
+                               </div>
+                       </f:for>
+               </div>
+               <a class="left typo3-login-carousel-control" href="#loginNews" role="button" data-slide="prev">
+                       <i class="fa fa-angle-left"></i>
+                       <span class="sr-only"><f:translate key="login.news.previous" /></span>
+               </a>
+               <a class="right typo3-login-carousel-control" href="#loginNews" role="button" data-slide="next">
+                       <i class="fa fa-angle-right"></i>
+                       <span class="sr-only"><f:translate key="login.news.next" /></span>
+               </a>
+       </div>
+</f:if>
diff --git a/typo3/sysext/backend/Resources/Private/Templates/Login.html b/typo3/sysext/backend/Resources/Private/Templates/Login.html
deleted file mode 100644 (file)
index d5b890c..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-<div class="typo3-login">
-       <div class="typo3-login-container">
-               <div class="typo3-login-wrap">
-                       <div class="panel panel-lg panel-login">
-                               <div class="panel-body">
-                                       <div class="typo3-login-logo">
-                                               <img src="{logo}" class="typo3-login-image" />
-                                       </div>
-                                       <f:render section="{formType}" arguments="{_all}" />
-                               </div>
-                               <f:render section="loginNews" arguments="{_all}" />
-                               <div class="panel-footer">
-                                       <div class="login-copyright-wrap">
-                                               <a href="#loginCopyright" class="typo3-login-copyright-link collapsed" data-toggle="collapse" aria-expanded="false" aria-controls="loginCopyright">
-                                                       <span><f:translate key="{labelPrefixPath}copyright.link" /></span>
-                                                       <img src="{images.typo3}" alt="{f:translate(key: '{labelPrefixPath}typo3.logo')}" width="70" height="20" />
-                                               </a>
-                                               <div id="loginCopyright" class="collapse">
-                                                       <div class="typo3-login-copyright-text">
-                                                               <p>
-                                                                       <f:format.raw>{copyright}</f:format.raw>
-                                                               </p>
-                                                               <ul class="list-unstyled">
-                                                                       <li><a href="http://typo3.org" target="_blank" class="t3-login-link-typo3"><i class="fa fa-external-link"></i> TYPO3.org</a></li>
-                                                                       <li><a href="http://typo3.org/donate/" target="_blank" class="t3-login-link-donate"><i class="fa fa-external-link"></i> <f:translate key="{labelPrefixPath}labels.donate" /></a></li>
-                                                               </ul>
-                                                       </div>
-                                               </div>
-                                       </div>
-                               </div>
-                       </div>
-               </div>
-       </div>
-</div>
-
-
-<f:section name="loginForm">
-       <f:if condition="{hasLoginError}">
-               <div class="t3js-login-error" id="t3-login-error">
-                       <div class="alert alert-danger">
-                               <strong><f:translate key="{labelPrefixPath}error.login.title" /></strong>
-                               <p><f:translate key="{labelPrefixPath}error.login.description" /></p>
-                       </div>
-               </div>
-       </f:if>
-       <noscript>
-               <f:be.infobox message="{f:translate(key: '{labelPrefixPath}error.javascript')}" state="2" />
-       </noscript>
-       <div class="hidden t3js-login-error-nocookies">
-               <f:be.infobox message="{f:translate(key: '{labelPrefixPath}error.cookies')}" state="2" />
-       </div>
-       <div class="typo3-login-form t3js-login-formfields">
-               <f:format.raw>{formTag}</f:format.raw>
-                       <f:if condition="{interfaceSelector}">
-                               <div class="form-group t3js-login-interface-section" id="t3-login-interface-section">
-                                       <div class="form-control-wrap">
-                                               <f:format.raw>{interfaceSelector}</f:format.raw>
-                                       </div>
-                               </div>
-                       </f:if>
-                       <div class="form-group t3js-login-username-section" id="t3-login-username-section">
-                               <div class="form-control-wrap">
-                                       <div class="form-control-holder">
-                                               <input type="text" id="t3-username" name="username" value="{presetUsername}" placeholder="{f:translate(key: '{labelPrefixPath}labels.username')}" class="form-control input-login t3js-clearable t3js-login-username-field" required="required">
-                                               <div class="form-notice-capslock hidden t3js-login-alert-capslock">
-                                                       <img src="{images.capslock}" width="14" height="14" alt="{f:translate(key: '{labelPrefixPath}error.capslock')}" title="{f:translate(key: '{labelPrefixPath}error.capslock')}" />
-                                               </div>
-                                       </div>
-                               </div>
-                       </div>
-                       <div class="form-group t3js-login-password-section" id="t3-login-password-section">
-                               <div class="form-control-wrap">
-                                       <div class="form-control-holder">
-                                               <input type="password" id="t3-password" name="p_field" value="{presetPassword}" placeholder="{f:translate(key: '{labelPrefixPath}labels.password')}" class="form-control input-login t3js-clearable t3js-login-password-field" required="required">
-                                               <div class="form-notice-capslock hidden t3js-login-alert-capslock">
-                                                       <img src="{images.capslock}" width="14" height="14" alt="{f:translate(key: '{labelPrefixPath}error.capslock')}" title="{f:translate(key: '{labelPrefixPath}error.capslock')}" />
-                                               </div>
-                                       </div>
-                               </div>
-                       </div>
-                       <f:if condition="{isOpenIdLoaded}">
-                               <div class="form-group hidden t3js-login-openid-section" id="t3-login-openid_url-section">
-                                       <div class="input-group">
-                                               <input type="text" id="openid_url" name="openid_url" value="{presetOpenId}" placeholder="{f:translate(key: '{labelPrefixPath}labels.openId')}" class="form-control input-login t3js-clearable t3js-login-openid-field" />
-                                               <div class="input-group-addon">
-                                                       <span class="fa fa-openid"></span>
-                                               </div>
-                                       </div>
-                               </div>
-                       </f:if>
-                       <div class="form-group" id="t3-login-submit-section">
-                               <button class="btn btn-block btn-login t3js-login-submit" id="t3-login-submit" type="submit" name="commandLI" data-loading-text="<i class='fa fa-circle-o-notch fa-spin'></i> {f:translate(key: '{labelPrefixPath}login_process')}" autocomplete="off">
-                                       <f:translate key="{labelPrefixPath}labels.submitLogin" />
-                               </button>
-                       </div>
-                       <f:if condition="{isOpenIdLoaded}">
-                               <ul class="list-unstyled typo3-login-links">
-                                       <li>
-                                               <a class="t3js-login-switch-to-openid">
-                                                       <i class="fa fa-fw fa-openid"></i> <span><f:translate key="{labelPrefixPath}labels.switchToOpenId" /></span>
-                                               </a>
-                                               <a class="t3js-login-switch-to-default hidden">
-                                                         <i class="fa fa-fw fa-key"></i> <span><f:translate key="{labelPrefixPath}labels.switchToDefault" /></span>
-                                               </a>
-                                       </li>
-                               </ul>
-                       </f:if>
-               </form>
-       </div>
-</f:section>
-
-
-<f:section name="logoutForm">
-       <form action="index.php" method="post" name="loginform">
-               <input type="hidden" name="login_status" value="logout" />
-               <div class="t3-login-box-body">
-                       <div class="t3-login-logout-form">
-                               <div class="t3-login-username">
-                                       <div class="t3-login-label t3-username">
-                                               <f:translate key="{labelPrefixPath}labels.username" />
-                                       </div>
-                                       <div class="t3-username-current">
-                                               {backendUser.username}
-                                       </div>
-                               </div>
-                               <f:if condition="{showInterfaceSelector}">
-                                       <div class="t3-login-interface">
-                                               <div class="t3-login-label t3-interface-selector">
-                                                       <f:translate key="{labelPrefixPath}labels.interface" />
-                                               </div>
-                                               <f:format.raw>{interfaceSelectorJump}</f:format.raw>
-                                       </div>
-                               </f:if>
-                               <input type="hidden" name="p_field" value="" />
-                               <input class="btn btn-block btn-lg" type="submit" name="commandLO" value="{f:translate(key: '{labelPrefixPath}labels.submitLogin')}" id="t3-login-submit" />
-                       </div>
-               </div>
-       </form>
-</f:section>
-
-
-<f:section name="loginNews">
-       <f:if condition="{loginNewsItems}">
-               <div class="panel-carousel carousel slide t3js-login-news-carousel" id="loginNews" data-interval="0" data-ride="carousel">
-                       <div class="carousel-inner" role="listbox">
-                               <f:for each="{loginNewsItems}" as="item" iteration="loginNewsIterator">
-                               <div class="item{f:if(condition: loginNewsIterator.isFirst,then: ' active')}">
-                                       <h3 class="typo3-login-news-heading">{item.header}</h3>
-                                       <f:format.html>{item.content}</f:format.html>
-                                       <span class="text-muted">{item.date}</span>
-                               </div>
-                               </f:for>
-                       </div>
-                       <a class="left typo3-login-carousel-control" href="#loginNews" role="button" data-slide="prev">
-                               <i class="fa fa-angle-left"></i>
-                               <span class="sr-only"><f:translate key="{labelPrefixPath}labels.previous" /></span>
-                       </a>
-                       <a class="right typo3-login-carousel-control" href="#loginNews" role="button" data-slide="next">
-                               <i class="fa fa-angle-right"></i>
-                               <span class="sr-only"><f:translate key="{labelPrefixPath}labels.next" /></span>
-                       </a>
-               </div>
-       </f:if>
-</f:section>
\ No newline at end of file
diff --git a/typo3/sysext/backend/Resources/Private/Templates/UserPassLoginForm.html b/typo3/sysext/backend/Resources/Private/Templates/UserPassLoginForm.html
new file mode 100644 (file)
index 0000000..bccc96b
--- /dev/null
@@ -0,0 +1,24 @@
+<f:layout name="Login" />
+
+<f:section name="loginFormFields">
+       <div class="form-group t3js-login-username-section" id="t3-login-username-section">
+               <div class="form-control-wrap">
+                       <div class="form-control-holder">
+                               <input type="text" id="t3-username" name="username" value="{presetUsername}" placeholder="{f:translate(key: 'login.username')}" class="form-control input-login t3js-clearable t3js-login-username-field" autofocus="autofocus" required="required">
+                               <div class="form-notice-capslock hidden t3js-login-alert-capslock">
+                                       <img src="{images.capslock}" width="14" height="14" alt="{f:translate(key: 'login.error.capslock')}" title="{f:translate(key: 'login.error.capslock')}" />
+                               </div>
+                       </div>
+               </div>
+       </div>
+       <div class="form-group t3js-login-password-section" id="t3-login-password-section">
+               <div class="form-control-wrap">
+                       <div class="form-control-holder">
+                               <input type="password" id="t3-password" name="p_field" value="{presetPassword}" placeholder="{f:translate(key: 'login.password')}" class="form-control input-login t3js-clearable t3js-login-password-field" required="required">
+                               <div class="form-notice-capslock hidden t3js-login-alert-capslock">
+                                       <img src="{images.capslock}" width="14" height="14" alt="{f:translate(key: 'login.error.capslock')}" title="{f:translate(key: 'login.error.capslock')}" />
+                               </div>
+                       </div>
+               </div>
+       </div>
+</f:section>
index d09a587..b6905b1 100755 (executable)
@@ -19,88 +19,16 @@ define('TYPO3/CMS/Backend/Login', ['jquery', 'TYPO3/CMS/Backend/jquery.clearable
                options: {
                        loginForm: '#typo3-login-form',
                        interfaceField: '.t3js-login-interface-field',
-                       interfaceSection: '.t3js-login-interface-section',
-                       usernameField: '.t3js-login-username-field',
-                       usernameSection: '.t3js-login-username-section',
-                       passwordField: '.t3js-login-password-field',
-                       passwordSection: '.t3js-login-password-section',
-                       openIdField: '.t3js-login-openid-field',
-                       openIdSection: '.t3js-login-openid-section',
                        useridentField: '.t3js-login-userident-field',
                        submitButton: '.t3js-login-submit',
                        error: '.t3js-login-error',
                        errorNoCookies: '.t3js-login-error-nocookies',
                        formFields: '.t3js-login-formfields',
-                       switchOpenIdSelector: '.t3js-login-switch-to-openid',
-                       switchDefaultSelector: '.t3js-login-switch-to-default',
                        submitHandler: null
                }
        },
        options = BackendLogin.options;
 
-       // Checks whether capslock is enabled (returns TRUE if enabled, false otherwise)
-       // thanks to http://24ways.org/2007/capturing-caps-lock
-       BackendLogin.isCapslockEnabled = function(e) {
-               var ev = e ? e : window.event;
-               if (!ev) {
-                       return;
-               }
-               // get key pressed
-               var which = -1;
-               if (ev.which) {
-                       which = ev.which;
-               } else if (ev.keyCode) {
-                       which = ev.keyCode;
-               }
-               // get shift status
-               var shift_status = false;
-               if (ev.shiftKey) {
-                       shift_status = ev.shiftKey;
-               } else if (ev.modifiers) {
-                       shift_status = !!(ev.modifiers & 4);
-               }
-               return (which >= 65 && which <= 90 && !shift_status)
-                       || (which >= 97 && which <= 122 && shift_status);
-       };
-
-       /**
-        * Change to Interface for OpenId login and save the selection to a cookie
-        */
-       BackendLogin.switchToOpenId = function(e) {
-               if (!$(this).hasClass("disabled")) {
-                       $(options.switchOpenIdSelector).addClass('hidden');
-                       $(options.switchDefaultSelector).removeClass('hidden');
-                       $(options.interfaceSection).addClass('hidden');
-                       $(options.passwordSection + ', ' + options.usernameSection).addClass('hidden');
-                       $(options.openIdSection).removeClass('hidden');
-                       $(options.openIdField).trigger('focus');
-                       $(options.usernameField).val('openid_url');
-                       $(options.passwordField).val('');
-                       BackendLogin.setLogintypeCookie('openid');
-               } else {
-                       return false;
-               }
-       };
-
-       /**
-        * Change to Interface for default login and save the selection to a cookie
-        */
-       BackendLogin.switchToDefault = function(e) {
-               if (!$(this).hasClass("disabled")) {
-                       $(options.switchOpenIdSelector).removeClass('hidden');
-                       $(options.switchDefaultSelector).addClass('hidden');
-                       $(options.interfaceSection).removeClass('hidden');
-                       $(options.passwordSection + ', ' + options.usernameSection).removeClass('hidden');
-                       $(options.openIdSection).addClass('hidden');
-                       $(options.usernameField).trigger('focus');
-                       $(options.openIdField).val('');
-                       $(options.usernameField).val('');
-                       BackendLogin.setLogintypeCookie('username');
-               } else {
-                       return false;
-               }
-       };
-
        /**
         * Hide all form fields and show a progress message and icon
         */
@@ -108,9 +36,6 @@ define('TYPO3/CMS/Backend/Login', ['jquery', 'TYPO3/CMS/Backend/jquery.clearable
                $(options.submitButton).button('loading');
                $(options.error).addClass('hidden');
                $(options.errorNoCookies).addClass('hidden');
-
-               $(options.switchOpenIdSelector).addClass('disabled');
-               $(options.switchDefaultSelector).addClass('disabled');
        };
 
        /**
@@ -129,36 +54,6 @@ define('TYPO3/CMS/Backend/Login', ['jquery', 'TYPO3/CMS/Backend/jquery.clearable
        };
 
        /**
-        * Reset user password field to prevent it from being submitted
-        */
-       BackendLogin.resetPassword = function() {
-               "use strict";
-
-               var $passwordField = $(BackendLogin.options.passwordField);
-               $(BackendLogin.options.useridentField).val($passwordField.val());
-               $passwordField.val('');
-       };
-
-       /**
-        * Store a login type in a cookie to save it for future visits
-        * Login type means whether you login by username/password or OpenID
-        */
-       BackendLogin.setLogintypeCookie = function(type) {
-               var now = new Date();
-               var expires = new Date(now.getTime() + 1000*60*60*24*365); // cookie expires in one year
-               document.cookie = 'typo3-login-method=' + type + '; expires=' + expires.toGMTString() + ';';
-       };
-
-       /**
-        * Check if a login type was stored in a cookie and change the Interface accordingly
-        */
-       BackendLogin.checkForLogintypeCookie = function() {
-               if (document.cookie.indexOf('typo3-login-method=openid') >- 1) {
-                       BackendLogin.switchToOpenId();
-               }
-       };
-
-       /**
         * Store the new selected Interface in a cookie to save it for future visits
         */
        BackendLogin.interfaceSelectorChanged = function() {
@@ -168,13 +63,6 @@ define('TYPO3/CMS/Backend/Login', ['jquery', 'TYPO3/CMS/Backend/jquery.clearable
        };
 
        /**
-        * Clears an input field and sets focus to it
-        */
-       BackendLogin.clearInputField = function($formFieldElement) {
-               $formFieldElement.val('').focus();
-       };
-
-       /**
         * Check if an interface was stored in a cookie and preselect it in the select box
         */
        BackendLogin.checkForInterfaceCookie = function() {
@@ -189,13 +77,6 @@ define('TYPO3/CMS/Backend/Login', ['jquery', 'TYPO3/CMS/Backend/jquery.clearable
        };
 
        /**
-        * To prevent its unintented use when typing the password, the user is warned when Capslock is on
-        */
-       BackendLogin.showCapsLockWarning = function($alertIconElement, event) {
-               $alertIconElement.toggleClass('hidden', !BackendLogin.isCapslockEnabled(event));
-       };
-
-       /**
         * Hides input fields and shows cookie warning
         */
        BackendLogin.showCookieWarning = function() {
@@ -239,14 +120,9 @@ define('TYPO3/CMS/Backend/Login', ['jquery', 'TYPO3/CMS/Backend/jquery.clearable
        };
 
        /**
-        * Registers listeners for the Login Interface (e.g. to toggle OpenID and Default login)
+        * Registers listeners for the Login Interface
         */
        BackendLogin.initializeEvents = function() {
-               // register default submit handler
-               BackendLogin.options.submitHandler = BackendLogin.resetPassword;
-
-               $(options.switchOpenIdSelector).on('click', BackendLogin.switchToOpenId);
-               $(options.switchDefaultSelector).on('click', BackendLogin.switchToDefault);
                $(options.loginForm).on('submit', BackendLogin.handleSubmit);
 
                // The Interface selector is not always present, so this check is needed
@@ -254,10 +130,6 @@ define('TYPO3/CMS/Backend/Login', ['jquery', 'TYPO3/CMS/Backend/jquery.clearable
                        $(document).on('change blur', options.interfaceField, BackendLogin.interfaceSelectorChanged);
                }
 
-               $(document).on('keypress', options.usernameField + ', ' + options.passwordField + ', ' + options.openIdField, function(evt) {
-                       BackendLogin.showCapsLockWarning($(this).parent().parent().find('.t3js-login-alert-capslock'), evt);
-               });
-
                $('.t3js-clearable').clearable();
 
                // carousel news height transition
@@ -266,39 +138,18 @@ define('TYPO3/CMS/Backend/Login', ['jquery', 'TYPO3/CMS/Backend/jquery.clearable
                        $(this).find('div.active').parent().animate({ height: nextH }, 500);
                });
        };
-       // initialize and return the BackendLogin object
-       return function() {
-               $(document).ready(function() {
-                       BackendLogin.checkForInterfaceCookie();
-                       BackendLogin.checkForLogintypeCookie();
-                       BackendLogin.checkCookieSupport();
-                       BackendLogin.initializeEvents();
-
-                       // previously named "startUp"
-
-                       // If the login screen is shown in the login_frameset window for re-login,
-                       // then try to get the username of the current/former login from opening windows main frame:
-                       try {
-                               if (parent.opener && parent.opener.TS && parent.opener.TS.username) {
-                                       $(options.usernameField).val(parent.opener.TS.username);
-                               }
-                       } catch (error) {} // continue
 
-                       // previously named "check focus"
-                       if ($(options.usernameField).val() == '') {
-                               $(options.usernameField).focus();
-                       } else if ($(options.passwordField).attr('type') !== 'hidden') {
-                               $(options.passwordField).focus();
-                       }
-
-               });
-
-               // prevent opening the login form in the backend frameset
-               if (top.location.href != self.location.href) {
-                       top.location.href = self.location.href;
-               }
-
-               TYPO3.BackendLogin = BackendLogin;
-               return BackendLogin;
-       }();
+       // initialize and return the BackendLogin object
+       $(document).ready(function() {
+               BackendLogin.checkCookieSupport();
+               BackendLogin.checkForInterfaceCookie();
+               BackendLogin.initializeEvents();
+       });
+
+       // prevent opening the login form in the backend frameset
+       if (top.location.href != self.location.href) {
+               top.location.href = self.location.href;
+       }
+
+       return BackendLogin;
 });
diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/UserPassLogin.js b/typo3/sysext/backend/Resources/Public/JavaScript/UserPassLogin.js
new file mode 100644 (file)
index 0000000..6261222
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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!
+ */
+
+/**
+ * JavaScript module for the UsernamePasswordLoginProvider
+ */
+define('TYPO3/CMS/Backend/UserPassLogin', ['jquery', 'TYPO3/CMS/Backend/Login'], function($, Login) {
+       var UserPassLogin = {
+               options: {
+                       usernameField: '.t3js-login-username-field',
+                       passwordField: '.t3js-login-password-field'
+               }
+       };
+
+       // Checks whether capslock is enabled (returns TRUE if enabled, false otherwise)
+       // thanks to http://24ways.org/2007/capturing-caps-lock
+       UserPassLogin.isCapslockEnabled = function(e) {
+               var ev = e ? e : window.event;
+               if (!ev) {
+                       return;
+               }
+               // get key pressed
+               var which = -1;
+               if (ev.which) {
+                       which = ev.which;
+               } else if (ev.keyCode) {
+                       which = ev.keyCode;
+               }
+               // get shift status
+               var shift_status = false;
+               if (ev.shiftKey) {
+                       shift_status = ev.shiftKey;
+               } else if (ev.modifiers) {
+                       shift_status = !!(ev.modifiers & 4);
+               }
+               return (which >= 65 && which <= 90 && !shift_status)
+                       || (which >= 97 && which <= 122 && shift_status);
+       };
+
+       /**
+        * Reset user password field to prevent it from being submitted
+        */
+       UserPassLogin.resetPassword = function() {
+               "use strict";
+
+               var $passwordField = $(UserPassLogin.options.passwordField);
+               $(Login.options.useridentField).val($passwordField.val());
+               $passwordField.val('');
+       };
+
+       /**
+        * To prevent its unintended use when typing the password, the user is warned when Capslock is on
+        */
+       UserPassLogin.showCapsLockWarning = function(event) {
+               $(this).parent().parent().find('.t3js-login-alert-capslock').toggleClass('hidden', !UserPassLogin.isCapslockEnabled(event));
+       };
+
+       // initialize and return the UserPassLogin object
+       $(document).ready(function() {
+               // register submit handler
+               Login.options.submitHandler = UserPassLogin.resetPassword;
+
+               var $usernameField = $(UserPassLogin.options.usernameField);
+               var $passwordField = $(UserPassLogin.options.passwordField);
+
+               $usernameField.on('keypress', UserPassLogin.showCapsLockWarning);
+               $passwordField.on('keypress', UserPassLogin.showCapsLockWarning);
+
+               // If the login screen is shown in the login_frameset window for re-login,
+               // then try to get the username of the current/former login from opening windows main frame:
+               try {
+                       if (parent.opener && parent.opener.TS && parent.opener.TS.username) {
+                               $usernameField.val(parent.opener.TS.username);
+                       }
+               } catch (error) {} // continue
+
+               if ($usernameField.val() == '') {
+                       $usernameField.focus();
+               } else {
+                       $passwordField.focus();
+               }
+       });
+
+       return UserPassLogin;
+});
diff --git a/typo3/sysext/backend/Tests/Unit/Controller/LoginControllerTest.php b/typo3/sysext/backend/Tests/Unit/Controller/LoginControllerTest.php
new file mode 100644 (file)
index 0000000..c6a0cb6
--- /dev/null
@@ -0,0 +1,144 @@
+<?php
+namespace TYPO3\CMS\Backend\Tests\Unit\Controller;
+
+/*
+ * 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!
+ */
+
+use TYPO3\CMS\Backend\Controller\LoginController;
+use TYPO3\CMS\Backend\LoginProvider\UsernamePasswordLoginProvider;
+use TYPO3\CMS\Core\Tests\UnitTestCase;
+
+/**
+ * Class LoginControllerTest
+ *
+ * @author Markus Klein <markus.klein@typo3.org>
+ */
+class LoginControllerTest extends UnitTestCase {
+
+       /**
+        * @var LoginController|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface
+        */
+       protected $loginControllerMock;
+
+       /**
+        * @throws \InvalidArgumentException
+        */
+       protected function setUp() {
+               $this->loginControllerMock = $this->getAccessibleMock(LoginController::class, ['dummy'], [], '', FALSE);
+       }
+
+       /**
+        * @test
+        * @expectedException \RuntimeException
+        * @expectedExceptionCode 1433417281
+        */
+       public function validateAndSortLoginProvidersDetectsMissingProviderConfiguration() {
+               unset($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['backend']['loginProviders']);
+               $this->loginControllerMock->_call('validateAndSortLoginProviders');
+       }
+
+       /**
+        * @test
+        * @expectedException \RuntimeException
+        * @expectedExceptionCode 1433417281
+        */
+       public function validateAndSortLoginProvidersDetectsNonArrayProviderConfiguration() {
+               $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['backend']['loginProviders'] = 'foo';
+               $this->loginControllerMock->_call('validateAndSortLoginProviders');
+       }
+
+       /**
+        * @test
+        * @expectedException \RuntimeException
+        * @expectedExceptionCode 1433417281
+        */
+       public function validateAndSortLoginProvidersDetectsIfNoProviderIsRegistered() {
+               $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['backend']['loginProviders'] = [];
+               $this->loginControllerMock->_call('validateAndSortLoginProviders');
+       }
+
+       /**
+        * @test
+        * @expectedException \RuntimeException
+        * @expectedExceptionCode 1433416043
+        */
+       public function validateAndSortLoginProvidersDetectsMissingConfigurationForProvider() {
+               $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['backend']['loginProviders'] = [
+                       1433419736 => []
+               ];
+               $this->loginControllerMock->_call('validateAndSortLoginProviders');
+       }
+
+       /**
+        * @test
+        * @expectedException \RuntimeException
+        * @expectedExceptionCode 1433416043
+        */
+       public function validateAndSortLoginProvidersDetectsWrongProvider() {
+               $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['backend']['loginProviders'] = [
+                       1433419736 => [
+                               'provider' => \stdClass::class
+                       ]
+               ];
+               $this->loginControllerMock->_call('validateAndSortLoginProviders');
+       }
+
+       /**
+        * @test
+        * @expectedException \RuntimeException
+        * @expectedExceptionCode 1433416044
+        */
+       public function validateAndSortLoginProvidersDetectsMissingLabel() {
+               $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['backend']['loginProviders'] = [
+                       1433419736 => [
+                               'provider' => UsernamePasswordLoginProvider::class,
+                               'sorting' => 30,
+                               'icon-class' => 'foo'
+                       ]
+               ];
+               $this->loginControllerMock->_call('validateAndSortLoginProviders');
+       }
+
+       /**
+        * @test
+        * @expectedException \RuntimeException
+        * @expectedExceptionCode 1433416045
+        */
+       public function validateAndSortLoginProvidersDetectsMissingIconClass() {
+               $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['backend']['loginProviders'] = [
+                       1433419736 => [
+                               'provider' => UsernamePasswordLoginProvider::class,
+                               'sorting' => 30,
+                               'label' => 'foo'
+                       ]
+               ];
+               $this->loginControllerMock->_call('validateAndSortLoginProviders');
+       }
+
+       /**
+        * @test
+        * @expectedException \RuntimeException
+        * @expectedExceptionCode 1433416046
+        */
+       public function validateAndSortLoginProvidersDetectsMissingSorting() {
+               $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['backend']['loginProviders'] = [
+                       1433419736 => [
+                               'provider' => UsernamePasswordLoginProvider::class,
+                               'label' => 'foo',
+                               'icon-class' => 'foo'
+                       ]
+               ];
+               $this->loginControllerMock->_call('validateAndSortLoginProviders');
+       }
+
+}
index 24450b1..4b2f541 100644 (file)
@@ -15,6 +15,13 @@ if (TYPO3_MODE === 'BE') {
        $GLOBALS['TYPO3_CONF_VARS']['BE']['toolbarItems'][] = \TYPO3\CMS\Backend\Backend\ToolbarItems\ShortcutToolbarItem::class;
        $GLOBALS['TYPO3_CONF_VARS']['BE']['toolbarItems'][] = \TYPO3\CMS\Backend\Backend\ToolbarItems\SystemInformationToolbarItem::class;
        $GLOBALS['TYPO3_CONF_VARS']['BE']['toolbarItems'][] = \TYPO3\CMS\Backend\Backend\ToolbarItems\UserToolbarItem::class;
+
+       $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['backend']['loginProviders'][1433416747] = array(
+               'provider' => \TYPO3\CMS\Backend\LoginProvider\UsernamePasswordLoginProvider::class,
+               'sorting' => 50,
+               'icon-class' => 'fa-key',
+               'label' => 'LLL:EXT:backend/Resources/Private/Language/locallang.xlf:login.link'
+       );
 }
 
 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default'] = \TYPO3\CMS\Core\FrontendEditing\FrontendEditingController::class;
index 3ecee91..859601d 100644 (file)
@@ -1112,7 +1112,6 @@ return array(
        'tx_rsaauth_php_backend' => \TYPO3\CMS\Rsaauth\Backend\PhpBackend::class,
        'tx_rsaauth_backendwarnings' => \TYPO3\CMS\Rsaauth\BackendWarnings::class,
        'tx_rsaauth_feloginhook' => \TYPO3\CMS\Rsaauth\Hook\FrontendLoginHook::class,
-       'tx_rsaauth_loginformhook' => \TYPO3\CMS\Rsaauth\Hook\LoginFormHook::class,
        'tx_rsaauth_usersetuphook' => \TYPO3\CMS\Rsaauth\Hook\UserSetupHook::class,
        'tx_rsaauth_keypair' => \TYPO3\CMS\Rsaauth\Keypair::class,
        'tx_rsaauth_sv1' => \TYPO3\CMS\Rsaauth\RsaAuthService::class,
index 8981d63..8f13ece 100644 (file)
@@ -5529,11 +5529,6 @@ class tx_rsaauth_feloginhook extends \TYPO3\CMS\Rsaauth\Hook\FrontendLoginHook {
 /**
  * @deprecated since 6.0, removed since 7.0
  */
-class tx_rsaauth_loginformhook extends \TYPO3\CMS\Rsaauth\Hook\LoginFormHook {}
-
-/**
- * @deprecated since 6.0, removed since 7.0
- */
 class tx_rsaauth_usersetuphook extends \TYPO3\CMS\Rsaauth\Hook\UserSetupHook {}
 
 /**
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Breaking-66669-BackendLoginControllerRefactored.rst b/typo3/sysext/core/Documentation/Changelog/master/Breaking-66669-BackendLoginControllerRefactored.rst
new file mode 100644 (file)
index 0000000..5a02aa0
--- /dev/null
@@ -0,0 +1,38 @@
+=====================================================
+Breaking: #66669 - Backend LoginController refactored
+=====================================================
+
+Description
+===========
+
+The backend login has been completely refactored and a new API has been introduced.
+The openid form has been extracted and is now using the new API as well.
+
+
+Impact
+======
+
+All former member variables of the  ``LoginController`` class have been removed or made protected, together with some, now pointless, hooks
+and their related classes.
+
+The deleted hooks are:
+
+- $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/index.php']['loginScriptHook']
+- $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/index.php']['loginFormHook']
+
+The removed class and its alias:
+
+- TYPO3\CMS\Rsaauth\Hook\LoginFormHook
+- tx_rsaauth_loginformhook
+
+
+Affected Installations
+======================
+
+Any code manipulating the BE login.
+
+
+Migration
+=========
+
+Use the new backend login form API.
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-66669-BeLoginFormAPI.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-66669-BeLoginFormAPI.rst
new file mode 100644 (file)
index 0000000..32a3a78
--- /dev/null
@@ -0,0 +1,98 @@
+===================================
+Feature: #66669 - BE login form API
+===================================
+
+Description
+===========
+
+The backend login has been completely refactored and a new API has been introduced.
+The OpenID form has been extracted and is now using the new API as well and is now completely
+independent of the central Core classes for the first time.
+
+
+Registering a login provider
+----------------------------
+
+The concept of the new backend login is based on "login providers".
+A login provider can be registered within your ``ext_localconf.php`` file like this:
+
+.. code-block:: php
+
+       $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['backend']['loginProviders'][1433416020] = [
+               'provider' => \TYPO3\CMS\Backend\LoginProvider\UsernamePasswordLoginProvider::class,
+               'sorting' => 50,
+               'icon-class' => 'fa-key',
+               'label' => 'LLL:EXT:backend/Resources/Private/Language/locallang.xlf:login.link'
+       ];
+
+The settings are defined as:
+
+* ``provider``: The login provider class name, which must implement ``TYPO3\CMS\Backend\LoginProvider\LoginProviderInterface``.
+* ``sorting``: The sorting is important for the ordering of the links to the possible login providers on the login screen.
+* ``icon-class``: The font-awesome icon name for the link on the login screen.
+* ``label``: The label for the login provider link on the login screen.
+
+For a new login provider you have to register a **new key** - a unix timestamp - in ``$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['backend']['loginProviders']``.
+If your login provider extends another one, you may only overwrite necessary settings. An example would be to
+extend an existing provider and replace its registered 'provider' class with your new class name.
+
+.. code-block:: php
+
+       $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['backend']['loginProviders'][1433416020]['provider'] = CustomProviderExtendingUsernamePasswordLoginProvider::class
+
+LoginProviderInterface
+----------------------
+
+The LoginProviderInterface contains only one method:
+
+``public function render(StandaloneView $view, PageRenderer $pageRenderer, LoginController $loginController);``
+
+The parameters are defined as:
+
+* ``$view``: The Fluid StandaloneView which renders the login screen. You have to set the template file and you may add variables to the view according to your needs.
+* ``$pageRenderer``: The PageRenderer instance provides possibility to add necessary JavaScript resources.
+* ``$loginController``: The LoginController instance.
+
+
+The View
+--------
+
+As mentioned above, the render method gets the Fluid StandaloneView as first parameter.
+You have to set the template path and filename using the methods of this object.
+The template file must only contain the form fields, not the form-tag.
+Later on, the view renders the complete login screen.
+
+View requirements:
+
+* The template must use the ``Login``-layout provided by the Core ``<f:layout name="Login">``.
+* Form fields must be provided within the section ``<f:section name="loginFormFields">``.
+
+
+.. code-block:: html
+
+       <f:layout name="Login" />
+       <f:section name="loginFormFields">
+               <div class="form-group t3js-login-openid-section" id="t3-login-openid_url-section">
+                       <div class="input-group">
+                               <input type="text" id="openid_url" name="openid_url" value="{presetOpenId}" autofocus="autofocus" placeholder="{f:translate(key: 'openId', extensionName: 'openid')}" class="form-control input-login t3js-clearable t3js-login-openid-field" />
+                               <div class="input-group-addon">
+                                       <span class="fa fa-openid"></span>
+                               </div>
+                       </div>
+               </div>
+       </f:section>
+
+
+Examples
+--------
+
+Within the Core you can find two best practice implementations:
+
+1. EXT:backend, which implements the ``UsernamePasswordLoginProvider`` (the default)
+2. EXT:openid, which implements the ``OpenIdLoginProvider`` and adds a second login option
+
+
+Impact
+======
+
+All extensions, which add additional fields to the login form, must be updated and make use of the new BE login form API.
index 86298b9..ce167ed 100644 (file)
@@ -5,39 +5,49 @@
                <body>
                        <trans-unit id="labels.username">
                                <source>Username</source>
+                               <note from="developer">This label is deprecated (not used anymore) since CMS 7</note>
                        </trans-unit>
                        <trans-unit id="labels.openId">
                                <source>OpenID</source>
+                               <note from="developer">This label is deprecated (not used anymore) since CMS 7</note>
                        </trans-unit>
                        <trans-unit id="labels.password">
                                <source>Password</source>
+                               <note from="developer">This label is deprecated (not used anymore) since CMS 7</note>
                        </trans-unit>
                        <trans-unit id="labels.interface">
                                <source>Interface</source>
+                               <note from="developer">This label is deprecated (not used anymore) since CMS 7</note>
                        </trans-unit>
                        <trans-unit id="labels.previous">
                                <source>Previous</source>
+                               <note from="developer">This label is deprecated (not used anymore) since CMS 7</note>
                        </trans-unit>
                        <trans-unit id="labels.next">
                                <source>Next</source>
+                               <note from="developer">This label is deprecated (not used anymore) since CMS 7</note>
                        </trans-unit>
                        <trans-unit id="labels.submitLogin">
                                <source>Login</source>
+                               <note from="developer">This label is deprecated (not used anymore) since CMS 7</note>
                        </trans-unit>
                        <trans-unit id="labels.submitLogout">
                                <source>Log Out</source>
                        </trans-unit>
                        <trans-unit id="labels.donate">
                                <source>Donate</source>
+                               <note from="developer">This label is deprecated (not used anymore) since CMS 7</note>
                        </trans-unit>
                        <trans-unit id="labels.whatIsOpenId">
                                <source>What is OpenId?</source>
                        </trans-unit>
                        <trans-unit id="labels.switchToOpenId">
                                <source>Switch to OpenID</source>
+                               <note from="developer">This label is deprecated (not used anymore) since CMS 7</note>
                        </trans-unit>
                        <trans-unit id="labels.switchToDefault">
                                <source>Switch to default login</source>
+                               <note from="developer">This label is deprecated (not used anymore) since CMS 7</note>
                        </trans-unit>
                        <trans-unit id="availableInterfaces">
                                <source>Backend,Front End,Traditional Backend</source>
@@ -68,6 +78,7 @@
                        </trans-unit>
                        <trans-unit id="login_process">
                                <source>Verifying Login Data ...</source>
+                               <note from="developer">This label is deprecated (not used anymore) since CMS 7</note>
                        </trans-unit>
                        <trans-unit id="warning.incompatibleBrowserHeadline">
                                <source>Unsupported Browser</source>
                        </trans-unit>
                        <trans-unit id="error.login.title">
                                <source>Your login attempt did not succeed</source>
+                               <note from="developer">This label is deprecated (not used anymore) since CMS 7</note>
                        </trans-unit>
                        <trans-unit id="error.login.description">
                                <source>Make sure to spell your username and password correctly, including upper/lowercase characters.</source>
+                               <note from="developer">This label is deprecated (not used anymore) since CMS 7</note>
                        </trans-unit>
                        <trans-unit id="error.javascript">
                                <source>Activate JavaScript, please!</source>
+                               <note from="developer">This label is deprecated (not used anymore) since CMS 7</note>
                        </trans-unit>
                        <trans-unit id="error.cookies">
                                <source>Activate Cookies, please!</source>
+                               <note from="developer">This label is deprecated (not used anymore) since CMS 7</note>
                        </trans-unit>
                        <trans-unit id="error.cookies_ignore">
                                <source>Ignore!</source>
                        </trans-unit>
                        <trans-unit id="error.capslock">
                                <source>Attention: Caps lock enabled!</source>
+                               <note from="developer">This label is deprecated (not used anymore) since CMS 7</note>
                        </trans-unit>
                        <trans-unit id="error.furtherInformation">
                                <source>For more information or further help click here</source>
                        </trans-unit>
                        <trans-unit id="typo3.logo">
                                <source>TYPO3 logo</source>
+                               <note from="developer">This label is deprecated (not used anymore) since CMS 7</note>
                        </trans-unit>
                        <trans-unit id="typo3.cms">
                                <source>TYPO3 CMS</source>
                        </trans-unit>
                        <trans-unit id="copyright.link">
                                <source>More about TYPO3</source>
+                               <note from="developer">This label is deprecated (not used anymore) since CMS 7</note>
                        </trans-unit>
                        <trans-unit id="extension.copyright">
                                <source>Extensions are copyright of their respective owners.</source>
diff --git a/typo3/sysext/openid/Classes/LoginProvider/OpenIdLoginProvider.php b/typo3/sysext/openid/Classes/LoginProvider/OpenIdLoginProvider.php
new file mode 100644 (file)
index 0000000..fbff347
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+namespace TYPO3\CMS\Openid\LoginProvider;
+
+/*
+ * 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!
+ */
+
+use TYPO3\CMS\Backend\Controller\LoginController;
+use TYPO3\CMS\Backend\LoginProvider\LoginProviderInterface;
+use TYPO3\CMS\Core\Page\PageRenderer;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Fluid\View\StandaloneView;
+
+/**
+ * Class OpenIdLoginProvider
+ *
+ * @package TYPO3\CMS\Openid\LoginProvider
+ */
+class OpenIdLoginProvider implements LoginProviderInterface {
+
+       /**
+        * @param StandaloneView $view
+        * @param PageRenderer $pageRenderer
+        * @param LoginController $loginController
+        */
+       public function render(StandaloneView $view, PageRenderer $pageRenderer, LoginController $loginController) {
+               $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:openid/Resources/Private/Templates/OpenidLogin.html'));
+               $view->assign('presetOpenId', GeneralUtility::_GP('openid_url'));
+       }
+
+}
index cf715aa..30ad2f2 100644 (file)
                        <trans-unit id="html.submit">
                                <source>Login</source>
                        </trans-unit>
+                       <trans-unit id="login.link">
+                               <source>Login with OpenID</source>
+                       </trans-unit>
+                       <trans-unit id="openId">
+                               <source>OpenID</source>
+                       </trans-unit>
                </body>
        </file>
 </xliff>
diff --git a/typo3/sysext/openid/Resources/Private/Templates/OpenidLogin.html b/typo3/sysext/openid/Resources/Private/Templates/OpenidLogin.html
new file mode 100644 (file)
index 0000000..b8e685a
--- /dev/null
@@ -0,0 +1,12 @@
+<f:layout name="Login" />
+
+<f:section name="loginFormFields">
+       <div class="form-group t3js-login-openid-section" id="t3-login-openid_url-section">
+               <div class="input-group">
+                       <input type="text" id="openid_url" name="openid_url" value="{presetOpenId}" autofocus="autofocus" placeholder="{f:translate(key: 'openId', extensionName: 'openid')}" class="form-control input-login t3js-clearable t3js-login-openid-field" />
+                       <div class="input-group-addon">
+                               <span class="fa fa-openid"></span>
+                       </div>
+               </div>
+       </div>
+</f:section>
index 8c00435..af70e71 100644 (file)
@@ -45,3 +45,10 @@ $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['setup']['accessLevelCheck'][\TYPO3\CM
 
 // Use popup window to refresh login instead of the AJAX relogin:
 $GLOBALS['TYPO3_CONF_VARS']['BE']['showRefreshLoginPopup'] = 1;
+
+$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['backend']['loginProviders'][1433416748] = array(
+       'provider' => \TYPO3\CMS\Openid\LoginProvider\OpenIdLoginProvider::class,
+       'sorting' => 25,
+       'icon-class' => 'fa-openid',
+       'label' => 'LLL:EXT:openid/Resources/Private/Language/locallang.xlf:login.link'
+);
diff --git a/typo3/sysext/rsaauth/Classes/Hook/LoginFormHook.php b/typo3/sysext/rsaauth/Classes/Hook/LoginFormHook.php
deleted file mode 100644 (file)
index b20fa98..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-<?php
-namespace TYPO3\CMS\Rsaauth\Hook;
-
-/*
- * 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!
- */
-use TYPO3\CMS\Backend\Controller\LoginController;
-use TYPO3\CMS\Backend\Template\DocumentTemplate;
-use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
-
-/**
- * This class provides a hook to the login form to add extra javascript code
- * and supply a proper form tag.
- *
- * @author Dmitry Dulepov <dmitry@typo3.org>
- * @author Helmut Hummel <helmut@typo3.org>
- */
-class LoginFormHook {
-
-       /**
-        * Adds RSA-specific JavaScript
-        *
-        * @param array $params
-        * @param LoginController $pObj
-        * @return string|NULL Dummy JS or NULL if security level is not rsa
-        */
-       public function getLoginFormJS(array $params, LoginController $pObj) {
-               if ($pObj->loginSecurityLevel !== 'rsa') {
-                       return NULL;
-               }
-               $javascriptPath = '../' . ExtensionManagementUtility::siteRelPath('rsaauth') . 'Resources/Public/JavaScript/';
-               $files = array(
-                       'jsbn/jsbn.js',
-                       'jsbn/prng4.js',
-                       'jsbn/rng.js',
-                       'jsbn/rsa.js',
-                       'jsbn/base64.js'
-               );
-
-               /** @var DocumentTemplate $doc */
-               $doc = $GLOBALS['TBE_TEMPLATE'];
-               $pageRenderer = $doc->getPageRenderer();
-               foreach ($files as $file) {
-                       $pageRenderer->addJsFile($javascriptPath . $file);
-               }
-
-               $pageRenderer->loadRequireJsModule('TYPO3/CMS/Rsaauth/BackendLoginFormRsaEncryption');
-
-               return '// no content';
-       }
-
-}
diff --git a/typo3/sysext/rsaauth/Classes/Slot/UsernamePasswordProviderSlot.php b/typo3/sysext/rsaauth/Classes/Slot/UsernamePasswordProviderSlot.php
new file mode 100644 (file)
index 0000000..e6b962d
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+namespace TYPO3\CMS\Rsaauth\Slot;
+
+/*
+ * 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!
+ */
+
+use TYPO3\CMS\Backend\Controller\LoginController;
+use TYPO3\CMS\Core\Page\PageRenderer;
+use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
+
+/**
+ * Class UsernamePasswordProviderSlot
+ */
+class UsernamePasswordProviderSlot {
+
+       /**
+        * @param PageRenderer $pageRenderer
+        */
+       public function getPageRenderer(PageRenderer $pageRenderer) {
+               $loginSecurityLevel = trim($GLOBALS['TYPO3_CONF_VARS']['BE']['loginSecurityLevel']) ?: 'normal';
+               if ($loginSecurityLevel === 'rsa') {
+                       $javascriptPath = '../' . ExtensionManagementUtility::siteRelPath('rsaauth') . 'Resources/Public/JavaScript/';
+                       $files = array(
+                               'jsbn/jsbn.js',
+                               'jsbn/prng4.js',
+                               'jsbn/rng.js',
+                               'jsbn/rsa.js',
+                               'jsbn/base64.js'
+                       );
+                       foreach ($files as $file) {
+                               $pageRenderer->addJsFile($javascriptPath . $file);
+                       }
+
+                       $pageRenderer->loadRequireJsModule('TYPO3/CMS/Rsaauth/BackendLoginFormRsaEncryption');
+               }
+       }
+}
index 4fd4a63..5b9b63c 100644 (file)
 /**
  * Object that handles RSA encryption and submission of the form
  */
-define('TYPO3/CMS/Rsaauth/BackendLoginFormRsaEncryption', ['jquery', 'TYPO3/CMS/Backend/Login'], function($, BackendLogin) {
+define(
+       'TYPO3/CMS/Rsaauth/BackendLoginFormRsaEncryption',
+       ['jquery', 'TYPO3/CMS/Backend/Login', 'TYPO3/CMS/Backend/UserPassLogin'],
+       function($, Login, UserPassLogin) {
 
        var RsaBackendLogin = {
 
@@ -37,11 +40,11 @@ define('TYPO3/CMS/Rsaauth/BackendLoginFormRsaEncryption', ['jquery', 'TYPO3/CMS/
                 * Replace event handler of submit button
                 */
                initialize: function() {
-                       this.userPasswordField = BackendLogin.options.passwordField;
-                       this.typo3PasswordField = BackendLogin.options.useridentField;
-                       this.loginForm = BackendLogin.options.loginForm;
+                       this.userPasswordField = UserPassLogin.options.passwordField;
+                       this.typo3PasswordField = Login.options.useridentField;
+                       this.loginForm = Login.options.loginForm;
 
-                       BackendLogin.options.submitHandler = this.handleFormSubmitRequest;
+                       Login.options.submitHandler = this.handleFormSubmitRequest;
                },
 
                /**
@@ -55,8 +58,6 @@ define('TYPO3/CMS/Rsaauth/BackendLoginFormRsaEncryption', ['jquery', 'TYPO3/CMS/
 
                                event.preventDefault();
 
-                               BackendLogin.showLoginProcess();
-
                                $.ajax({
                                        url: TYPO3.settings.ajaxUrls['BackendLogin::getRsaPublicKey'],
                                        data: {'skipSessionUpdate': 1},
index e5b66ab..d3ba763 100644 (file)
@@ -17,8 +17,6 @@ defined('TYPO3_MODE') or die();
        'className' => \TYPO3\CMS\Rsaauth\RsaAuthService::class
 ));
 
-// Add a hook to the BE login form
-$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/index.php']['loginScriptHook']['rsaauth'] = \TYPO3\CMS\Rsaauth\Hook\LoginFormHook::class . '->getLoginFormJS';
 // Add hook for user setup module
 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/setup/mod/index.php']['setupScriptHook']['rsaauth'] = \TYPO3\CMS\Rsaauth\Hook\UserSetupHook::class . '->getLoginScripts';
 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/setup/mod/index.php']['modifyUserDataBeforeSave']['rsaauth'] = \TYPO3\CMS\Rsaauth\Hook\UserSetupHook::class . '->decryptPassword';
@@ -37,3 +35,10 @@ $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['displ
 $GLOBALS['TYPO3_CONF_VARS']['FE']['eID_include']['FrontendLoginRsaPublicKey'] = 'EXT:rsaauth/Resources/PHP/FrontendLoginRsaPublicKey.php';
 
 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/backend.php']['constructPostProcess'][] = \TYPO3\CMS\Rsaauth\Hook\BackendHookForAjaxLogin::class . '->addRsaJsLibraries';
+
+\TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher::class)->connect(
+       \TYPO3\CMS\Backend\LoginProvider\UsernamePasswordLoginProvider::class,
+       \TYPO3\CMS\Backend\LoginProvider\UsernamePasswordLoginProvider::SIGNAL_getPageRenderer,
+       \TYPO3\CMS\Rsaauth\Slot\UsernamePasswordProviderSlot::class,
+       'getPageRenderer'
+);
index ae749c3..4866d3e 100644 (file)
@@ -57,8 +57,8 @@
 }
 .typo3-login-links {
        padding-top: inherit;
-       margin-top: 40px;
-       margin-bottom: 0;
+       margin-top: 20px;
+       margin-bottom: 20px;
        .disabled {
                cursor: not-allowed!important;
                text-decoration: none!important;
        > *:last-child {
                margin-bottom: 0;
        }
-}
\ No newline at end of file
+}
index 53a43ef..a570dfb 100644 (file)
@@ -11064,8 +11064,8 @@ a.badge-danger:hover {
 }
 .typo3-login-links {
   padding-top: inherit;
-  margin-top: 40px;
-  margin-bottom: 0;
+  margin-top: 20px;
+  margin-bottom: 20px;
 }
 .typo3-login-links .disabled {
   cursor: not-allowed !important;