Skip to content

Commit db20a53

Browse files
committed
refactor(workflowengine): Check if class is correct
Signed-off-by: Carl Schwan <[email protected]>
1 parent ea1b5fe commit db20a53

File tree

2 files changed

+63
-22
lines changed

2 files changed

+63
-22
lines changed

apps/workflowengine/lib/Manager.php

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -417,17 +417,20 @@ public function deleteOperation($id, ScopeContext $scopeContext) {
417417
}
418418

419419
protected function validateEvents(string $entity, array $events, IOperation $operation) {
420+
/** @psalm-suppress TaintedCallable newInstance is not called */
421+
$reflection = new \ReflectionClass($entity);
422+
423+
if ($entity !== IEntity::class && !in_array(IEntity::class, $reflection->getInterfaceNames())) {
424+
throw new \UnexpectedValueException($this->l->t('Entity %s is invalid', [$entity]));
425+
}
426+
420427
try {
421428
/** @var IEntity $instance */
422429
$instance = $this->container->query($entity);
423430
} catch (QueryException $e) {
424431
throw new \UnexpectedValueException($this->l->t('Entity %s does not exist', [$entity]));
425432
}
426433

427-
if (!$instance instanceof IEntity) {
428-
throw new \UnexpectedValueException($this->l->t('Entity %s is invalid', [$entity]));
429-
}
430-
431434
if (empty($events)) {
432435
if (!$operation instanceof IComplexOperation) {
433436
throw new \UnexpectedValueException($this->l->t('No events are chosen.'));
@@ -458,17 +461,23 @@ protected function validateEvents(string $entity, array $events, IOperation $ope
458461
* @throws \UnexpectedValueException
459462
*/
460463
public function validateOperation($class, $name, array $checks, $operation, ScopeContext $scope, string $entity, array $events) {
464+
if (strlen($operation) > IManager::MAX_OPERATION_VALUE_BYTES) {
465+
throw new \UnexpectedValueException($this->l->t('The provided operation data is too long'));
466+
}
467+
468+
/** @psalm-suppress TaintedCallable newInstance is not called */
469+
$reflection = new \ReflectionClass($class);
470+
if ($class !== IOperation::class && !in_array(IOperation::class, $reflection->getInterfaceNames())) {
471+
throw new \UnexpectedValueException($this->l->t('Operation %s is invalid', [$class]) . join(', ', $reflection->getInterfaceNames()));
472+
}
473+
461474
try {
462475
/** @var IOperation $instance */
463476
$instance = $this->container->query($class);
464477
} catch (QueryException $e) {
465478
throw new \UnexpectedValueException($this->l->t('Operation %s does not exist', [$class]));
466479
}
467480

468-
if (!($instance instanceof IOperation)) {
469-
throw new \UnexpectedValueException($this->l->t('Operation %s is invalid', [$class]));
470-
}
471-
472481
if (!$instance->isAvailableForScope($scope->getScope())) {
473482
throw new \UnexpectedValueException($this->l->t('Operation %s is invalid', [$class]));
474483
}
@@ -490,17 +499,22 @@ public function validateOperation($class, $name, array $checks, $operation, Scop
490499
throw new \UnexpectedValueException($this->l->t('Invalid check provided'));
491500
}
492501

502+
if (strlen((string) $check['value']) > IManager::MAX_CHECK_VALUE_BYTES) {
503+
throw new \UnexpectedValueException($this->l->t('The provided check value is too long'));
504+
}
505+
506+
$reflection = new \ReflectionClass($check['class']);
507+
if ($check['class'] !== ICheck::class && !in_array(ICheck::class, $reflection->getInterfaceNames())) {
508+
throw new \UnexpectedValueException($this->l->t('Check %s is invalid', [$class]));
509+
}
510+
493511
try {
494512
/** @var ICheck $instance */
495513
$instance = $this->container->query($check['class']);
496514
} catch (QueryException $e) {
497515
throw new \UnexpectedValueException($this->l->t('Check %s does not exist', [$class]));
498516
}
499517

500-
if (!($instance instanceof ICheck)) {
501-
throw new \UnexpectedValueException($this->l->t('Check %s is invalid', [$class]));
502-
}
503-
504518
if (!empty($instance->supportedEntities())
505519
&& !in_array($entity, $instance->supportedEntities())
506520
) {

apps/workflowengine/tests/ManagerTest.php

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use OCA\WorkflowEngine\Helper\ScopeContext;
1313
use OCA\WorkflowEngine\Manager;
1414
use OCP\AppFramework\QueryException;
15+
use OCP\EventDispatcher\Event;
1516
use OCP\EventDispatcher\IEventDispatcher;
1617
use OCP\Files\Events\Node\NodeCreatedEvent;
1718
use OCP\Files\IRootFolder;
@@ -32,10 +33,41 @@
3233
use OCP\WorkflowEngine\IEntityEvent;
3334
use OCP\WorkflowEngine\IManager;
3435
use OCP\WorkflowEngine\IOperation;
36+
use OCP\WorkflowEngine\IRuleMatcher;
3537
use PHPUnit\Framework\MockObject\MockObject;
3638
use Psr\Log\LoggerInterface;
3739
use Test\TestCase;
3840

41+
class TestAdminOp implements IOperation {
42+
public function getDisplayName(): string {
43+
return 'Admin';
44+
}
45+
46+
public function getDescription(): string {
47+
return '';
48+
}
49+
50+
public function getIcon(): string {
51+
return '';
52+
}
53+
54+
public function isAvailableForScope(int $scope): bool {
55+
return true;
56+
}
57+
58+
public function validateOperation(string $name, array $checks, string $operation): void {
59+
}
60+
61+
public function onEvent(string $eventName, Event $event, IRuleMatcher $ruleMatcher): void {
62+
}
63+
}
64+
65+
class TestUserOp extends TestAdminOp {
66+
public function getDisplayName(): string {
67+
return 'User';
68+
}
69+
}
70+
3971
/**
4072
* Class ManagerTest
4173
*
@@ -400,19 +432,19 @@ public function testUpdateOperation() {
400432
$opId1 = $this->invokePrivate(
401433
$this->manager,
402434
'insertOperation',
403-
['OCA\WFE\TestAdminOp', 'Test01', [11, 22], 'foo', $entity, []]
435+
[TestAdminOp::class, 'Test01', [11, 22], 'foo', $entity, []]
404436
);
405437
$this->invokePrivate($this->manager, 'addScope', [$opId1, $adminScope]);
406438

407439
$opId2 = $this->invokePrivate(
408440
$this->manager,
409441
'insertOperation',
410-
['OCA\WFE\TestUserOp', 'Test02', [33, 22], 'bar', $entity, []]
442+
[TestUserOp::class, 'Test02', [33, 22], 'bar', $entity, []]
411443
);
412444
$this->invokePrivate($this->manager, 'addScope', [$opId2, $userScope]);
413445

414-
$check1 = ['class' => 'OCA\WFE\C22', 'operator' => 'eq', 'value' => 'asdf'];
415-
$check2 = ['class' => 'OCA\WFE\C33', 'operator' => 'eq', 'value' => 23456];
446+
$check1 = ['class' => ICheck::class, 'operator' => 'eq', 'value' => 'asdf'];
447+
$check2 = ['class' => ICheck::class, 'operator' => 'eq', 'value' => 23456];
416448

417449
/** @noinspection PhpUnhandledExceptionInspection */
418450
$op = $this->manager->updateOperation($opId1, 'Test01a', [$check1, $check2], 'foohur', $adminScope, $entity, ['\OCP\Files::postDelete']);
@@ -675,11 +707,6 @@ public function testValidateOperationDataLengthError() {
675707
->method('getScope')
676708
->willReturn(IManager::SCOPE_ADMIN);
677709

678-
$operationMock->expects($this->once())
679-
->method('isAvailableForScope')
680-
->with(IManager::SCOPE_ADMIN)
681-
->willReturn(true);
682-
683710
$operationMock->expects($this->never())
684711
->method('validateOperation');
685712

@@ -727,7 +754,7 @@ public function testValidateOperationScopeNotAvailable() {
727754
'operator' => 'is',
728755
'value' => 'barfoo',
729756
];
730-
$operationData = str_pad('', IManager::MAX_OPERATION_VALUE_BYTES + 1, 'FooBar');
757+
$operationData = str_pad('', IManager::MAX_OPERATION_VALUE_BYTES - 1, 'FooBar');
731758

732759
$operationMock = $this->createMock(IOperation::class);
733760
$entityMock = $this->createMock(IEntity::class);

0 commit comments

Comments
 (0)