Completed
Push — 3.x ( 1cd1d0...b03505 )
by Grégoire
16:27 queued 03:11
created

GenerateObjectAclCommand::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 3
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Sonata Project package.
7
 *
8
 * (c) Thomas Rabaix <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Sonata\AdminBundle\Command;
15
16
use Sonata\AdminBundle\Admin\AdminInterface;
17
use Sonata\AdminBundle\Admin\Pool;
18
use Sonata\AdminBundle\Util\ObjectAclManipulatorInterface;
19
use Symfony\Bridge\Doctrine\RegistryInterface;
20
use Symfony\Component\Console\Input\InputInterface;
21
use Symfony\Component\Console\Input\InputOption;
22
use Symfony\Component\Console\Output\OutputInterface;
23
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
24
use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity;
25
26
/**
27
 * @author Thomas Rabaix <[email protected]>
28
 */
29
class GenerateObjectAclCommand extends QuestionableCommand
30
{
31
    /**
32
     * {@inheritdoc}
33
     */
34
    protected static $defaultName = 'sonata:admin:generate-object-acl';
35
36
    /**
37
     * @var string
38
     */
39
    protected $userEntityClass = '';
40
41
    /**
42
     * @var Pool
43
     */
44
    private $pool;
45
46
    /**
47
     * An array of object ACL manipulators indexed by their service ids.
48
     *
49
     * @var ObjectAclManipulatorInterface[]
50
     */
51
    private $aclObjectManipulators = [];
52
53
    /**
54
     * @var RegistryInterface
55
     */
56
    private $registry;
57
58
    public function __construct(Pool $pool, array $aclObjectManipulators, RegistryInterface $registry = null)
59
    {
60
        $this->pool = $pool;
61
        $this->aclObjectManipulators = $aclObjectManipulators;
62
        $this->registry = $registry;
63
64
        parent::__construct();
65
    }
66
67
    public function configure()
68
    {
69
        $this
70
            ->setDescription('Install ACL for the objects of the Admin Classes.')
71
            ->addOption('object_owner', null, InputOption::VALUE_OPTIONAL, 'If set, the task will set the object owner for each admin.')
72
            ->addOption('user_entity', null, InputOption::VALUE_OPTIONAL, 'Shortcut notation like <comment>AcmeDemoBundle:User</comment>. If not set, it will be asked the first time an object owner is set.')
73
            ->addOption('step', null, InputOption::VALUE_NONE, 'If set, the task will ask for each admin if the ACLs need to be generated and what object owner to set, if any.')
74
        ;
75
    }
76
77
    public function execute(InputInterface $input, OutputInterface $output)
78
    {
79
        $output->writeln('Welcome to the AdminBundle object ACL generator');
80
        $output->writeln([
81
                '',
82
                'This command helps you to generate ACL entities for the objects handled by the AdminBundle.',
83
                '',
84
                'If the step option is used, you will be asked if you want to generate the object ACL entities for each Admin.',
85
                'You must use the shortcut notation like <comment>AcmeDemoBundle:User</comment> if you want to set an object owner.',
86
                '',
87
        ]);
88
89
        if (!$this->registry) {
90
            $msg = sprintf('The command "%s" has a dependency on a non-existent service "doctrine".', static::$defaultName);
91
92
            throw new ServiceNotFoundException('doctrine', static::class, null, [], $msg);
93
        }
94
95
        if ($input->getOption('user_entity')) {
96
            try {
97
                $this->getUserEntityClass($input, $output);
98
            } catch (\Exception $e) {
99
                $output->writeln(sprintf('<error>%s</error>', $e->getMessage()));
100
101
                return;
102
            }
103
        }
104
105
        foreach ($this->pool->getAdminServiceIds() as $id) {
106
            try {
107
                $admin = $this->pool->getInstance($id);
108
            } catch (\Exception $e) {
109
                $output->writeln('<error>Warning : The admin class cannot be initiated from the command line</error>');
110
                $output->writeln(sprintf('<error>%s</error>', $e->getMessage()));
111
112
                continue;
113
            }
114
115
            if ($input->getOption('step') && !$this->askConfirmation($input, $output, sprintf("<question>Generate ACLs for the object instances handled by \"%s\"?</question>\n", $id), 'no', '?')) {
116
                continue;
117
            }
118
119
            $securityIdentity = null;
120
            if ($input->getOption('step') && $this->askConfirmation($input, $output, "<question>Set an object owner?</question>\n", 'no', '?')) {
121
                $username = $this->askAndValidate($input, $output, 'Please enter the username: ', '', 'Sonata\AdminBundle\Command\Validators::validateUsername');
122
123
                $securityIdentity = new UserSecurityIdentity($username, $this->getUserEntityClass($input, $output));
124
            }
125
            if (!$input->getOption('step') && $input->getOption('object_owner')) {
126
                $securityIdentity = new UserSecurityIdentity($input->getOption('object_owner'), $this->getUserEntityClass($input, $output));
127
            }
128
129
            $manipulatorId = sprintf('sonata.admin.manipulator.acl.object.%s', $admin->getManagerType());
130
            if ($manipulator = $this->aclObjectManipulators[$manipulatorId] ?? null) {
131
                $output->writeln('Admin class is using a manager type that has no manipulator implemented : <info>ignoring</info>');
132
133
                continue;
134
            }
135
            if (!$manipulator instanceof ObjectAclManipulatorInterface) {
136
                $output->writeln(sprintf('The interface "ObjectAclManipulatorInterface" is not implemented for %s: <info>ignoring</info>', \get_class($manipulator)));
137
138
                continue;
139
            }
140
141
            \assert($admin instanceof AdminInterface);
142
            $manipulator->batchConfigureAcls($output, $admin, $securityIdentity);
143
        }
144
    }
145
146
    /**
147
     * @return string
148
     */
149
    protected function getUserEntityClass(InputInterface $input, OutputInterface $output)
150
    {
151
        if ('' === $this->userEntityClass) {
152
            if ($input->getOption('user_entity')) {
153
                list($userBundle, $userEntity) = Validators::validateEntityName($input->getOption('user_entity'));
154
                $this->userEntityClass = $this->registry->getEntityNamespace($userBundle).'\\'.$userEntity;
155
            } else {
156
                list($userBundle, $userEntity) = $this->askAndValidate($input, $output, 'Please enter the User Entity shortcut name: ', '', 'Sonata\AdminBundle\Command\Validators::validateEntityName');
157
158
                // Entity exists?
159
                $this->userEntityClass = $this->registry->getEntityNamespace($userBundle).'\\'.$userEntity;
160
            }
161
        }
162
163
        return $this->userEntityClass;
164
    }
165
}
166