From bd2e3d6e0c6022db8e204a91992858635995625a Mon Sep 17 00:00:00 2001 From: Clemente Raposo Date: Fri, 6 Mar 2020 12:59:10 +0000 Subject: [PATCH] Add session swapping to LegacyHandlers - Autowire LegacyHandlers as Symfony Services -- Configure legacy handlers folder in service.yml -- Replace handler instantiation with service injection - Add init and close to legacy handlers -- Swap sessions -- Swap current dir -- Add config to allow moving values between sessions -- replace hard coded values with injected configuration - Remove unused services and corresponding unit test -- NavbarService.php -- AuthenticationService.php - Refactor Authentication Handler to -- call init and close methods - Refactor navbar handler to be used as a service -- Adjust navbar unit tests --- composer.json | 1 - config/services.yaml | 11 ++ core/legacy/Authentication.php | 20 +-- core/legacy/AuthenticationService.php | 36 ----- core/legacy/LegacyHandler.php | 116 ++++++++++++-- core/legacy/Navbar.php | 134 ---------------- core/legacy/NavbarHandler.php | 146 ++++++++++++++++++ core/legacy/NavbarService.php | 34 ---- .../DataProvider/NavbarItemDataProvider.php | 45 ++++-- core/src/Entity/Navbar.php | 2 + core/src/Security/LoginFormAuthenticator.php | 18 ++- .../unit/legacy/AuthenticationServiceTest.php | 39 ----- tests/unit/legacy/NavbarServiceTest.php | 36 ----- tests/unit/legacy/NavbarTest.php | 25 ++- 14 files changed, 343 insertions(+), 320 deletions(-) delete mode 100644 core/legacy/AuthenticationService.php delete mode 100644 core/legacy/Navbar.php create mode 100644 core/legacy/NavbarHandler.php delete mode 100644 core/legacy/NavbarService.php delete mode 100644 tests/unit/legacy/AuthenticationServiceTest.php delete mode 100644 tests/unit/legacy/NavbarServiceTest.php diff --git a/composer.json b/composer.json index 5326be2fc..6b3455363 100755 --- a/composer.json +++ b/composer.json @@ -46,7 +46,6 @@ }, "autoload": { "psr-4": { - "SuiteCRM\\Core\\src\\": "core/src/", "SuiteCRM\\Core\\Legacy\\": "core/legacy/", "App\\": "core/src/", "App\\Entity\\": "core/src/Entity/", diff --git a/config/services.yaml b/config/services.yaml index 3cbbd22f1..3e56d7b0b 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -3,6 +3,9 @@ framework: parameters: secret: ThisTokenIsNotSoSecretChangeIt + legacy.dir: '%kernel.project_dir%/legacy' + legacy.session_name: 'LEGACYSESSID' + default_session_name: 'PHPSESSID' imports: - { resource: services/*/*.yaml } @@ -14,6 +17,9 @@ services: autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. bind: $projectDir: '%kernel.project_dir%' + $legacyDir: '%legacy.dir%' + $legacySessionName: '%legacy.session_name%' + $defaultSessionName: '%default_session_name%' # makes classes in src/ available to be used as services # this creates a service per class whose id is the fully-qualified class name @@ -21,6 +27,11 @@ services: resource: '../core/src/*' exclude: '../core/src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}' + # makes classes in legacy/ available to be used as services + # this creates a service per class whose id is the fully-qualified class name + SuiteCRM\Core\Legacy\: + resource: '../core/legacy/*' + # controllers are imported separately to make sure services can be injected # as action arguments even if you don't extend any base controller class App\Controller\: diff --git a/core/legacy/Authentication.php b/core/legacy/Authentication.php index 2ffa1256f..23a57ccc6 100644 --- a/core/legacy/Authentication.php +++ b/core/legacy/Authentication.php @@ -4,14 +4,14 @@ namespace SuiteCRM\Core\Legacy; use AuthenticationController; use Exception; -use RuntimeException; /** * Class Authentication - * @package SuiteCRM\Core\Legacy */ class Authentication extends LegacyHandler { + protected $config; + /** * Set the config * @@ -32,19 +32,21 @@ class Authentication extends LegacyHandler * @param $password * @param $grant_type * - * @return boolean + * @return bool * @throws Exception */ public function login($username, $password, $grant_type = 'password'): bool { - if ($this->runLegacyEntryPoint()) { - $authController = new AuthenticationController(); + $this->init(); - $PARAMS = []; + $authController = new AuthenticationController(); - return $authController->login($username, $password, $PARAMS); - } + $PARAMS = []; - throw new RuntimeException('Running legacy entry point failed'); + $result = $authController->login($username, $password, $PARAMS); + + $this->close(); + + return $result; } } diff --git a/core/legacy/AuthenticationService.php b/core/legacy/AuthenticationService.php deleted file mode 100644 index 4808d0c55..000000000 --- a/core/legacy/AuthenticationService.php +++ /dev/null @@ -1,36 +0,0 @@ -projectDir = $projectDir; + $this->legacyDir = $legacyDir; + $this->legacySessionName = $legacySessionName; + $this->defaultSessionName = $defaultSessionName; + } + + /** + * Legacy handler initialization method + */ + public function init(): void + { + // Set working directory for legacy + chdir($this->legacyDir); + + if (!$this->runLegacyEntryPoint()) { + throw new RuntimeException(self::MSG_LEGACY_BOOTSTRAP_FAILED); + } + + $this->switchSession($this->legacySessionName); + } + + /** + * Bootstraps legacy suite * @return bool */ public function runLegacyEntryPoint(): bool { - if (!defined('LEGACY_PATH')) { - define('LEGACY_PATH', dirname(__DIR__, 2) . '/legacy/'); + if (defined('IS_LEGACY_BOOTSTRAPPED') && IS_LEGACY_BOOTSTRAPPED) { + return true; } // Set up sugarEntry @@ -24,13 +80,55 @@ class LegacyHandler define('sugarEntry', true); } - // Set working directory for legacy - chdir(LEGACY_PATH); - // Load in legacy - require_once LEGACY_PATH . 'include/MVC/preDispatch.php'; - require_once LEGACY_PATH . 'include/entryPoint.php'; + require_once 'include/MVC/preDispatch.php'; + require_once 'include/entryPoint.php'; + + define('IS_LEGACY_BOOTSTRAPPED', true); return true; } + + /** + * Close the legacy handler + */ + public function close(): void + { + if (!empty($this->projectDir)) { + chdir($this->projectDir); + } + + $this->switchSession($this->defaultSessionName); + } + + + /** + * Swap symfony session with legacy suite session + * @param string $sessionName + * @param array $keysToSync + */ + protected function switchSession(string $sessionName, array $keysToSync = []) + { + $carryOver = []; + + foreach ($keysToSync as $key) { + if (!empty($_SESSION[$key])) { + $carryOver[$key] = $_SESSION[$key]; + } + } + + session_write_close(); + session_name($sessionName); + + if (!isset($_COOKIE[$sessionName])) { + $_COOKIE[$sessionName] = session_create_id(); + } + + session_id($_COOKIE[$sessionName]); + session_start(); + + foreach ($carryOver as $key => $value) { + $_SESSION[$key] = $value; + } + } } diff --git a/core/legacy/Navbar.php b/core/legacy/Navbar.php deleted file mode 100644 index ddff4bfab..000000000 --- a/core/legacy/Navbar.php +++ /dev/null @@ -1,134 +0,0 @@ -runLegacyEntryPoint()) { - require LEGACY_PATH . 'modules/MySettings/TabController.php'; - $tabArray = (new TabController())->get_user_tabs($GLOBALS['current_user']); - $tabArray = array_map('strtolower', $tabArray); - - - return $tabArray; - } - - throw new RuntimeException('Running legacy entry point failed'); - } - - public function getModuleSubMenus(): array { - if ($this->runLegacyEntryPoint()) { - ob_start(); - require_once LEGACY_PATH . 'modules/Home/sitemap.php'; - ob_end_clean(); - - return sm_build_array(); - } - } - - /** - * @return array - */ - public function getGroupedNavTabs(): array - { - if ($this->runLegacyEntryPoint()) { - global $current_language; - - require_once LEGACY_PATH . 'include/GroupedTabs/GroupedTabStructure.php'; - require_once LEGACY_PATH . 'modules/Studio/TabGroups/TabGroupHelper.php'; - - $tg = new TabGroupHelper(); - $selectedAppLanguages = return_application_language($current_language); - $availableModules = $tg->getAvailableModules($current_language); - $modList = array_keys($availableModules); - $modList = array_combine($modList, $modList); - $groupedTabStructure = (new GroupedTabStructure())->get_tab_structure($modList, '', true, true); - foreach ($groupedTabStructure as $mainTab => $subModules) { - $groupedTabStructure[$mainTab]['label'] = $mainTab; - $groupedTabStructure[$mainTab]['labelValue'] = strtolower($selectedAppLanguages[$mainTab]); - $submoduleArray = []; - - foreach ($subModules['modules'] as $submodule) { - $submoduleArray[] = strtolower($submodule); - } - - sort($submoduleArray); - - $output[] = [ - - 'name' => $groupedTabStructure[$mainTab]['labelValue'], - 'labelKey' => $mainTab, - 'modules' => array_values($submoduleArray) - - ]; - } - - return $output; - } - - throw new RuntimeException('Running legacy entry point failed'); - } - - /** - * @return array - */ - public function getUserActionMenu(): array - { - if ($this->runLegacyEntryPoint()) { - $userActionMenu = []; - $global_control_links = []; - - $userActionMenu[] = [ - 'name' => 'profile', - 'labelKey' => 'LBL_PROFILE', - 'url' => 'index.php?module=Users&action=EditView&record=1', - 'icon' => '', - ]; - - require LEGACY_PATH . 'include/globalControlLinks.php'; - - $labelKeys = [ - 'employees' => 'LBL_EMPLOYEES', - 'training' => 'LBL_TRAINING', - 'about' => 'LNK_ABOUT', - 'users' => 'LBL_LOGOUT', - ]; - - foreach ($global_control_links as $key => $value) { - foreach ($value as $linkAttribute => $attributeValue) { - // get the main link info - if ($linkAttribute === 'linkinfo') { - $userActionMenu[] = [ - 'name' => strtolower(key($attributeValue)), - 'labelKey' => $labelKeys[$key], - 'url' => current($attributeValue), - 'icon' => '', - ]; - } - } - } - - $item = $userActionMenu[3]; - unset($userActionMenu[3]); - $userActionMenu[] = $item; - - return array_values($userActionMenu); - } - - throw new RuntimeException('Running legacy entry point failed'); - } -} diff --git a/core/legacy/NavbarHandler.php b/core/legacy/NavbarHandler.php new file mode 100644 index 000000000..dcfe1fe83 --- /dev/null +++ b/core/legacy/NavbarHandler.php @@ -0,0 +1,146 @@ +init(); + + $navbar = new Navbar(); + + $navbar->NonGroupedTabs = $this->fetchNonGroupedNavTabs(); + $navbar->groupedTabs = $this->fetchGroupedNavTabs(); + $navbar->userActionMenu = $this->fetchUserActionMenu(); + $navbar->moduleSubmenus = $this->fetchModuleSubMenus(); + + $this->close(); + + return $navbar; + } + + /** + * Fetch module navigation tabs + * @return array + */ + protected function fetchNonGroupedNavTabs(): array + { + require_once 'modules/MySettings/TabController.php'; + $tabArray = (new TabController())->get_user_tabs($GLOBALS['current_user']); + $tabArray = array_map('strtolower', $tabArray); + + return $tabArray; + } + + /** + * Fetch the module submenus + * @return array + */ + protected function fetchModuleSubMenus(): array + { + ob_start(); + require_once 'modules/Home/sitemap.php'; + ob_end_clean(); + + return sm_build_array(); + } + + /** + * Fetch Grouped Navigation tabs + * @return array + */ + protected function fetchGroupedNavTabs(): array + { + $output = []; + global $current_language; + + require_once 'include/GroupedTabs/GroupedTabStructure.php'; + require_once 'modules/Studio/TabGroups/TabGroupHelper.php'; + + $tg = new TabGroupHelper(); + $selectedAppLanguages = return_application_language($current_language); + $availableModules = $tg->getAvailableModules($current_language); + $modList = array_keys($availableModules); + $modList = array_combine($modList, $modList); + $groupedTabStructure = (new GroupedTabStructure())->get_tab_structure($modList, '', true, true); + foreach ($groupedTabStructure as $mainTab => $subModules) { + $groupedTabStructure[$mainTab]['label'] = $mainTab; + $groupedTabStructure[$mainTab]['labelValue'] = strtolower($selectedAppLanguages[$mainTab]); + $submoduleArray = []; + + foreach ($subModules['modules'] as $submodule) { + $submoduleArray[] = strtolower($submodule); + } + + sort($submoduleArray); + + $output[] = [ + + 'name' => $groupedTabStructure[$mainTab]['labelValue'], + 'labelKey' => $mainTab, + 'modules' => array_values($submoduleArray) + + ]; + } + + return $output; + } + + /** + * Fetch the user action menu + */ + protected function fetchUserActionMenu(): array + { + $userActionMenu = []; + $global_control_links = []; + + $userActionMenu[] = [ + 'name' => 'profile', + 'labelKey' => 'LBL_PROFILE', + 'url' => 'index.php?module=Users&action=EditView&record=1', + 'icon' => '', + ]; + + require 'include/globalControlLinks.php'; + + $labelKeys = [ + 'employees' => 'LBL_EMPLOYEES', + 'training' => 'LBL_TRAINING', + 'about' => 'LNK_ABOUT', + 'users' => 'LBL_LOGOUT', + ]; + + foreach ($global_control_links as $key => $value) { + foreach ($value as $linkAttribute => $attributeValue) { + // get the main link info + if ($linkAttribute === 'linkinfo') { + $userActionMenu[] = [ + 'name' => strtolower(key($attributeValue)), + 'labelKey' => $labelKeys[$key], + 'url' => current($attributeValue), + 'icon' => '', + ]; + } + } + } + + $item = $userActionMenu[3]; + unset($userActionMenu[3]); + $userActionMenu[] = $item; + + return array_values($userActionMenu); + } +} diff --git a/core/legacy/NavbarService.php b/core/legacy/NavbarService.php deleted file mode 100644 index 4820ea598..000000000 --- a/core/legacy/NavbarService.php +++ /dev/null @@ -1,34 +0,0 @@ -navbarHandler = $navbarHandler; + } + + /** + * Define supported resources + * @param string $resourceClass + * @param string|null $operationName + * @param array $context + * @return bool + */ public function supports(string $resourceClass, string $operationName = null, array $context = []): bool { return Navbar::class === $resourceClass; } + /** + * Get navbar + * @param string $resourceClass + * @param array|int|string $id + * @param string|null $operationName + * @param array $context + * @return Navbar|null + */ public function getItem(string $resourceClass, $id, string $operationName = null, array $context = []): ?Navbar { - $navbarData = new LegacyNavbar(); - $output = new Navbar(); + $navbar = $this->navbarHandler->getNavbar(); // This should be updated once we have authentication. - $output->userID = 1; - $output->NonGroupedTabs = $navbarData->getNonGroupedNavTabs(); - $output->groupedTabs = $navbarData->getGroupedNavTabs(); - $output->userActionMenu = $navbarData->getUserActionMenu(); - $output->moduleSubmenus = $navbarData->getModuleSubMenus(); - return $output; + $navbar->userID = 1; + + return $navbar; } } diff --git a/core/src/Entity/Navbar.php b/core/src/Entity/Navbar.php index 8138571e6..8bc211dcb 100644 --- a/core/src/Entity/Navbar.php +++ b/core/src/Entity/Navbar.php @@ -10,6 +10,8 @@ use ApiPlatform\Core\Annotation\ApiProperty; * itemOperations={ * "get" * }, + * collectionOperations={ + * } * ) */ final class Navbar diff --git a/core/src/Security/LoginFormAuthenticator.php b/core/src/Security/LoginFormAuthenticator.php index c5311fa76..8b2d2442a 100644 --- a/core/src/Security/LoginFormAuthenticator.php +++ b/core/src/Security/LoginFormAuthenticator.php @@ -3,6 +3,7 @@ namespace App\Security; use Doctrine\ORM\EntityManagerInterface; +use Exception; use SuiteCRM\Core\Legacy\Authentication; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\RedirectResponse; @@ -31,8 +32,18 @@ class LoginFormAuthenticator extends AbstractFormLoginAuthenticator implements P private $router; private $csrfTokenManager; private $passwordEncoder; + private $authentication; + /** + * LoginFormAuthenticator constructor. + * @param Authentication $authentication + * @param EntityManagerInterface $entityManager + * @param RouterInterface $router + * @param CsrfTokenManagerInterface $csrfTokenManager + * @param UserPasswordEncoderInterface $passwordEncoder + */ public function __construct( + Authentication $authentication, EntityManagerInterface $entityManager, RouterInterface $router, CsrfTokenManagerInterface $csrfTokenManager, @@ -42,6 +53,7 @@ class LoginFormAuthenticator extends AbstractFormLoginAuthenticator implements P $this->router = $router; $this->csrfTokenManager = $csrfTokenManager; $this->passwordEncoder = $passwordEncoder; + $this->authentication = $authentication; } /** @@ -91,10 +103,9 @@ class LoginFormAuthenticator extends AbstractFormLoginAuthenticator implements P throw new CustomUserMessageAuthenticationException('Username could not be found.'); } - $authentication = new Authentication(); try { - $authentication->login($credentials['username'], $credentials['password']); - } catch (\Exception $e) { + $this->authentication->login($credentials['username'], $credentials['password']); + } catch (Exception $e) { throw new CustomUserMessageAuthenticationException('Legacy username could not be found.'); } @@ -110,7 +121,6 @@ class LoginFormAuthenticator extends AbstractFormLoginAuthenticator implements P { // TODO: Password validation return true; - // TODO: Password hash upgrading } diff --git a/tests/unit/legacy/AuthenticationServiceTest.php b/tests/unit/legacy/AuthenticationServiceTest.php deleted file mode 100644 index f966652f3..000000000 --- a/tests/unit/legacy/AuthenticationServiceTest.php +++ /dev/null @@ -1,39 +0,0 @@ -authenticationService = new AuthenticationService(); - } - - public function testGetName(): void - { - $this->assertSame('users.authentication', $this->authenticationService->getName()); - } - - public function testGetDescription(): void - { - $this->assertSame( - 'This service will deal with legacy authentication', - $this->authenticationService->getDescription() - ); - } - - public function testCreateService(): void - { - $this->assertInstanceOf(Authentication::class, $this->authenticationService->createService()); - } -} diff --git a/tests/unit/legacy/NavbarServiceTest.php b/tests/unit/legacy/NavbarServiceTest.php deleted file mode 100644 index 64ba96843..000000000 --- a/tests/unit/legacy/NavbarServiceTest.php +++ /dev/null @@ -1,36 +0,0 @@ -navbarService = new NavbarService(); - } - - public function testGetName(): void - { - $this->assertSame('template.navbar', $this->navbarService->getName()); - } - - public function testGetDescription(): void - { - $this->assertSame('This service will deal with retrieval of the navbar structure', - $this->navbarService->getDescription()); - } - - public function testCreateService(): void - { - $this->assertInstanceOf(Navbar::class, $this->navbarService->createService()); - } -} diff --git a/tests/unit/legacy/NavbarTest.php b/tests/unit/legacy/NavbarTest.php index 86086f6d4..4b31457c1 100644 --- a/tests/unit/legacy/NavbarTest.php +++ b/tests/unit/legacy/NavbarTest.php @@ -2,19 +2,30 @@ declare(strict_types=1); +use App\Entity\Navbar; use Codeception\Test\Unit; -use SuiteCRM\Core\Legacy\Navbar; +use SuiteCRM\Core\Legacy\NavbarHandler; final class NavbarTest extends Unit { + /** + * @var NavbarHandler + */ + private $navbarHandler; + /** * @var Navbar */ - private $navbar; + protected $navbar; protected function _before() { - $this->navbar = new Navbar(); + $projectDir = codecept_root_dir(); + $legacyDir = $projectDir . '/legacy'; + $legacySessionName = 'LEGACYSESSID'; + $defaultSessionName = 'PHPSESSID'; + $this->navbarHandler = new NavbarHandler($projectDir, $legacyDir, $legacySessionName, $defaultSessionName); + $this->navbar = $this->navbarHandler->getNavbar(); } public function testGetUserActionMenu(): void @@ -55,7 +66,7 @@ final class NavbarTest extends Unit $this->assertSame( $expected, - $this->navbar->getUserActionMenu() + $this->navbar->userActionMenu ); } @@ -103,7 +114,7 @@ final class NavbarTest extends Unit $this->assertSame( $expected, - $this->navbar->getNonGroupedNavTabs() + $this->navbar->NonGroupedTabs ); } @@ -177,7 +188,7 @@ final class NavbarTest extends Unit $this->assertSame( $expected, - $this->navbar->getGroupedNavTabs() + $this->navbar->groupedTabs ); } @@ -371,7 +382,7 @@ final class NavbarTest extends Unit $this->assertSame( $expected, - $this->navbar->getModuleSubMenus() + $this->navbar->moduleSubmenus ); } }