Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: add Shopmon ACL role #251

Closed
wants to merge 3 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 168 additions & 0 deletions src/Command/ShopmonCreateAclRole.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
<?php

Check warning on line 1 in src/Command/ShopmonCreateAclRole.php

View workflow job for this annotation

GitHub Actions / cs / Check Style

Found violation(s) of type: braces_position

declare(strict_types=1);

namespace Frosh\Tools\Command;

use Doctrine\DBAL\Connection;
use Shopware\Core\Framework\Api\Acl\Role\AclRoleCollection;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\Doctrine\RetryableQuery;
use Shopware\Core\Framework\DataAbstractionLayer\EntityCollection;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Write\WriteException;
use Shopware\Core\Framework\Uuid\Uuid;
use Shopware\Core\Framework\Validation\WriteConstraintViolationException;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

#[AsCommand('frosh:shopmon:create-acl-role', 'Create Shopmon ACL role')]
class ShopmonCreateAclRole extends Command
{
private const SHOPMON_ACL_ROLE_NAME = 'shopmon_acl_role';
private const SHOPMON_ACL_ROLE_DESCRIPTION = 'This role has the necessary permissions to use Shopmon';
private const SHOPMON_ACL_ROLE_ID = '018dd6ae4c4072b1b5887fe8d3b9b95a';

/**
* @param EntityRepository<AclRoleCollection> $aclRoleRepository
* @param EntityRepository<EntityCollection> $userRepository
* @param Connection $connection
*/
public function __construct(

Check failure on line 37 in src/Command/ShopmonCreateAclRole.php

View workflow job for this annotation

GitHub Actions / phpstan / Static Analyse

Method Frosh\Tools\Command\ShopmonCreateAclRole::__construct() has parameter $userRepository with generic class Shopware\Core\Framework\DataAbstractionLayer\EntityCollection but does not specify its types: TElement
private readonly EntityRepository $aclRoleRepository,
private readonly EntityRepository $userRepository,
private readonly Connection $connection
)
{
parent::__construct();
}

protected function configure(): void
{
$this->addOption('username', null, InputOption::VALUE_OPTIONAL, 'User that will be assigned the role');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why a user, we should only create a integration? 🤔

Copy link
Member Author

@MelvinAchterhuis MelvinAchterhuis Feb 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

user:create command in core doesn't support ACL/roles

so in a CI/CD setup first create a shopmon user with user:create and create+assign the role with this command

}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);

if ($this->roleExists()) {
$io->warning(sprintf('ACL role with the name `%s` already exists', self::SHOPMON_ACL_ROLE_NAME));
return Command::SUCCESS;
}

$userId = null;
if ($input->getOption('username') !== null) {
$userId = $this->userExists($input->getOption('username'));
if (!$userId) {
$io->error(sprintf('User with the username `%s` does not exist', $input->getOption('username')));
return Command::FAILURE;
}
}

try {
$this->createRole();
} catch (WriteException $exception) {
$io->error('Something went wrong.');
$messages = $this->createWriteExceptionMessages($exception);
$io->listing($messages);
return Command::FAILURE;
}
$io->success(sprintf('ACL role with the name `%s` has been created', self::SHOPMON_ACL_ROLE_NAME));

if ($userId === null) {
return Command::SUCCESS;
}

try {
$this->assignRoleToUser($userId);
} catch (\Exception $e) {
$io->error($e->getMessage());
return Command::FAILURE;
}
$io->success(sprintf('ACL role been assigned to the user with the username `%s`', $input->getOption('username')));

return Command::SUCCESS;
}

private function roleExists(): bool
{
$context = Context::createDefaultContext();
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('name', self::SHOPMON_ACL_ROLE_NAME));
$criteria->setLimit(1);

return $this->aclRoleRepository->search($criteria, $context)->getTotal() > 0;
}

private function userExists(string $username): ?string
{
$context = Context::createDefaultContext();
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('username', $username));
$criteria->setLimit(1);

return $this->userRepository->searchIds($criteria, $context)->firstId();
}

private function createRole(): void
{
$aclPermissions = [
'app:read',
'product:read',
'product:write',
'system_config:read',
'scheduled_task:read',
'frosh_tools:read',
'system:clear:cache',
'system:cache:info'
];

$aclRole = [
'id' => self::SHOPMON_ACL_ROLE_ID,
'name' => self::SHOPMON_ACL_ROLE_NAME,
'description' => self::SHOPMON_ACL_ROLE_DESCRIPTION,
'privileges' => $aclPermissions,
];

$context = Context::createDefaultContext();
$this->aclRoleRepository->create([$aclRole], $context);
}

private function assignRoleToUser(string $userId): void
{
$sql = <<<'SQL'
INSERT INTO `acl_user_role` (`user_id`, `acl_role_id`, `created_at`)
VALUES (:user_id, :acl_role_id, :created_at)
SQL;

$data = [
'user_id' => Uuid::fromHexToBytes($userId),
'acl_role_id' => Uuid::fromHexToBytes(self::SHOPMON_ACL_ROLE_ID),
'created_at' => (new \DateTime())->format('Y-m-d H:i:s')
];

$query = new RetryableQuery($this->connection, $this->connection->prepare($sql));
$query->execute($data);
}

private function createWriteExceptionMessages(WriteException $exception): array

Check failure on line 155 in src/Command/ShopmonCreateAclRole.php

View workflow job for this annotation

GitHub Actions / phpstan / Static Analyse

Method Frosh\Tools\Command\ShopmonCreateAclRole::createWriteExceptionMessages() return type has no value type specified in iterable type array.
{
$messages = [];
foreach ($exception->getExceptions() as $err) {
if ($err instanceof WriteConstraintViolationException) {
foreach ($err->getViolations() as $violation) {
$messages[] = $violation->getPropertyPath() . ': ' . $violation->getMessage();
}
}
}

return $messages;
}
}
Loading