inform the user when flow config data exceeds thresholds

Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
This commit is contained in:
Arthur Schiwon 2020-10-28 14:07:14 +01:00 committed by backportbot[bot]
parent 4d20a63d4f
commit 4967d5b2ba
3 changed files with 178 additions and 0 deletions

View File

@ -492,6 +492,11 @@ class Manager implements IManager {
if (count($checks) === 0) { if (count($checks) === 0) {
throw new \UnexpectedValueException($this->l->t('At least one check needs to be provided')); throw new \UnexpectedValueException($this->l->t('At least one check needs to be provided'));
} }
if (strlen((string)$operation) > IManager::MAX_OPERATION_VALUE_BYTES) {
throw new \UnexpectedValueException($this->l->t('The provided operation data is too long'));
}
$instance->validateOperation($name, $checks, $operation); $instance->validateOperation($name, $checks, $operation);
foreach ($checks as $check) { foreach ($checks as $check) {
@ -516,6 +521,10 @@ class Manager implements IManager {
throw new \UnexpectedValueException($this->l->t('Check %s is not allowed with this entity', [$class])); throw new \UnexpectedValueException($this->l->t('Check %s is not allowed with this entity', [$class]));
} }
if (strlen((string)$check['value']) > IManager::MAX_CHECK_VALUE_BYTES) {
throw new \UnexpectedValueException($this->l->t('The provided check value is too long'));
}
$instance->validateCheck($check['operator'], $check['value']); $instance->validateCheck($check['operator'], $check['value']);
} }
} }

View File

