Passed
Push — master ( 111b19...81e998 )
by Damien
06:16
created

DoctrineProvider::supportsStorage()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
c 0
b 0
f 0
rs 10
cc 1
nc 1
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\Security\RoleCheckerInterface;
20
use DH\Auditor\Security\SecurityProviderInterface;
21
use DH\Auditor\User\UserProviderInterface;
22
use Doctrine\ORM\EntityManagerInterface;
23
use Gedmo\SoftDeleteable\SoftDeleteableListener;
24
25
class DoctrineProvider extends AbstractProvider
26
{
27
    /**
28
     * @var TransactionManager
29
     */
30
    private $transactionManager;
31
32
    public function __construct(ConfigurationInterface $configuration)
33
    {
34
        $this->configuration = $configuration;
35
        $this->transactionManager = new TransactionManager($this);
36
    }
37
38
    public function registerAuditingService(AuditingServiceInterface $service): ProviderInterface
39
    {
40
        parent::registerAuditingService($service);
41
42
        /** @var AuditingService $service */
43
        $entityManager = $service->getEntityManager();
44
        $evm = $entityManager->getEventManager();
45
46
        // Register subscribers
47
        $evm->addEventSubscriber(new DoctrineSubscriber($this->transactionManager));
48
        $evm->addEventSubscriber(new SoftDeleteableListener());
49
50
        $this->loadAnnotations($entityManager);
51
52
        return $this;
53
    }
54
55
    public function registerStorageService(StorageServiceInterface $service): ProviderInterface
56
    {
57
        parent::registerStorageService($service);
58
59
        /** @var StorageService $service */
60
        $entityManager = $service->getEntityManager();
61
        $evm = $entityManager->getEventManager();
62
63
        // Register subscribers
64
        $evm->addEventSubscriber(new CreateSchemaListener($this));
65
66
        return $this;
67
    }
68
69
    public function isStorageMapperRequired(): bool
70
    {
71
        return \count($this->getStorageServices()) > 1;
72
    }
73
74
    public function getStorageServiceForEntity(string $entity): StorageServiceInterface
75
    {
76
        $this->checkStorageMapper();
77
78
        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

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

232
        $this->configuration->/** @scrutinizer ignore-call */ 
233
                              setStorageMapper($storageMapper);
Loading history...
233
    }
234
235
    public function setUserProvider(UserProviderInterface $userProvider): void
236
    {
237
        $this->configuration->setUserProvider($userProvider);
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

237
        $this->configuration->/** @scrutinizer ignore-call */ 
238
                              setUserProvider($userProvider);
Loading history...
238
    }
239
240
    public function setSecurityProvider(SecurityProviderInterface $securityProvider): void
241
    {
242
        $this->configuration->setSecurityProvider($securityProvider);
0 ignored issues
show
Bug introduced by
The method setSecurityProvider() 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

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