From 9101912bc25164ae6c16cfbdc1257ec64c0b9041 Mon Sep 17 00:00:00 2001 From: Stepan Furman Date: Sun, 14 Jun 2020 20:57:06 +0200 Subject: [PATCH] 2 command to work with admin users and its roles --- .../Command/AdminRolesListCommand.php | 72 ++++++++ .../Authorization/Model/GetAdminRolesList.php | 45 +++++ .../Command/AdminRolesListCommandTest.php | 33 ++++ .../Test/Integration/_files/test_role.php | 30 ++++ app/code/Magento/Authorization/etc/di.xml | 10 +- .../User/Console/AssignAdminToRoleCommand.php | 162 ++++++++++++++++++ .../Magento/User/Model/AssignUserToRole.php | 151 ++++++++++++++++ .../Magento/User/Model/GetAdminUserList.php | 43 +++++ .../Command/AssignAdminToRoleCommandTest.php | 129 ++++++++++++++ .../Test/Integration/_files/test_user.php | 36 ++++ app/code/Magento/User/etc/di.xml | 8 +- 11 files changed, 716 insertions(+), 3 deletions(-) create mode 100644 app/code/Magento/Authorization/Command/AdminRolesListCommand.php create mode 100644 app/code/Magento/Authorization/Model/GetAdminRolesList.php create mode 100644 app/code/Magento/Authorization/Test/Integration/Console/Command/AdminRolesListCommandTest.php create mode 100644 app/code/Magento/Authorization/Test/Integration/_files/test_role.php create mode 100644 app/code/Magento/User/Console/AssignAdminToRoleCommand.php create mode 100644 app/code/Magento/User/Model/AssignUserToRole.php create mode 100644 app/code/Magento/User/Model/GetAdminUserList.php create mode 100644 app/code/Magento/User/Test/Integration/Console/Command/AssignAdminToRoleCommandTest.php create mode 100644 app/code/Magento/User/Test/Integration/_files/test_user.php diff --git a/app/code/Magento/Authorization/Command/AdminRolesListCommand.php b/app/code/Magento/Authorization/Command/AdminRolesListCommand.php new file mode 100644 index 0000000000000..26c632c8cd543 --- /dev/null +++ b/app/code/Magento/Authorization/Command/AdminRolesListCommand.php @@ -0,0 +1,72 @@ +getAdminRolesListService = $getAdminRolesListService; + $this->tableFactory = $tableFactory; + } + + /** + * @inheritDoc + */ + protected function configure(): void + { + $this->setName("admin:role:list"); + $this->setDescription("Shows all existing admin role ids and names"); + parent::configure(); + } + + /** + * Provides table with admin roles + * + * @param InputInterface $input + * @param OutputInterface $output + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + protected function execute(InputInterface $input, OutputInterface $output): void + { + $output->writeln('Existing admin roles:'); + $table = $this->tableFactory->create(['output' => $output]); + $table->setHeaders(['Role ID', 'Role Name']); + $table->setRows($this->getAdminRolesListService->execute()); + $table->render(); + } +} diff --git a/app/code/Magento/Authorization/Model/GetAdminRolesList.php b/app/code/Magento/Authorization/Model/GetAdminRolesList.php new file mode 100644 index 0000000000000..5b2eb0656b334 --- /dev/null +++ b/app/code/Magento/Authorization/Model/GetAdminRolesList.php @@ -0,0 +1,45 @@ +rolesGridCollectionFactory = $rolesGridCollectionFactory; + } + + /** + * Provides information about all existing group roles + * + * @return array + */ + public function execute(): array + { + return $this->rolesGridCollectionFactory->create() + ->addFieldToSelect('role_id') + ->addFieldToSelect('role_name') + ->load() + ->toArray()['items']; + } +} diff --git a/app/code/Magento/Authorization/Test/Integration/Console/Command/AdminRolesListCommandTest.php b/app/code/Magento/Authorization/Test/Integration/Console/Command/AdminRolesListCommandTest.php new file mode 100644 index 0000000000000..7fac27d18c4ba --- /dev/null +++ b/app/code/Magento/Authorization/Test/Integration/Console/Command/AdminRolesListCommandTest.php @@ -0,0 +1,33 @@ +get(AdminRolesListCommand::class); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + $this->assertStringContainsString('Role ID ', $commandTester->getDisplay()); + $this->assertStringContainsString('Role Name', $commandTester->getDisplay()); + $this->assertStringContainsString('test_role', $commandTester->getDisplay()); + } +} diff --git a/app/code/Magento/Authorization/Test/Integration/_files/test_role.php b/app/code/Magento/Authorization/Test/Integration/_files/test_role.php new file mode 100644 index 0000000000000..01694b525abba --- /dev/null +++ b/app/code/Magento/Authorization/Test/Integration/_files/test_role.php @@ -0,0 +1,30 @@ +loadArea(FrontNameResolver::AREA_CODE); +$objectManager = Bootstrap::getObjectManager(); +/** @var RoleResource $roleResource */ +$roleResource = $objectManager->get(RoleResource::class); +/** @var RoleFactory $roleFactory */ +$roleFactory = $objectManager->get(RoleFactory::class); +$role = $roleFactory->create(); +$role->setName('test_role') + ->setPid(0) + ->setRoleType(RoleGroup::ROLE_TYPE) + ->setUserType(UserContextInterface::USER_TYPE_ADMIN); + +$roleResource->save($role); +$ruleFactory = $objectManager->get(RulesFactory::class); +$ruleFactory->create()->setRoleId($role->getId())->setResources(['Magento_Backend::all'])->saveRel(); diff --git a/app/code/Magento/Authorization/etc/di.xml b/app/code/Magento/Authorization/etc/di.xml index 21420922ef596..ad37ea7797da0 100644 --- a/app/code/Magento/Authorization/etc/di.xml +++ b/app/code/Magento/Authorization/etc/di.xml @@ -5,7 +5,8 @@ * See COPYING.txt for license details. */ --> - + @@ -24,4 +25,11 @@ + + + + Magento\Authorization\Command\AdminRolesListCommand + + + diff --git a/app/code/Magento/User/Console/AssignAdminToRoleCommand.php b/app/code/Magento/User/Console/AssignAdminToRoleCommand.php new file mode 100644 index 0000000000000..f575bccfc667b --- /dev/null +++ b/app/code/Magento/User/Console/AssignAdminToRoleCommand.php @@ -0,0 +1,162 @@ +assignUserToRoleService = $assignUserToRoleService; + $this->getAdminRolesListService = $getAdminRolesListService; + $this->getAdminUserListService = $getAdminUserListService; + } + + /** + * @inheritDoc + */ + protected function configure(): void + { + $this->setName("admin:user:assign-role"); + $this->setDescription("Assigns user to role"); + $this->setDefinition($this->getOptionsList()); + + parent::configure(); + } + + /** + * Assigns role to user + * + * @param InputInterface $input + * @param OutputInterface $output + * + * @return void + * @throws AlreadyExistsException + */ + protected function execute(InputInterface $input, OutputInterface $output): void + { + $this->assignUserToRoleService->execute( + $input->getOption(self::USER_NAME_OPTION), + (int)$input->getOption(self::ROLE_ID_OPTION) + ); + $output->writeln("User assigned to the role."); + } + + /** + * @inheritDoc + */ + protected function interact(InputInterface $input, OutputInterface $output) + { + /** @var QuestionHelper $questionHelper */ + $questionHelper = $this->getHelper('question'); + + if (!$input->getOption(self::USER_NAME_OPTION)) { + $question = new Question('Admin username: ', ''); + $userList = $this->getAdminUserListService->execute(); + $question->setValidator(function ($value) use ($userList) { + /** @var User $user */ + foreach ($userList as $user) { + if ($user->getUserName() === $value) { + return $value; + } + } + throw new InvalidArgumentException(sprintf(AssignUserToRole::USER_DOES_NOT_EXISTS_ERROR_MSG, $value)); + }); + + $input->setOption( + self::USER_NAME_OPTION, + $questionHelper->ask($input, $output, $question) + ); + } + + if (!$input->getOption(self::ROLE_ID_OPTION)) { + $question = new Question('Role ID: ', ''); + $rolesList = $this->getAdminRolesListService->execute(); + $question->setValidator(function ($value) use ($rolesList) { + foreach ($rolesList as $role) { + if ($role['role_id'] === $value) { + return $value; + } + } + throw new InvalidArgumentException(sprintf(AssignUserToRole::ROLE_DOES_NOT_EXISTS_ERROR_MSG, $value)); + }); + + $input->setOption( + self::ROLE_ID_OPTION, + $questionHelper->ask($input, $output, $question) + ); + } + } + + /** + * Get list of arguments for the command + * + * @return InputOption[] + */ + public function getOptionsList(): array + { + return [ + new InputOption( + self::USER_NAME_OPTION, + null, + InputOption::VALUE_REQUIRED, + '(Required) Admin Username' + ), + new InputOption( + self::ROLE_ID_OPTION, + null, + InputOption::VALUE_REQUIRED, + '(Required) Role ID' + ) + ]; + } +} diff --git a/app/code/Magento/User/Model/AssignUserToRole.php b/app/code/Magento/User/Model/AssignUserToRole.php new file mode 100644 index 0000000000000..4c498b14875b8 --- /dev/null +++ b/app/code/Magento/User/Model/AssignUserToRole.php @@ -0,0 +1,151 @@ +roleFactory = $roleFactory; + $this->userResource = $userResource; + $this->roleResource = $roleResource; + $this->userFactory = $userFactory; + } + + /** + * Assigns user to role + * + * @param string $userName + * @param int $roleId + * + * @throws AlreadyExistsException + * @throws Exception + */ + public function execute(string $userName, int $roleId): void + { + $user = $this->userFactory->create(); + $this->userResource->load($user, $userName, 'username'); + $this->validateUserExists($userName, $user); + $this->validateParentRoleExists($roleId); + + $userRole = $this->roleFactory->create(); + $this->roleResource->load($userRole, $user->getId(), 'user_id'); + $this->validateUserIsNotAssignedToRole($roleId, $userRole, $user); + $user->setRoleId($roleId); + $this->userResource->save($user); + } + + /** + * Checks that user exists + * + * @param string $userName + * @param User $user + * + * @return void + * @throws Exception + */ + private function validateUserExists(string $userName, User $user): void + { + if (!$user->getId()) { + throw new InvalidArgumentException( + sprintf(self::USER_DOES_NOT_EXISTS_ERROR_MSG, $userName) + ); + } + } + + /** + * Check that Role exists + * + * @param int $roleId + * @throws Exception + */ + private function validateParentRoleExists(int $roleId): void + { + $role = $this->roleFactory->create(); + $this->roleResource->load($role, $roleId); + if (!$role || !$this->isGroupTypeRole($role)) { + throw new InvalidArgumentException( + sprintf(self::ROLE_DOES_NOT_EXISTS_ERROR_MSG, $roleId) + ); + } + } + + /** + * Checks that role is group role + * + * @param Role $role + * + * @return bool + */ + private function isGroupTypeRole(Role $role): bool + { + return $role->getRoleType() === Group::ROLE_TYPE; + } + + /** + * Check user is not assigned to given role + * + * @param int $parentRoleId + * @param Role $role + * @param User $user + * @throws Exception + */ + private function validateUserIsNotAssignedToRole(int $parentRoleId, Role $role, User $user): void + { + if ((int)$role->getParentId() === $parentRoleId && (int)$role->getUserId() === (int)$user->getId()) { + throw new InvalidArgumentException( + sprintf(self::USER_ALREADY_ASSIGNED_ERROR_MSG, $user->getUsername(), $parentRoleId) + ); + } + } +} diff --git a/app/code/Magento/User/Model/GetAdminUserList.php b/app/code/Magento/User/Model/GetAdminUserList.php new file mode 100644 index 0000000000000..c046dd3cff1dc --- /dev/null +++ b/app/code/Magento/User/Model/GetAdminUserList.php @@ -0,0 +1,43 @@ +userCollectionFactory = $userCollectionFactory; + } + + /** + * Provides admin users list + * + * @return Collection + */ + public function execute(): Collection + { + return $this->userCollectionFactory->create()->addFieldToSelect('username')->load(); + } +} diff --git a/app/code/Magento/User/Test/Integration/Console/Command/AssignAdminToRoleCommandTest.php b/app/code/Magento/User/Test/Integration/Console/Command/AssignAdminToRoleCommandTest.php new file mode 100644 index 0000000000000..3d40185873759 --- /dev/null +++ b/app/code/Magento/User/Test/Integration/Console/Command/AssignAdminToRoleCommandTest.php @@ -0,0 +1,129 @@ +objectManager = Bootstrap::getObjectManager(); + /** @var AssignAdminToRoleCommand $command */ + $command = $this->objectManager->get(AssignAdminToRoleCommand::class); + $helperSet = new HelperSet([new QuestionHelper()]); + $command->setHelperSet($helperSet); + $this->commandTester = new CommandTester($command); + } + + /** + * @magentoDataFixture ./../../../../app/code/Magento/User/Test/Integration/_files/test_user.php + * @magentoDataFixture ./../../../../app/code/Magento/Authorization/Test/Integration/_files/test_role.php + */ + public function testCommandAssignedExistingUserToExistingRole(): void + { + $testUser = $this->getTestUser(); + $role = $this->getTestRole(); + $this->commandTester->execute( + [self::USER_NAME_OPTION => 'test_user', self::ROLE_ID_OPTION => $role->getId()], + ['interactive' => false] + ); + $this->assertSame((int)$role->getId(), (int)$testUser->getAclRole()); + } + + /** + * @magentoDataFixture ./../../../../app/code/Magento/User/Test/Integration/_files/test_user.php + */ + public function testCommandAssignedExistingUserToNonExistingRole(): void + { + $nonExistingRoleId = '999999'; + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage( + sprintf(AssignUserToRole::ROLE_DOES_NOT_EXISTS_ERROR_MSG, $nonExistingRoleId) + ); + $this->commandTester->execute( + [self::USER_NAME_OPTION => 'test_user', self::ROLE_ID_OPTION => $nonExistingRoleId], + ['interactive' => false] + ); + } + + /** + * @magentoDataFixture ./../../../../app/code/Magento/Authorization/Test/Integration/_files/test_role.php + */ + public function testCommandAssignedNonExistingUserToExistingRole(): void + { + $role = $this->getTestRole(); + $this->expectException(InvalidArgumentException::class); + $nonExistingUserName = 'non_existing_user'; + $this->expectExceptionMessage( + sprintf(AssignUserToRole::USER_DOES_NOT_EXISTS_ERROR_MSG, $nonExistingUserName) + ); + $this->commandTester->execute( + [self::USER_NAME_OPTION => $nonExistingUserName, self::ROLE_ID_OPTION => $role->getId()], + ['interactive' => false] + ); + } + + /** + * @return User + */ + private function getTestUser(): User + { + $userResource = $this->objectManager->get(UserResource::class); + $testUser = $this->objectManager->get(UserFactory::class)->create(); + $userResource->load($testUser, 'test_user', 'username'); + + return $testUser; + } + + /** + * @return Role + */ + private function getTestRole(): Role + { + $roleResource = $this->objectManager->get(RoleResource::class); + $roleFactory = $this->objectManager->get(RoleFactory::class); + $role = $roleFactory->create(); + $roleResource->load($role, 'test_role', 'role_name'); + + return $role; + } +} diff --git a/app/code/Magento/User/Test/Integration/_files/test_user.php b/app/code/Magento/User/Test/Integration/_files/test_user.php new file mode 100644 index 0000000000000..956070796c32e --- /dev/null +++ b/app/code/Magento/User/Test/Integration/_files/test_user.php @@ -0,0 +1,36 @@ +loadArea(FrontNameResolver::AREA_CODE); +$objectManager = Bootstrap::getObjectManager(); +/** @var UserFactory $userFactory */ +$userFactory = $objectManager->create(UserFactory::class); +/** @var UserResource $userResource */ +$userResource = $objectManager->create(UserResource::class); +$user = $userFactory->create(['data' => [ + "username" => "test_user", + "firstname" => "Test", + "lastname" => "Dev", + "email" => "de@de.de", + "password" => "admin123", + "password_confirmation" => "admin123", + "interface_locale" => "en_US", + "is_active" => 1, + "limit" => "20", + "page" => "1", + "assigned_user_role" => "", + "role_name" => "", + "extra" => null, +]]); +$user->setHasDataChanges(true); +$userResource->save($user); +$user->getId(); diff --git a/app/code/Magento/User/etc/di.xml b/app/code/Magento/User/etc/di.xml index 8fc85bc19e59a..96d20cf8b1001 100644 --- a/app/code/Magento/User/etc/di.xml +++ b/app/code/Magento/User/etc/di.xml @@ -5,7 +5,8 @@ * See COPYING.txt for license details. */ --> - + @@ -13,7 +14,10 @@ - Magento\User\Console\UnlockAdminAccountCommand + Magento\User\Console\UnlockAdminAccountCommand + Magento\User\Console\AssignAdminToRoleCommand