From 996fc7d43361720e3749401fd0f005ed3d97404b Mon Sep 17 00:00:00 2001
From: Tobias Gross-Vogt <tgros@tugraz.at>
Date: Wed, 23 Nov 2022 13:35:13 +0100
Subject: [PATCH] extending the authorization expression language

---
 .../FilterExpressionFunctionProvider.php      | 40 +++++++++++++++++++
 .../MapExpressionFunctionProvider.php         | 38 ++++++++++++++++++
 .../PhpArrayExpressionFunctionProvider.php    | 20 ++++++++++
 .../PhpNumericExpressionFunctionProvider.php  | 22 ++++++++++
 .../PhpStringExpressionFunctionProvider.php   | 22 ++++++++++
 .../ExpressionLanguage/ExpressionLanguage.php | 29 ++++++++++++++
 .../UserAuthorizationChecker.php              |  2 +-
 7 files changed, 172 insertions(+), 1 deletion(-)
 create mode 100644 src/Authorization/ExpressionLanguage/ExpressionFunctionProviders/FilterExpressionFunctionProvider.php
 create mode 100644 src/Authorization/ExpressionLanguage/ExpressionFunctionProviders/MapExpressionFunctionProvider.php
 create mode 100644 src/Authorization/ExpressionLanguage/ExpressionFunctionProviders/PhpArrayExpressionFunctionProvider.php
 create mode 100644 src/Authorization/ExpressionLanguage/ExpressionFunctionProviders/PhpNumericExpressionFunctionProvider.php
 create mode 100644 src/Authorization/ExpressionLanguage/ExpressionFunctionProviders/PhpStringExpressionFunctionProvider.php
 create mode 100644 src/Authorization/ExpressionLanguage/ExpressionLanguage.php

