Passed
Push — master ( 8001cb...0d19a8 )
by Damien
03:58
created

DoctrineProvider::checkStorageMapper()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 12
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 12
c 0
b 0
f 0
rs 10
cc 3
nc 2
nop 0
1
<?php
2
3
namespace DH\Auditor\Provider\Doctrine;
4
5
use DH\Auditor\Event\LifecycleEvent;
6
use DH\Auditor\Exception\ProviderException;
7
use DH\Auditor\Provider\AbstractProvider;
8
use DH\Auditor\Provider\ConfigurationInterface;
9
use DH\Auditor\Provider\Doctrine\Auditing\Annotation\AnnotationLoader;
10
use DH\Auditor\Provider\Doctrine\Auditing\Event\DoctrineSubscriber;
11
use DH\Auditor\Provider\Doctrine\Auditing\Transaction\TransactionManager;
12
use DH\Auditor\Provider\Doctrine\Persistence\Event\CreateSchemaListener;
13
use DH\Auditor\Provider\Doctrine\Persistence\Helper\DoctrineHelper;
14
use DH\Auditor\Provider\Doctrine\Service\AuditingService;
15
use DH\Auditor\Provider\Doctrine\Service\StorageService;
16
use DH\Auditor\Provider\ProviderInterface;
17
use DH\Auditor\Provider\Service\AuditingServiceInterface;
18
use DH\Auditor\Provider\Service\StorageServiceInterface;
19
use DH\Auditor\User\UserInterface;
20
use DH\AuditorBundle\Security\IpProvider;
0 ignored issues
show
Bug introduced by
The type DH\AuditorBundle\Security\IpProvider was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
21
use DH\AuditorBundle\Security\RolesChecker;
0 ignored issues
show
Bug introduced by
The type DH\AuditorBundle\Security\RolesChecker was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
22
use DH\AuditorBundle\User\UserProvider;
0 ignored issues
show
Bug introduced by
The type DH\AuditorBundle\User\UserProvider was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
23
use Doctrine\ORM\EntityManagerInterface;
24
use Gedmo\SoftDeleteable\SoftDeleteableListener;
25
26
class DoctrineProvider extends AbstractProvider
27
{
28
    public const BOTH = 3;
29
30
    /**
31
     * @var TransactionManager
32
     */
33
    private $transactionManager;
34
35
    public function __construct(ConfigurationInterface $configuration)
36
    {
37
        $this->configuration = $configuration;
38
        $this->transactionManager = new TransactionManager($this);
39
    }
40
41
    public function registerAuditingService(AuditingServiceInterface $service): ProviderInterface
42
    {
43
        parent::registerAuditingService($service);
44
45
        /** @var AuditingService $service */
46
        $entityManager = $service->getEntityManager();
47
        $evm = $entityManager->getEventManager();
48
49
        // Register subscribers
50
        $evm->addEventSubscriber(new DoctrineSubscriber($this->transactionManager));
51
        $evm->addEventSubscriber(new SoftDeleteableListener());
52
53
        $this->loadAnnotations($entityManager);
54
55
        return $this;
56
    }
57
58
    public function registerStorageService(StorageServiceInterface $service): ProviderInterface
59
    {
60
        parent::registerStorageService($service);
61
62
        /** @var StorageService $service */
63
        $entityManager = $service->getEntityManager();
64
        $evm = $entityManager->getEventManager();
65
66
        // Register subscribers
67
        $evm->addEventSubscriber(new CreateSchemaListener($this));
68
69
        return $this;
70
    }
71
72
    public function isStorageMapperRequired(): bool
73
    {
74
        return \count($this->getStorageServices()) > 1;
75
    }
76
77
    public function getStorageServiceForEntity(string $entity): StorageServiceInterface
78
    {
79
        $this->checkStorageMapper();
80
81
        if (null === $this->getConfiguration()->getStorageMapper() && 1 === \count($this->getStorageServices())) {
0 ignored issues
show
Bug introduced by
The method getStorageMapper() does not exist on DH\Auditor\Provider\ConfigurationInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to DH\Auditor\Provider\ConfigurationInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

81
        if (null === $this->getConfiguration()->/** @scrutinizer ignore-call */ getStorageMapper() && 1 === \count($this->getStorageServices())) {
Loading history...
82
            // No mapper and only 1 storage entity manager
83
            return array_values($this->getStorageServices())[0];
84
        }
85
86
        return $this->getConfiguration()->getStorageMapper()->call($this, $entity, $this->getStorageServices());
87
    }
88
89
    public function persist(LifecycleEvent $event): void
90
    {
91
        $payload = $event->getPayload();
92
        $auditTable = $payload['table'];
93
        $entity = $payload['entity'];
94
        unset($payload['table'], $payload['entity']);
95
96
        $fields = [
97
            'type' => ':type',
98
            'object_id' => ':object_id',
99
            'discriminator' => ':discriminator',
100
            'transaction_hash' => ':transaction_hash',
101
            'diffs' => ':diffs',
102
            'blame_id' => ':blame_id',
103
            'blame_user' => ':blame_user',
104
            'blame_user_fqdn' => ':blame_user_fqdn',
105
            'blame_user_firewall' => ':blame_user_firewall',
106
            'ip' => ':ip',
107
            'created_at' => ':created_at',
108
        ];
109
110
        $query = sprintf(
111
            'INSERT INTO %s (%s) VALUES (%s)',
112
            $auditTable,
113
            implode(', ', array_keys($fields)),
114
            implode(', ', array_values($fields))
115
        );
116
117
        /** @var StorageService $storageService */
118
        $storageService = $this->getStorageServiceForEntity($entity);
119
        $statement = $storageService->getEntityManager()->getConnection()->prepare($query);
120
121
        foreach ($payload as $key => $value) {
122
            $statement->bindValue($key, $value);
123
        }
124
125
        $statement->execute();
126
    }
127
128
    /**
129
     * Returns true if $entity is auditable.
130
     *
131
     * @param object|string $entity
132
     */
133
    public function isAuditable($entity): bool
134
    {
135
        $class = DoctrineHelper::getRealClassName($entity);
136
        // is $entity part of audited entities?
137
        /** @var Configuration $configuration */
138
        $configuration = $this->configuration;
139
        if (!\array_key_exists($class, $configuration->getEntities())) {
140
            // no => $entity is not audited
141
            return false;
142
        }
143
144
        return true;
145
    }
146
147
    /**
148
     * Returns true if $entity is audited.
149
     *
150
     * @param object|string $entity
151
     */
152
    public function isAudited($entity): bool
153
    {
154
        if (!$this->auditor->getConfiguration()->isEnabled()) {
155
            return false;
156
        }
157
158
        /** @var Configuration $configuration */
159
        $configuration = $this->configuration;
160
        $class = DoctrineHelper::getRealClassName($entity);
161
162
        // is $entity part of audited entities?
163
        if (!\array_key_exists($class, $configuration->getEntities())) {
164
            // no => $entity is not audited
165
            return false;
166
        }
167
168
        $entityOptions = $configuration->getEntities()[$class];
169
170
        if (null === $entityOptions) {
171
            // no option defined => $entity is audited
172
            return true;
173
        }
174
175
        if (isset($entityOptions['enabled'])) {
176
            return (bool) $entityOptions['enabled'];
177
        }
178
179
        return true;
180
    }
181
182
    /**
183
     * Returns true if $field is audited.
184
     *
185
     * @param object|string $entity
186
     */
187
    public function isAuditedField($entity, string $field): bool
188
    {
189
        // is $field is part of globally ignored columns?
190
        /** @var Configuration $configuration */
191
        $configuration = $this->configuration;
192
        if (\in_array($field, $configuration->getIgnoredColumns(), true)) {
193
            // yes => $field is not audited
194
            return false;
195
        }
196
197
        // is $entity audited?
198
        if (!$this->isAudited($entity)) {
199
            // no => $field is not audited
200
            return false;
201
        }
202
203
        $class = DoctrineHelper::getRealClassName($entity);
204
        $entityOptions = $configuration->getEntities()[$class];
205
206
        if (null === $entityOptions) {
207
            // no option defined => $field is audited
208
            return true;
209
        }
210
211
        // are columns excluded and is field part of them?
212
        if (isset($entityOptions['ignored_columns']) &&
213
            \in_array($field, $entityOptions['ignored_columns'], true)) {
214
            // yes => $field is not audited
215
            return false;
216
        }
217
218
        return true;
219
    }
220
221
    public function supportsStorage(): bool
222
    {
223
        return true;
224
    }
225
226
    public function supportsAuditing(): bool
227
    {
228
        return true;
229
    }
230
231
    public function setUserProvider(UserProvider $userProvider): void
232
    {
233
        $this->configuration->setUserProvider(function () use ($userProvider): ?UserInterface {
0 ignored issues
show
Bug introduced by
The method setUserProvider() does not exist on DH\Auditor\Provider\ConfigurationInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to DH\Auditor\Provider\ConfigurationInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

233
        $this->configuration->/** @scrutinizer ignore-call */ 
234
                              setUserProvider(function () use ($userProvider): ?UserInterface {
Loading history...
234
            return $userProvider->getUser();
235
        });
236
    }
237
238
    public function setIpProvider(IpProvider $ipProvider): void
239
    {
240
        $this->configuration->setIpProvider(function () use ($ipProvider): array {
0 ignored issues
show
Bug introduced by
The method setIpProvider() does not exist on DH\Auditor\Provider\ConfigurationInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to DH\Auditor\Provider\ConfigurationInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

240
        $this->configuration->/** @scrutinizer ignore-call */ 
241
                              setIpProvider(function () use ($ipProvider): array {
Loading history...
241
            return $ipProvider->getClientIpAndFirewall();
242
        });
243
    }
244
245
    public function setRolesChecker(RolesChecker $rolesChecker): void
246
    {
247
        $this->configuration->setRoleChecker(function (string $entity, string $scope) use ($rolesChecker): bool {
0 ignored issues
show
Bug introduced by
The method setRoleChecker() does not exist on DH\Auditor\Provider\ConfigurationInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to DH\Auditor\Provider\ConfigurationInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

247
        $this->configuration->/** @scrutinizer ignore-call */ 
248
                              setRoleChecker(function (string $entity, string $scope) use ($rolesChecker): bool {
Loading history...
248
            return $rolesChecker->isGranted($entity, $scope);
249
        });
250
    }
251
252
    private function loadAnnotations(EntityManagerInterface $entityManager): self
253
    {
254
        /** @var Configuration $configuration */
255
        $configuration = $this->configuration;
256
        $annotationLoader = new AnnotationLoader($entityManager);
257
        $configuration->setEntities(array_merge(
258
            $configuration->getEntities(),
259
            $annotationLoader->load()
260
        ));
261
262
        return $this;
263
    }
264
265
    private function checkStorageMapper(): self
266
    {
267
        if (null === $this->getConfiguration()->getStorageMapper() && $this->isStorageMapperRequired()) {
268
            throw new ProviderException('You must provide a mapper function to map audits to storage.');
269
        }
270
271
//        if (null === $this->getStorageMapper() && 1 === count($this->getStorageServices())) {
272
//            // No mapper and only 1 storage entity manager
273
//            return array_values($this->storageServices)[0];
274
//        }
275
276
        return $this;
277
    }
278
}
279