@ -37,6 +37,7 @@ use OCP\IUserSession;
use OCP\SystemTag\ISystemTagManager; use OCP\SystemTag\ISystemTagManager;
use OCP\WorkflowEngine\ICheck; use OCP\WorkflowEngine\ICheck;
use OCP\WorkflowEngine\IEntity; use OCP\WorkflowEngine\IEntity;
use OCP\WorkflowEngine\IEntityEvent;
use OCP\WorkflowEngine\IManager; use OCP\WorkflowEngine\IManager;
use OCP\WorkflowEngine\IOperation; use OCP\WorkflowEngine\IOperation;
use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\MockObject\MockObject;
@ -433,4 +434,162 @@ class ManagerTest extends TestCase {
$this->assertSame(1, $entityTypeCounts[0]); $this->assertSame(1, $entityTypeCounts[0]);
$this->assertSame(1, $entityTypeCounts[1]); $this->assertSame(1, $entityTypeCounts[1]);
} }
public function testValidateOperationOK() {
$check = [
'class' => ICheck::class,
'operator' => 'is',
'value' => 'barfoo',
];
$operationMock = $this->createMock(IOperation::class);
$entityMock = $this->createMock(IEntity::class);
$eventEntityMock = $this->createMock(IEntityEvent::class);
$checkMock = $this->createMock(ICheck::class);
$operationMock->expects($this->once())
->method('validateOperation')
->with('test', [$check], 'operationData');
$entityMock->expects($this->any())
->method('getEvents')
->willReturn([$eventEntityMock]);
$eventEntityMock->expects($this->any())
->method('getEventName')
->willReturn('MyEvent');
$checkMock->expects($this->any())
->method('supportedEntities')
->willReturn([IEntity::class]);
$checkMock->expects($this->atLeastOnce())
->method('validateCheck');
$this->container->expects($this->any())
->method('query')
->willReturnCallback(function ($className) use ($operationMock, $entityMock, $eventEntityMock, $checkMock) {
switch ($className) {
case IOperation::class:
return $operationMock;
case IEntity::class:
return $entityMock;
case IEntityEvent::class:
return $eventEntityMock;
case ICheck::class:
return $checkMock;
default:
return $this->createMock($className);
}
});
$this->manager->validateOperation(IOperation::class, 'test', [$check], 'operationData', IEntity::class, ['MyEvent']);
}
public function testValidateOperationCheckInputLengthError() {
$check = [
'class' => ICheck::class,
'operator' => 'is',
'value' => str_pad('', IManager::MAX_CHECK_VALUE_BYTES + 1, 'FooBar'),
];
$operationMock = $this->createMock(IOperation::class);
$entityMock = $this->createMock(IEntity::class);
$eventEntityMock = $this->createMock(IEntityEvent::class);
$checkMock = $this->createMock(ICheck::class);
$operationMock->expects($this->once())
->method('validateOperation')
->with('test', [$check], 'operationData');
$entityMock->expects($this->any())
->method('getEvents')
->willReturn([$eventEntityMock]);
$eventEntityMock->expects($this->any())
->method('getEventName')
->willReturn('MyEvent');
$checkMock->expects($this->any())
->method('supportedEntities')
->willReturn([IEntity::class]);
$checkMock->expects($this->never())
->method('validateCheck');
$this->container->expects($this->any())
->method('query')
->willReturnCallback(function ($className) use ($operationMock, $entityMock, $eventEntityMock, $checkMock) {
switch ($className) {
case IOperation::class:
return $operationMock;
case IEntity::class:
return $entityMock;
case IEntityEvent::class:
return $eventEntityMock;
case ICheck::class:
return $checkMock;
default:
return $this->createMock($className);
}
});
try {
$this->manager->validateOperation(IOperation::class, 'test', [$check], 'operationData', IEntity::class, ['MyEvent']);
} catch (\UnexpectedValueException $e) {
$this->assertSame('The provided check value is too long', $e->getMessage());
}
}
public function testValidateOperationDataLengthError() {
$check = [
'class' => ICheck::class,
'operator' => 'is',
'value' => 'barfoo',
];
$operationData = str_pad('', IManager::MAX_OPERATION_VALUE_BYTES + 1, 'FooBar');
$operationMock = $this->createMock(IOperation::class);
$entityMock = $this->createMock(IEntity::class);
$eventEntityMock = $this->createMock(IEntityEvent::class);
$checkMock = $this->createMock(ICheck::class);
$operationMock->expects($this->never())
->method('validateOperation');
$entityMock->expects($this->any())
->method('getEvents')
->willReturn([$eventEntityMock]);
$eventEntityMock->expects($this->any())
->method('getEventName')
->willReturn('MyEvent');
$checkMock->expects($this->any())
->method('supportedEntities')
->willReturn([IEntity::class]);
$checkMock->expects($this->never())
->method('validateCheck');
$this->container->expects($this->any())
->method('query')
->willReturnCallback(function ($className) use ($operationMock, $entityMock, $eventEntityMock, $checkMock) {
switch ($className) {
case IOperation::class:
return $operationMock;
case IEntity::class:
return $entityMock;
case IEntityEvent::class:
return $eventEntityMock;
case ICheck::class:
return $checkMock;
default:
return $this->createMock($className);
}
});
try {
$this->manager->validateOperation(IOperation::class, 'test', [$check], $operationData, IEntity::class, ['MyEvent']);
} catch (\UnexpectedValueException $e) {
$this->assertSame('The provided operation data is too long', $e->getMessage());
}
}
} }

View File

@ -35,6 +35,16 @@ interface IManager {
public const SCOPE_ADMIN = 0; public const SCOPE_ADMIN = 0;
public const SCOPE_USER = 1; public const SCOPE_USER = 1;
/**
* @since 21.0.0
*/
public const MAX_CHECK_VALUE_BYTES = 2048;
/**
* @since 21.0.0
*/
public const MAX_OPERATION_VALUE_BYTES = 4096;
/** /**
* @depreacted Will be removed in NC19. Use the dedicated events in OCP\WorkflowEngine\Events * @depreacted Will be removed in NC19. Use the dedicated events in OCP\WorkflowEngine\Events
*/ */