diff --git a/src/Authorization/ExpressionLanguage/ExpressionFunctionProviders/FilterExpressionFunctionProvider.php b/src/Authorization/ExpressionLanguage/ExpressionFunctionProviders/FilterExpressionFunctionProvider.php
new file mode 100644
index 0000000..54bb280
--- /dev/null
+++ b/src/Authorization/ExpressionLanguage/ExpressionFunctionProviders/FilterExpressionFunctionProvider.php
@@ -0,0 +1,40 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Dbp\Relay\CoreBundle\Authorization\ExpressionLanguage\ExpressionFunctionProviders;
+
+use Symfony\Component\ExpressionLanguage\ExpressionFunction;
+use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
+use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
+
+class FilterExpressionFunctionProvider implements ExpressionFunctionProviderInterface
+{
+    /** @var ExpressionLanguage */
+    private $expressionLanguage;
+
+    public function __construct(ExpressionLanguage $expressionLanguage)
+    {
+        $this->expressionLanguage = $expressionLanguage;
+    }
+
+    public function getFunctions(): array
+    {
+        return [
+            new ExpressionFunction('filter',
+                function (string $iterableName, string $expression): string {
+                    return sprintf('filter(%s, %s)', $iterableName, $expression);
+                },
+                function ($arguments, iterable $iterable, string $expression): array {
+                    $filteredResult = [];
+                    foreach ($iterable as $key => $value) {
+                        if ($this->expressionLanguage->evaluate($expression, ['key' => $key, 'value' => $value])) {
+                            $filteredResult[] = $value;
+                        }
+                    }
+
+                    return $filteredResult;
+                }),
+        ];
+    }
+}
diff --git a/src/Authorization/ExpressionLanguage/ExpressionFunctionProviders/MapExpressionFunctionProvider.php b/src/Authorization/ExpressionLanguage/ExpressionFunctionProviders/MapExpressionFunctionProvider.php
new file mode 100644
index 0000000..e1d7b00
--- /dev/null
+++ b/src/Authorization/ExpressionLanguage/ExpressionFunctionProviders/MapExpressionFunctionProvider.php
@@ -0,0 +1,38 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Dbp\Relay\CoreBundle\Authorization\ExpressionLanguage\ExpressionFunctionProviders;
+
+use Symfony\Component\ExpressionLanguage\ExpressionFunction;
+use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
+use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
+
+class MapExpressionFunctionProvider implements ExpressionFunctionProviderInterface
+{
+    /** @var ExpressionLanguage */
+    private $expressionLanguage;
+
+    public function __construct(ExpressionLanguage $expressionLanguage)
+    {
+        $this->expressionLanguage = $expressionLanguage;
+    }
+
+    public function getFunctions(): array
+    {
+        return [
+            new ExpressionFunction('map',
+                function (string $iterableName, string $expression): string {
+                    return sprintf('map(%s, %s)', $iterableName, $expression);
+                },
+                function ($arguments, iterable $iterable, string $expression): array {
+                    $transformedResult = [];
+                    foreach ($iterable as $key => $value) {
+                        $transformedResult[$key] = $this->expressionLanguage->evaluate($expression, ['key' => $key, 'value' => $value]);
+                    }
+
+                    return $transformedResult;
+                }),
+        ];
+    }
+}
diff --git a/src/Authorization/ExpressionLanguage/ExpressionFunctionProviders/PhpArrayExpressionFunctionProvider.php b/src/Authorization/ExpressionLanguage/ExpressionFunctionProviders/PhpArrayExpressionFunctionProvider.php
new file mode 100644
index 0000000..db907ca
--- /dev/null
+++ b/src/Authorization/ExpressionLanguage/ExpressionFunctionProviders/PhpArrayExpressionFunctionProvider.php
@@ -0,0 +1,20 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Dbp\Relay\CoreBundle\Authorization\ExpressionLanguage\ExpressionFunctionProviders;
+
+use Symfony\Component\ExpressionLanguage\ExpressionFunction;
+use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
+
+class PhpArrayExpressionFunctionProvider implements ExpressionFunctionProviderInterface
+{
+    public function getFunctions(): array
+    {
+        return [
+            ExpressionFunction::fromPhp('count'),
+            ExpressionFunction::fromPhp('implode'),
+            ExpressionFunction::fromPhp('explode'),
+        ];
+    }
+}
diff --git a/src/Authorization/ExpressionLanguage/ExpressionFunctionProviders/PhpNumericExpressionFunctionProvider.php b/src/Authorization/ExpressionLanguage/ExpressionFunctionProviders/PhpNumericExpressionFunctionProvider.php
new file mode 100644
index 0000000..ca7ec43
--- /dev/null
+++ b/src/Authorization/ExpressionLanguage/ExpressionFunctionProviders/PhpNumericExpressionFunctionProvider.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Dbp\Relay\CoreBundle\Authorization\ExpressionLanguage\ExpressionFunctionProviders;
+
+use Symfony\Component\ExpressionLanguage\ExpressionFunction;
+use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
+
+class PhpNumericExpressionFunctionProvider implements ExpressionFunctionProviderInterface
+{
+    public function getFunctions(): array
+    {
+        return [
+            ExpressionFunction::fromPhp('ceil'),
+            ExpressionFunction::fromPhp('floor'),
+            ExpressionFunction::fromPhp('round'),
+            ExpressionFunction::fromPhp('max'),
+            ExpressionFunction::fromPhp('min'),
+        ];
+    }
+}
diff --git a/src/Authorization/ExpressionLanguage/ExpressionFunctionProviders/PhpStringExpressionFunctionProvider.php b/src/Authorization/ExpressionLanguage/ExpressionFunctionProviders/PhpStringExpressionFunctionProvider.php
new file mode 100644
index 0000000..1412daf
--- /dev/null
+++ b/src/Authorization/ExpressionLanguage/ExpressionFunctionProviders/PhpStringExpressionFunctionProvider.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Dbp\Relay\CoreBundle\Authorization\ExpressionLanguage\ExpressionFunctionProviders;
+
+use Symfony\Component\ExpressionLanguage\ExpressionFunction;
+use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
+
+class PhpStringExpressionFunctionProvider implements ExpressionFunctionProviderInterface
+{
+    public function getFunctions(): array
+    {
+        return [
+            ExpressionFunction::fromPhp('str_starts_with'),
+            ExpressionFunction::fromPhp('str_ends_with'),
+            ExpressionFunction::fromPhp('substr'),
+            ExpressionFunction::fromPhp('strpos'),
+            ExpressionFunction::fromPhp('strlen'),
+        ];
+    }
+}
diff --git a/src/Authorization/ExpressionLanguage/ExpressionLanguage.php b/src/Authorization/ExpressionLanguage/ExpressionLanguage.php
new file mode 100644
index 0000000..a997ab8
--- /dev/null
+++ b/src/Authorization/ExpressionLanguage/ExpressionLanguage.php
@@ -0,0 +1,29 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Dbp\Relay\CoreBundle\Authorization\ExpressionLanguage;
+
+use Dbp\Relay\CoreBundle\Authorization\ExpressionLanguage\ExpressionFunctionProviders\FilterExpressionFunctionProvider;
+use Dbp\Relay\CoreBundle\Authorization\ExpressionLanguage\ExpressionFunctionProviders\MapExpressionFunctionProvider;
+use Dbp\Relay\CoreBundle\Authorization\ExpressionLanguage\ExpressionFunctionProviders\PhpArrayExpressionFunctionProvider;
+use Dbp\Relay\CoreBundle\Authorization\ExpressionLanguage\ExpressionFunctionProviders\PhpNumericExpressionFunctionProvider;
+use Dbp\Relay\CoreBundle\Authorization\ExpressionLanguage\ExpressionFunctionProviders\PhpStringExpressionFunctionProvider;
+use Psr\Cache\CacheItemPoolInterface;
+use Symfony\Component\ExpressionLanguage\ExpressionLanguage as SymfonyExpressionLanguage;
+
+class ExpressionLanguage extends SymfonyExpressionLanguage
+{
+    public function __construct(CacheItemPoolInterface $cache = null, array $providers = [])
+    {
+        $providers = array_merge([
+            new FilterExpressionFunctionProvider($this),
+            new MapExpressionFunctionProvider($this),
+            new PhpArrayExpressionFunctionProvider(),
+            new PhpNumericExpressionFunctionProvider(),
+            new PhpStringExpressionFunctionProvider(),
+        ], $providers);
+
+        parent::__construct($cache, $providers);
+    }
+}
diff --git a/src/Authorization/UserAuthorizationChecker.php b/src/Authorization/UserAuthorizationChecker.php
index dda9986..ba961d6 100644
--- a/src/Authorization/UserAuthorizationChecker.php
+++ b/src/Authorization/UserAuthorizationChecker.php
@@ -4,8 +4,8 @@ declare(strict_types=1);
 
 namespace Dbp\Relay\CoreBundle\Authorization;
 
+use Dbp\Relay\CoreBundle\Authorization\ExpressionLanguage\ExpressionLanguage;
 use Dbp\Relay\CoreBundle\Helpers\Tools;
-use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
 
 class UserAuthorizationChecker
 {
-- 
GitLab