Passed
Push — master ( bacf0d...a376f1 )
by Roeland
10:03 queued 13s
created

Manager::registerCheck()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 2
rs 10
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016 Morris Jobke <[email protected]>
4
 *
5
 * @license GNU AGPL version 3 or any later version
6
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Affero General Public License as
9
 * published by the Free Software Foundation, either version 3 of the
10
 * License, or (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
 *
20
 */
21
22
namespace OCA\WorkflowEngine;
23
24
use Doctrine\DBAL\DBALException;
25
use OC\Cache\CappedMemoryCache;
26
use OCA\WorkflowEngine\AppInfo\Application;
27
use OCA\WorkflowEngine\Check\FileMimeType;
28
use OCA\WorkflowEngine\Check\FileName;
29
use OCA\WorkflowEngine\Check\FileSize;
30
use OCA\WorkflowEngine\Check\FileSystemTags;
31
use OCA\WorkflowEngine\Check\RequestRemoteAddress;
32
use OCA\WorkflowEngine\Check\RequestTime;
33
use OCA\WorkflowEngine\Check\RequestURL;
34
use OCA\WorkflowEngine\Check\RequestUserAgent;
35
use OCA\WorkflowEngine\Check\UserGroupMembership;
36
use OCA\WorkflowEngine\Entity\File;
37
use OCA\WorkflowEngine\Helper\ScopeContext;
38
use OCA\WorkflowEngine\Service\Logger;
39
use OCA\WorkflowEngine\Service\RuleMatcher;
40
use OCP\AppFramework\QueryException;
41
use OCP\DB\QueryBuilder\IQueryBuilder;
42
use OCP\EventDispatcher\IEventDispatcher;
43
use OCP\Files\Storage\IStorage;
44
use OCP\IConfig;
45
use OCP\IDBConnection;
46
use OCP\IL10N;
47
use OCP\ILogger;
48
use OCP\IServerContainer;
49
use OCP\IUserSession;
50
use OCP\WorkflowEngine\Events\RegisterChecksEvent;
51
use OCP\WorkflowEngine\Events\RegisterEntitiesEvent;
52
use OCP\WorkflowEngine\Events\RegisterOperationsEvent;
53
use OCP\WorkflowEngine\ICheck;
54
use OCP\WorkflowEngine\IComplexOperation;
55
use OCP\WorkflowEngine\IEntity;
56
use OCP\WorkflowEngine\IEntityEvent;
57
use OCP\WorkflowEngine\IManager;
58
use OCP\WorkflowEngine\IOperation;
59
use OCP\WorkflowEngine\IRuleMatcher;
60
use Symfony\Component\EventDispatcher\EventDispatcherInterface as LegacyDispatcher;
61
use Symfony\Component\EventDispatcher\GenericEvent;
62
63
class Manager implements IManager {
64
65
	/** @var IStorage */
66
	protected $storage;
67
68
	/** @var string */
69
	protected $path;
70
71
	/** @var object */
72
	protected $entity;
73
74
	/** @var array[] */
75
	protected $operations = [];
76
77
	/** @var array[] */
78
	protected $checks = [];
79
80
	/** @var IDBConnection */
81
	protected $connection;
82
83
	/** @var IServerContainer|\OC\Server */
84
	protected $container;
85
86
	/** @var IL10N */
87
	protected $l;
88
89
	/** @var LegacyDispatcher */
90
	protected $legacyEventDispatcher;
91
92
	/** @var IEntity[] */
93
	protected $registeredEntities = [];
94
95
	/** @var IOperation[] */
96
	protected $registeredOperators = [];
97
98
	/** @var ICheck[] */
99
	protected $registeredChecks = [];
100
101
	/** @var ILogger */
102
	protected $logger;
103
104
	/** @var CappedMemoryCache */
105
	protected $operationsByScope = [];
106
107
	/** @var IUserSession */
108
	protected $session;
109
110
	/** @var IEventDispatcher */
111
	private $dispatcher;
112
113
	/** @var IConfig */
114
	private $config;
115
116
	public function __construct(
117
		IDBConnection $connection,
118
		IServerContainer $container,
119
		IL10N $l,
120
		LegacyDispatcher $eventDispatcher,
121
		ILogger $logger,
122
		IUserSession $session,
123
		IEventDispatcher $dispatcher,
124
		IConfig $config
125
	) {
126
		$this->connection = $connection;
127
		$this->container = $container;
128
		$this->l = $l;
129
		$this->legacyEventDispatcher = $eventDispatcher;
130
		$this->logger = $logger;
131
		$this->operationsByScope = new CappedMemoryCache(64);
132
		$this->session = $session;
133
		$this->dispatcher = $dispatcher;
134
		$this->config = $config;
135
	}
136
137
	public function getRuleMatcher(): IRuleMatcher {
138
		return new RuleMatcher(
139
			$this->session,
140
			$this->container,
141
			$this->l,
142
			$this,
143
			$this->container->query(Logger::class)
144
		);
145
	}
146
147
	public function getAllConfiguredEvents() {
148
		$query = $this->connection->getQueryBuilder();
149
150
		$query->selectDistinct('class')
151
			->addSelect('entity', 'events')
152
			->from('flow_operations')
153
			->where($query->expr()->neq('events', $query->createNamedParameter('[]'), IQueryBuilder::PARAM_STR));
154
155
		$result = $query->execute();
156
		$operations = [];
157
		while($row = $result->fetch()) {
158
			$eventNames = \json_decode($row['events']);
159
160
			$operation = $row['class'];
161
			$entity =  $row['entity'];
162
163
			$operations[$operation] = $operations[$row['class']] ?? [];
164
			$operations[$operation][$entity] = $operations[$operation][$entity] ?? [];
165
166
			$operations[$operation][$entity] = array_unique(array_merge($operations[$operation][$entity], $eventNames ?? []));
167
		}
168
		$result->closeCursor();
169
170
		return $operations;
171
	}
172
173
	/**
174
	 * @param string $operationClass
175
	 * @return ScopeContext[]
176
	 */
177
	public function getAllConfiguredScopesForOperation(string $operationClass): array {
178
		static $scopesByOperation = [];
179
		if (isset($scopesByOperation[$operationClass])) {
180
			return $scopesByOperation[$operationClass];
181
		}
182
183
		$query = $this->connection->getQueryBuilder();
184
185
		$query->selectDistinct('s.type')
186
			->addSelect('s.value')
187
			->from('flow_operations', 'o')
188
			->leftJoin('o', 'flow_operations_scope', 's', $query->expr()->eq('o.id', 's.operation_id'))
189
			->where($query->expr()->eq('o.class', $query->createParameter('operationClass')));
190
191
		$query->setParameters(['operationClass' => $operationClass]);
192
		$result = $query->execute();
193
194
		$scopesByOperation[$operationClass] = [];
195
		while ($row = $result->fetch()) {
196
			$scope = new ScopeContext($row['type'], $row['value']);
197
			$scopesByOperation[$operationClass][$scope->getHash()] = $scope;
198
		}
199
200
		return $scopesByOperation[$operationClass];
201
	}
202
203
	public function getAllOperations(ScopeContext $scopeContext): array {
204
		if(isset($this->operations[$scopeContext->getHash()])) {
205
			return $this->operations[$scopeContext->getHash()];
206
		}
207
208
		$query = $this->connection->getQueryBuilder();
209
210
		$query->select('o.*')
211
			->selectAlias('s.type', 'scope_type')
212
			->selectAlias('s.value', 'scope_actor_id')
213
			->from('flow_operations', 'o')
214
			->leftJoin('o', 'flow_operations_scope', 's', $query->expr()->eq('o.id', 's.operation_id'))
215
			->where($query->expr()->eq('s.type', $query->createParameter('scope')));
216
217
		if($scopeContext->getScope() === IManager::SCOPE_USER) {
218
			$query->andWhere($query->expr()->eq('s.value', $query->createParameter('scopeId')));
219
		}
220
221
		$query->setParameters(['scope' => $scopeContext->getScope(), 'scopeId' => $scopeContext->getScopeId()]);
222
		$result = $query->execute();
223
224
		$this->operations[$scopeContext->getHash()] = [];
225
		while ($row = $result->fetch()) {
226
			if(!isset($this->operations[$scopeContext->getHash()][$row['class']])) {
227
				$this->operations[$scopeContext->getHash()][$row['class']] = [];
228
			}
229
			$this->operations[$scopeContext->getHash()][$row['class']][] = $row;
230
		}
231
232
		return $this->operations[$scopeContext->getHash()];
233
	}
234
235
	public function getOperations(string $class, ScopeContext $scopeContext): array {
236
		if (!isset($this->operations[$scopeContext->getHash()])) {
237
			$this->getAllOperations($scopeContext);
238
		}
239
		return $this->operations[$scopeContext->getHash()][$class] ?? [];
240
	}
241
242
	/**
243
	 * @param int $id
244
	 * @return array
245
	 * @throws \UnexpectedValueException
246
	 */
247
	protected function getOperation($id) {
248
		$query = $this->connection->getQueryBuilder();
249
		$query->select('*')
250
			->from('flow_operations')
251
			->where($query->expr()->eq('id', $query->createNamedParameter($id)));
252
		$result = $query->execute();
253
		$row = $result->fetch();
254
		$result->closeCursor();
255
256
		if ($row) {
257
			return $row;
258
		}
259
260
		throw new \UnexpectedValueException($this->l->t('Operation #%s does not exist', [$id]));
261
	}
262
263
	protected function insertOperation(
264
		string $class,
265
		string $name,
266
		array $checkIds,
267
		string $operation,
268
		string $entity,
269
		array $events
270
	): int {
271
		$query = $this->connection->getQueryBuilder();
272
		$query->insert('flow_operations')
273
			->values([
274
				'class' => $query->createNamedParameter($class),
275
				'name' => $query->createNamedParameter($name),
276
				'checks' => $query->createNamedParameter(json_encode(array_unique($checkIds))),
277
				'operation' => $query->createNamedParameter($operation),
278
				'entity' => $query->createNamedParameter($entity),
279
				'events' => $query->createNamedParameter(json_encode($events))
280
			]);
281
		$query->execute();
282
283
		return $query->getLastInsertId();
284
	}
285
286
	/**
287
	 * @param string $class
288
	 * @param string $name
289
	 * @param array[] $checks
290
	 * @param string $operation
291
	 * @return array The added operation
292
	 * @throws \UnexpectedValueException
293
	 * @throws DBALException
294
	 */
295
	public function addOperation(
296
		string $class,
297
		string $name,
298
		array $checks,
299
		string $operation,
300
		ScopeContext $scope,
301
		string $entity,
302
		array $events
303
	) {
304
		$this->validateOperation($class, $name, $checks, $operation, $entity, $events);
305
306
		$this->connection->beginTransaction();
307
308
		try {
309
			$checkIds = [];
310
			foreach ($checks as $check) {
311
				$checkIds[] = $this->addCheck($check['class'], $check['operator'], $check['value']);
312
			}
313
314
			$id = $this->insertOperation($class, $name, $checkIds, $operation, $entity, $events);
315
			$this->addScope($id, $scope);
316
317
			$this->connection->commit();
318
		} catch (DBALException $e) {
319
			$this->connection->rollBack();
320
			throw $e;
321
		}
322
323
		return $this->getOperation($id);
324
	}
325
326
	protected function canModify(int $id, ScopeContext $scopeContext):bool {
327
		if(isset($this->operationsByScope[$scopeContext->getHash()])) {
328
			return in_array($id, $this->operationsByScope[$scopeContext->getHash()], true);
329
		}
330
331
		$qb = $this->connection->getQueryBuilder();
332
		$qb = $qb->select('o.id')
333
			->from('flow_operations', 'o')
334
			->leftJoin('o', 'flow_operations_scope', 's', $qb->expr()->eq('o.id', 's.operation_id'))
335
			->where($qb->expr()->eq('s.type', $qb->createParameter('scope')));
336
337
		if($scopeContext->getScope() !== IManager::SCOPE_ADMIN) {
338
			$qb->where($qb->expr()->eq('s.value', $qb->createParameter('scopeId')));
339
		}
340
341
		$qb->setParameters(['scope' => $scopeContext->getScope(), 'scopeId' => $scopeContext->getScopeId()]);
342
		$result = $qb->execute();
343
344
		$this->operationsByScope[$scopeContext->getHash()] = [];
345
		while($opId = $result->fetchColumn(0)) {
346
			$this->operationsByScope[$scopeContext->getHash()][] = (int)$opId;
347
		}
348
		$result->closeCursor();
349
350
		return in_array($id, $this->operationsByScope[$scopeContext->getHash()], true);
351
	}
352
353
	/**
354
	 * @param int $id
355
	 * @param string $name
356
	 * @param array[] $checks
357
	 * @param string $operation
358
	 * @return array The updated operation
359
	 * @throws \UnexpectedValueException
360
	 * @throws \DomainException
361
	 * @throws DBALException
362
	 */
363
	public function updateOperation(
364
		int $id,
365
		string $name,
366
		array $checks,
367
		string $operation,
368
		ScopeContext $scopeContext,
369
		string $entity,
370
		array $events
371
	): array {
372
		if(!$this->canModify($id, $scopeContext)) {
373
			throw new \DomainException('Target operation not within scope');
374
		};
375
		$row = $this->getOperation($id);
376
		$this->validateOperation($row['class'], $name, $checks, $operation, $entity, $events);
377
378
		$checkIds = [];
379
		try {
380
			$this->connection->beginTransaction();
381
			foreach ($checks as $check) {
382
				$checkIds[] = $this->addCheck($check['class'], $check['operator'], $check['value']);
383
			}
384
385
			$query = $this->connection->getQueryBuilder();
386
			$query->update('flow_operations')
387
				->set('name', $query->createNamedParameter($name))
388
				->set('checks', $query->createNamedParameter(json_encode(array_unique($checkIds))))
389
				->set('operation', $query->createNamedParameter($operation))
390
				->set('entity', $query->createNamedParameter($entity))
391
				->set('events', $query->createNamedParameter(json_encode($events)))
392
				->where($query->expr()->eq('id', $query->createNamedParameter($id)));
393
			$query->execute();
394
			$this->connection->commit();
395
		} catch (DBALException $e) {
396
			$this->connection->rollBack();
397
			throw $e;
398
		}
399
		unset($this->operations[$scopeContext->getHash()]);
400
401
		return $this->getOperation($id);
402
	}
403
404
	/**
405
	 * @param int $id
406
	 * @return bool
407
	 * @throws \UnexpectedValueException
408
	 * @throws DBALException
409
	 * @throws \DomainException
410
	 */
411
	public function deleteOperation($id, ScopeContext $scopeContext) {
412
		if(!$this->canModify($id, $scopeContext)) {
413
			throw new \DomainException('Target operation not within scope');
414
		};
415
		$query = $this->connection->getQueryBuilder();
416
		try {
417
			$this->connection->beginTransaction();
418
			$result = (bool)$query->delete('flow_operations')
419
				->where($query->expr()->eq('id', $query->createNamedParameter($id)))
420
				->execute();
421
			if($result) {
422
				$qb = $this->connection->getQueryBuilder();
423
				$result &= (bool)$qb->delete('flow_operations_scope')
424
					->where($qb->expr()->eq('operation_id', $qb->createNamedParameter($id)))
425
					->execute();
426
			}
427
			$this->connection->commit();
428
		} catch (DBALException $e) {
429
			$this->connection->rollBack();
430
			throw $e;
431
		}
432
433
		if(isset($this->operations[$scopeContext->getHash()])) {
434
			unset($this->operations[$scopeContext->getHash()]);
435
		}
436
437
		return $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result also could return the type integer which is incompatible with the documented return type boolean.
Loading history...
438
	}
439
440
	protected function validateEvents(string $entity, array $events, IOperation $operation) {
441
		try {
442
			/** @var IEntity $instance */
443
			$instance = $this->container->query($entity);
444
		} catch (QueryException $e) {
445
			throw new \UnexpectedValueException($this->l->t('Entity %s does not exist', [$entity]));
446
		}
447
448
		if(!$instance instanceof IEntity) {
0 ignored issues
show
introduced by
$instance is always a sub-type of OCP\WorkflowEngine\IEntity.
Loading history...
449
			throw new \UnexpectedValueException($this->l->t('Entity %s is invalid', [$entity]));
450
		}
451
452
		if(empty($events)) {
453
			if(!$operation instanceof IComplexOperation) {
454
				throw new \UnexpectedValueException($this->l->t('No events are chosen.'));
455
			}
456
			return;
457
		}
458
459
		$availableEvents = [];
460
		foreach ($instance->getEvents() as $event) {
461
			/** @var IEntityEvent $event */
462
			$availableEvents[] = $event->getEventName();
463
		}
464
465
		$diff = array_diff($events, $availableEvents);
466
		if(!empty($diff)) {
467
			throw new \UnexpectedValueException($this->l->t('Entity %s has no event %s', [$entity, array_shift($diff)]));
468
		}
469
	}
470
471
	/**
472
	 * @param string $class
473
	 * @param string $name
474
	 * @param array[] $checks
475
	 * @param string $operation
476
	 * @throws \UnexpectedValueException
477
	 */
478
	public function validateOperation($class, $name, array $checks, $operation, string $entity, array $events) {
479
		try {
480
			/** @var IOperation $instance */
481
			$instance = $this->container->query($class);
482
		} catch (QueryException $e) {
483
			throw new \UnexpectedValueException($this->l->t('Operation %s does not exist', [$class]));
484
		}
485
486
		if (!($instance instanceof IOperation)) {
0 ignored issues
show
introduced by
$instance is always a sub-type of OCP\WorkflowEngine\IOperation.
Loading history...
487
			throw new \UnexpectedValueException($this->l->t('Operation %s is invalid', [$class]));
488
		}
489
490
		$this->validateEvents($entity, $events, $instance);
491
492
		if (count($checks) === 0) {
493
			throw new \UnexpectedValueException($this->l->t('At least one check needs to be provided'));
494
		}
495
		$instance->validateOperation($name, $checks, $operation);
496
497
		foreach ($checks as $check) {
498
			if (!is_string($check['class'])) {
499
				throw new \UnexpectedValueException($this->l->t('Invalid check provided'));
500
			}
501
502
			try {
503
				/** @var ICheck $instance */
504
				$instance = $this->container->query($check['class']);
505
			} catch (QueryException $e) {
506
				throw new \UnexpectedValueException($this->l->t('Check %s does not exist', [$class]));
507
			}
508
509
			if (!($instance instanceof ICheck)) {
510
				throw new \UnexpectedValueException($this->l->t('Check %s is invalid', [$class]));
511
			}
512
513
			if (!empty($instance->supportedEntities())
514
				&& !in_array($entity, $instance->supportedEntities())
515
			) {
516
				throw new \UnexpectedValueException($this->l->t('Check %s is not allowed with this entity', [$class]));
517
			}
518
519
			$instance->validateCheck($check['operator'], $check['value']);
520
		}
521
	}
522
523
	/**
524
	 * @param int[] $checkIds
525
	 * @return array[]
526
	 */
527
	public function getChecks(array $checkIds) {
528
		$checkIds = array_map('intval', $checkIds);
529
530
		$checks = [];
531
		foreach ($checkIds as $i => $checkId) {
532
			if (isset($this->checks[$checkId])) {
533
				$checks[$checkId] = $this->checks[$checkId];
534
				unset($checkIds[$i]);
535
			}
536
		}
537
538
		if (empty($checkIds)) {
539
			return $checks;
540
		}
541
542
		$query = $this->connection->getQueryBuilder();
543
		$query->select('*')
544
			->from('flow_checks')
545
			->where($query->expr()->in('id', $query->createNamedParameter($checkIds, IQueryBuilder::PARAM_INT_ARRAY)));
546
		$result = $query->execute();
547
548
		while ($row = $result->fetch()) {
549
			$this->checks[(int) $row['id']] = $row;
550
			$checks[(int) $row['id']] = $row;
551
		}
552
		$result->closeCursor();
553
554
		$checkIds = array_diff($checkIds, array_keys($checks));
555
556
		if (!empty($checkIds)) {
557
			$missingCheck = array_pop($checkIds);
558
			throw new \UnexpectedValueException($this->l->t('Check #%s does not exist', $missingCheck));
559
		}
560
561
		return $checks;
562
	}
563
564
	/**
565
	 * @param string $class
566
	 * @param string $operator
567
	 * @param string $value
568
	 * @return int Check unique ID
569
	 */
570
	protected function addCheck($class, $operator, $value) {
571
		$hash = md5($class . '::' . $operator . '::' . $value);
572
573
		$query = $this->connection->getQueryBuilder();
574
		$query->select('id')
575
			->from('flow_checks')
576
			->where($query->expr()->eq('hash', $query->createNamedParameter($hash)));
577
		$result = $query->execute();
578
579
		if ($row = $result->fetch()) {
580
			$result->closeCursor();
581
			return (int) $row['id'];
582
		}
583
584
		$query = $this->connection->getQueryBuilder();
585
		$query->insert('flow_checks')
586
			->values([
587
				'class' => $query->createNamedParameter($class),
588
				'operator' => $query->createNamedParameter($operator),
589
				'value' => $query->createNamedParameter($value),
590
				'hash' => $query->createNamedParameter($hash),
591
			]);
592
		$query->execute();
593
594
		return $query->getLastInsertId();
595
	}
596
597
	protected function addScope(int $operationId, ScopeContext $scope): void {
598
		$query = $this->connection->getQueryBuilder();
599
600
		$insertQuery = $query->insert('flow_operations_scope');
601
		$insertQuery->values([
602
			'operation_id' => $query->createNamedParameter($operationId),
603
			'type' => $query->createNamedParameter($scope->getScope()),
604
			'value' => $query->createNamedParameter($scope->getScopeId()),
605
		]);
606
		$insertQuery->execute();
607
	}
608
609
	public function formatOperation(array $operation): array {
610
		$checkIds = json_decode($operation['checks'], true);
611
		$checks = $this->getChecks($checkIds);
612
613
		$operation['checks'] = [];
614
		foreach ($checks as $check) {
615
			// Remove internal values
616
			unset($check['id']);
617
			unset($check['hash']);
618
619
			$operation['checks'][] = $check;
620
		}
621
		$operation['events'] = json_decode($operation['events'], true) ?? [];
622
623
624
		return $operation;
625
	}
626
627
	/**
628
	 * @return IEntity[]
629
	 */
630
	public function getEntitiesList(): array {
631
		$this->dispatcher->dispatchTyped(new RegisterEntitiesEvent($this));
632
		$this->legacyEventDispatcher->dispatch(IManager::EVENT_NAME_REG_ENTITY, new GenericEvent($this));
0 ignored issues
show
Unused Code introduced by
The call to Symfony\Contracts\EventD...erInterface::dispatch() has too many arguments starting with new Symfony\Component\Ev...her\GenericEvent($this). ( Ignorable by Annotation )

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

632
		$this->legacyEventDispatcher->/** @scrutinizer ignore-call */ 
633
                                dispatch(IManager::EVENT_NAME_REG_ENTITY, new GenericEvent($this));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Bug introduced by
OCP\WorkflowEngine\IManager::EVENT_NAME_REG_ENTITY of type string is incompatible with the type object expected by parameter $event of Symfony\Contracts\EventD...erInterface::dispatch(). ( Ignorable by Annotation )

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

632
		$this->legacyEventDispatcher->dispatch(/** @scrutinizer ignore-type */ IManager::EVENT_NAME_REG_ENTITY, new GenericEvent($this));
Loading history...
633
634
		return array_values(array_merge($this->getBuildInEntities(), $this->registeredEntities));
635
	}
636
637
	/**
638
	 * @return IOperation[]
639
	 */
640
	public function getOperatorList(): array {
641
		$this->dispatcher->dispatchTyped(new RegisterOperationsEvent($this));
642
		$this->legacyEventDispatcher->dispatch(IManager::EVENT_NAME_REG_OPERATION, new GenericEvent($this));
0 ignored issues
show
Bug introduced by
OCP\WorkflowEngine\IMana...VENT_NAME_REG_OPERATION of type string is incompatible with the type object expected by parameter $event of Symfony\Contracts\EventD...erInterface::dispatch(). ( Ignorable by Annotation )

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

642
		$this->legacyEventDispatcher->dispatch(/** @scrutinizer ignore-type */ IManager::EVENT_NAME_REG_OPERATION, new GenericEvent($this));
Loading history...
Unused Code introduced by
The call to Symfony\Contracts\EventD...erInterface::dispatch() has too many arguments starting with new Symfony\Component\Ev...her\GenericEvent($this). ( Ignorable by Annotation )

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

642
		$this->legacyEventDispatcher->/** @scrutinizer ignore-call */ 
643
                                dispatch(IManager::EVENT_NAME_REG_OPERATION, new GenericEvent($this));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
643
644
		return array_merge($this->getBuildInOperators(), $this->registeredOperators);
645
	}
646
647
	/**
648
	 * @return ICheck[]
649
	 */
650
	public function getCheckList(): array {
651
		$this->dispatcher->dispatchTyped(new RegisterChecksEvent($this));
652
		$this->legacyEventDispatcher->dispatch(IManager::EVENT_NAME_REG_CHECK, new GenericEvent($this));
0 ignored issues
show
Unused Code introduced by
The call to Symfony\Contracts\EventD...erInterface::dispatch() has too many arguments starting with new Symfony\Component\Ev...her\GenericEvent($this). ( Ignorable by Annotation )

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

652
		$this->legacyEventDispatcher->/** @scrutinizer ignore-call */ 
653
                                dispatch(IManager::EVENT_NAME_REG_CHECK, new GenericEvent($this));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Bug introduced by
OCP\WorkflowEngine\IManager::EVENT_NAME_REG_CHECK of type string is incompatible with the type object expected by parameter $event of Symfony\Contracts\EventD...erInterface::dispatch(). ( Ignorable by Annotation )

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

652
		$this->legacyEventDispatcher->dispatch(/** @scrutinizer ignore-type */ IManager::EVENT_NAME_REG_CHECK, new GenericEvent($this));
Loading history...
653
654
		return array_merge($this->getBuildInChecks(), $this->registeredChecks);
655
	}
656
657
	public function registerEntity(IEntity $entity): void {
658
		$this->registeredEntities[get_class($entity)] = $entity;
659
	}
660
661
	public function registerOperation(IOperation $operator): void {
662
		$this->registeredOperators[get_class($operator)] = $operator;
663
	}
664
665
	public function registerCheck(ICheck $check): void {
666
		$this->registeredChecks[get_class($check)] = $check;
667
	}
668
669
	/**
670
	 * @return IEntity[]
671
	 */
672
	protected function getBuildInEntities(): array {
673
		try {
674
			return [
675
				File::class => $this->container->query(File::class),
676
			];
677
		} catch (QueryException $e) {
678
			$this->logger->logException($e);
679
			return [];
680
		}
681
	}
682
683
	/**
684
	 * @return IOperation[]
685
	 */
686
	protected function getBuildInOperators(): array {
687
		try {
688
			return [
689
				// None yet
690
			];
691
		} catch (QueryException $e) {
0 ignored issues
show
Unused Code introduced by
catch (\OCP\AppFramework\QueryException $e) is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
692
			$this->logger->logException($e);
693
			return [];
694
		}
695
	}
696
697
	/**
698
	 * @return IEntity[]
699
	 */
700
	protected function getBuildInChecks(): array {
701
		try {
702
			return [
703
				$this->container->query(FileMimeType::class),
704
				$this->container->query(FileName::class),
705
				$this->container->query(FileSize::class),
706
				$this->container->query(FileSystemTags::class),
707
				$this->container->query(RequestRemoteAddress::class),
708
				$this->container->query(RequestTime::class),
709
				$this->container->query(RequestURL::class),
710
				$this->container->query(RequestUserAgent::class),
711
				$this->container->query(UserGroupMembership::class),
712
			];
713
		} catch (QueryException $e) {
714
			$this->logger->logException($e);
715
			return [];
716
		}
717
	}
718
719
	public function isUserScopeEnabled(): bool {
720
		return $this->config->getAppValue(Application::APP_ID, 'user_scope_disabled', 'no') === 'no';
721
	}
722
}
723