aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoas Schilling <coding@schilljs.com>2023-02-15 13:20:20 +0100
committerJoas Schilling <coding@schilljs.com>2023-02-23 06:13:00 +0100
commitc6700150d53423f64c7608f757d3839b84e3932a (patch)
tree43a686bcfb2aeb9297bdbd6f476621240128798c
parent1d211a4b8923ea007ec974933e590906d6d06091 (diff)
downloadnextcloud-server-c6700150d53423f64c7608f757d3839b84e3932a.tar.gz
nextcloud-server-c6700150d53423f64c7608f757d3839b84e3932a.zip
Validate the scope when validating operations
Signed-off-by: Joas Schilling <coding@schilljs.com>
-rw-r--r--apps/workflowengine/lib/Manager.php13
-rw-r--r--apps/workflowengine/tests/ManagerTest.php113
2 files changed, 118 insertions, 8 deletions
diff --git a/apps/workflowengine/lib/Manager.php b/apps/workflowengine/lib/Manager.php
index 417e1a1eba6..7ef913bb1a5 100644
--- a/apps/workflowengine/lib/Manager.php
+++ b/apps/workflowengine/lib/Manager.php
@@ -323,7 +323,7 @@ class Manager implements IManager {
string $entity,
array $events
) {
- $this->validateOperation($class, $name, $checks, $operation, $entity, $events);
+ $this->validateOperation($class, $name, $checks, $operation, $scope, $entity, $events);
$this->connection->beginTransaction();
@@ -396,7 +396,7 @@ class Manager implements IManager {
throw new \DomainException('Target operation not within scope');
};
$row = $this->getOperation($id);
- $this->validateOperation($row['class'], $name, $checks, $operation, $entity, $events);
+ $this->validateOperation($row['class'], $name, $checks, $operation, $scopeContext, $entity, $events);
$checkIds = [];
try {
@@ -499,9 +499,12 @@ class Manager implements IManager {
* @param string $name
* @param array[] $checks
* @param string $operation
+ * @param ScopeContext $scope
+ * @param string $entity
+ * @param array $events
* @throws \UnexpectedValueException
*/
- public function validateOperation($class, $name, array $checks, $operation, string $entity, array $events) {
+ public function validateOperation($class, $name, array $checks, $operation, ScopeContext $scope, string $entity, array $events) {
try {
/** @var IOperation $instance */
$instance = $this->container->query($class);
@@ -513,6 +516,10 @@ class Manager implements IManager {
throw new \UnexpectedValueException($this->l->t('Operation %s is invalid', [$class]));
}
+ if (!$instance->isAvailableForScope($scope->getScope())) {
+ throw new \UnexpectedValueException($this->l->t('Operation %s is invalid', [$class]));
+ }
+
$this->validateEvents($entity, $events, $instance);
if (count($checks) === 0) {
diff --git a/apps/workflowengine/tests/ManagerTest.php b/apps/workflowengine/tests/ManagerTest.php
index d3678b66bee..bb77377210e 100644
--- a/apps/workflowengine/tests/ManagerTest.php
+++ b/apps/workflowengine/tests/ManagerTest.php
@@ -334,11 +334,20 @@ class ManagerTest extends TestCase {
->with('events');
$this->cacheFactory->method('createDistributed')->willReturn($cache);
+ $operationMock = $this->createMock(IOperation::class);
+ $operationMock->expects($this->any())
+ ->method('isAvailableForScope')
+ ->withConsecutive(
+ [IManager::SCOPE_ADMIN],
+ [IManager::SCOPE_USER]
+ )
+ ->willReturn(true);
+
$this->container->expects($this->any())
->method('query')
- ->willReturnCallback(function ($class) {
+ ->willReturnCallback(function ($class) use ($operationMock) {
if (substr($class, -2) === 'Op') {
- return $this->createMock(IOperation::class);
+ return $operationMock;
} elseif ($class === File::class) {
return $this->getMockBuilder(File::class)
->setConstructorArgs([
@@ -505,6 +514,16 @@ class ManagerTest extends TestCase {
$entityMock = $this->createMock(IEntity::class);
$eventEntityMock = $this->createMock(IEntityEvent::class);
$checkMock = $this->createMock(ICheck::class);
+ $scopeMock = $this->createMock(ScopeContext::class);
+
+ $scopeMock->expects($this->any())
+ ->method('getScope')
+ ->willReturn(IManager::SCOPE_ADMIN);
+
+ $operationMock->expects($this->once())
+ ->method('isAvailableForScope')
+ ->with(IManager::SCOPE_ADMIN)
+ ->willReturn(true);
$operationMock->expects($this->once())
->method('validateOperation')
@@ -541,7 +560,7 @@ class ManagerTest extends TestCase {
}
});
- $this->manager->validateOperation(IOperation::class, 'test', [$check], 'operationData', IEntity::class, ['MyEvent']);
+ $this->manager->validateOperation(IOperation::class, 'test', [$check], 'operationData', $scopeMock, IEntity::class, ['MyEvent']);
}
public function testValidateOperationCheckInputLengthError() {
@@ -555,6 +574,16 @@ class ManagerTest extends TestCase {
$entityMock = $this->createMock(IEntity::class);
$eventEntityMock = $this->createMock(IEntityEvent::class);
$checkMock = $this->createMock(ICheck::class);
+ $scopeMock = $this->createMock(ScopeContext::class);
+
+ $scopeMock->expects($this->any())
+ ->method('getScope')
+ ->willReturn(IManager::SCOPE_ADMIN);
+
+ $operationMock->expects($this->once())
+ ->method('isAvailableForScope')
+ ->with(IManager::SCOPE_ADMIN)
+ ->willReturn(true);
$operationMock->expects($this->once())
->method('validateOperation')
@@ -592,7 +621,7 @@ class ManagerTest extends TestCase {
});
try {
- $this->manager->validateOperation(IOperation::class, 'test', [$check], 'operationData', IEntity::class, ['MyEvent']);
+ $this->manager->validateOperation(IOperation::class, 'test', [$check], 'operationData', $scopeMock, IEntity::class, ['MyEvent']);
} catch (\UnexpectedValueException $e) {
$this->assertSame('The provided check value is too long', $e->getMessage());
}
@@ -610,6 +639,16 @@ class ManagerTest extends TestCase {
$entityMock = $this->createMock(IEntity::class);
$eventEntityMock = $this->createMock(IEntityEvent::class);
$checkMock = $this->createMock(ICheck::class);
+ $scopeMock = $this->createMock(ScopeContext::class);
+
+ $scopeMock->expects($this->any())
+ ->method('getScope')
+ ->willReturn(IManager::SCOPE_ADMIN);
+
+ $operationMock->expects($this->once())
+ ->method('isAvailableForScope')
+ ->with(IManager::SCOPE_ADMIN)
+ ->willReturn(true);
$operationMock->expects($this->never())
->method('validateOperation');
@@ -646,9 +685,73 @@ class ManagerTest extends TestCase {
});
try {
- $this->manager->validateOperation(IOperation::class, 'test', [$check], $operationData, IEntity::class, ['MyEvent']);
+ $this->manager->validateOperation(IOperation::class, 'test', [$check], $operationData, $scopeMock, IEntity::class, ['MyEvent']);
} catch (\UnexpectedValueException $e) {
$this->assertSame('The provided operation data is too long', $e->getMessage());
}
}
+
+ public function testValidateOperationScopeNotAvailable() {
+ $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);
+ $scopeMock = $this->createMock(ScopeContext::class);
+
+ $scopeMock->expects($this->any())
+ ->method('getScope')
+ ->willReturn(IManager::SCOPE_ADMIN);
+
+ $operationMock->expects($this->once())
+ ->method('isAvailableForScope')
+ ->with(IManager::SCOPE_ADMIN)
+ ->willReturn(false);
+
+ $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, $scopeMock, IEntity::class, ['MyEvent']);
+ } catch (\UnexpectedValueException $e) {
+ $this->assertSame('Operation OCP\WorkflowEngine\IOperation is invalid', $e->getMessage());
+ }
+ }
}