diff --git a/Router/RouteVerb.php b/Router/RouteVerb.php index de711f233..a103877e2 100644 --- a/Router/RouteVerb.php +++ b/Router/RouteVerb.php @@ -12,7 +12,7 @@ */ declare(strict_types=1); -namespace phpOMS\Router; +namespace phpOMS\WebRouter; use phpOMS\Stdlib\Base\Enum; diff --git a/Router/RouterInterface.php b/Router/RouterInterface.php new file mode 100644 index 000000000..5ace44809 --- /dev/null +++ b/Router/RouterInterface.php @@ -0,0 +1,37 @@ + [ + * 'dest' => '{DESTINATION_NAMESPACE:method}', // can also be static by using :: between namespace and function name + * 'permission' => [ // optional + * 'module' => '{MODULE_NAME}', + * 'type' => PermissionType::{TYPE}, + * 'state' => PermissionState::{STATE}, + * ], + * // define different destination for different verb + * ], + * // define another regex path, destination, permission here + * ]; + * + * @param string $path Route file path + * + * @return bool + * + * @since 1.0.0 + */ + public function importFromFile(string $path) : bool + { + if (!\file_exists($path)) { + return false; + } + + /** @noinspection PhpIncludeInspection */ + $this->routes += include $path; + + return true; + } + + /** + * Add route. + * + * @param string $route Route regex + * @param mixed $destination Destination e.g. Module:function string or callback + * + * @return void + * + * @since 1.0.0 + */ + public function add(string $route, $destination) : void + { + if (!isset($this->routes[$route])) { + $this->routes[$route] = []; + } + + $this->routes[$route][] = [ + 'dest' => $destination, + ]; + } + + /** + * Route request. + * + * @param string $uri Route + * @param string $app Application name + * @param int $orgId Organization id + * @param mixed $account Account + * + * @return array[] + * + * @since 1.0.0 + */ + public function route( + string $uri, + string $app = null, + int $orgId = null, + $account = null + ) : array + { + $bound = []; + foreach ($this->routes as $route => $destination) { + if (!((bool) \preg_match('~^' . $route . '$~', $uri))) { + continue; + } + + foreach ($destination as $d) { + // if permission check is invalid + if ((isset($d['permission']) && $account === null) + || (isset($d['permission']) + && !$account->hasPermission( + $d['permission']['type'], $orgId, $app, $d['permission']['module'], $d['permission']['state'] + ) + ) + ) { + return $app !== null ? $this->route('/' . \strtolower($app) . '/e403') : $this->route('/e403'); + } + + $bound[] = ['dest' => $d['dest']]; + } + } + + return $bound; + } +} diff --git a/Router/Router.php b/Router/WebRouter.php similarity index 96% rename from Router/Router.php rename to Router/WebRouter.php index 31525a470..6f314b032 100644 --- a/Router/Router.php +++ b/Router/WebRouter.php @@ -14,18 +14,17 @@ declare(strict_types=1); namespace phpOMS\Router; -use phpOMS\Message\Http\Request; -use phpOMS\Uri\Http; +use phpOMS\DataStorage\Database\Schema\Grammar\RouterInterface; /** - * Router class. + * Router class for web routes. * * @package phpOMS\Router * @license OMS License 1.0 * @link https://orange-management.org * @since 1.0.0 */ -final class Router +final class WebRouter implements RouterInterface { /** diff --git a/tests/Router/RouteVerbTest.php b/tests/Router/RouteVerbTest.php index d1e4eae92..888cf8a84 100644 --- a/tests/Router/RouteVerbTest.php +++ b/tests/Router/RouteVerbTest.php @@ -12,7 +12,7 @@ */ declare(strict_types=1); -namespace phpOMS\tests\Router; +namespace phpOMS\tests\WebRouter; require_once __DIR__ . '/../Autoloader.php'; diff --git a/tests/Router/SocketRouterTest.php b/tests/Router/SocketRouterTest.php new file mode 100644 index 000000000..e2d65428b --- /dev/null +++ b/tests/Router/SocketRouterTest.php @@ -0,0 +1,163 @@ +route('some_test route')); + } + + public function testInvalidRoutingFile() : void + { + $router = new SocketRouter(); + self::assertFalse($router->importFromFile(__Dir__ . '/invalidFile.php')); + } + + public function testLoadingRoutesFromFile() : void + { + $router = new SocketRouter(); + self::assertTrue($router->importFromFile(__Dir__ . '/socketRouterTestFile.php')); + } + + public function testRouteMatching() : void + { + $router = new SocketRouter(); + self::assertTrue($router->importFromFile(__Dir__ . '/socketRouterTestFile.php')); + + self::assertEquals( + [['dest' => '\Modules\Admin\Controller:viewSettingsGeneral']], + $router->route('backend_admin -settings=general -t 123') + ); + } + + public function testDynamicRouteAdding() : void + { + $router = new SocketRouter(); + self::assertNotEquals( + [['dest' => '\Modules\Admin\Controller:viewSettingsGeneral']], + $router->route('backends_admin -settings=general -t 123') + ); + + $router->add('^.*backends_admin -settings=general.*$', 'Controller:test'); + self::assertEquals( + [['dest' => 'Controller:test']], + $router->route('backends_admin -settings=general -t 123') + ); + } + + public function testWithValidPermissions() : void + { + $router = new SocketRouter(); + self::assertTrue($router->importFromFile(__Dir__ . '/socketRouterTestFilePermission.php')); + + $perm = new class( + null, + null, + BackendController::MODULE_NAME, + 0, + PermissionState::SETTINGS, + null, + null, + PermissionType::READ + ) extends PermissionAbstract {}; + + $account = new Account(); + $account->addPermission($perm); + + self::assertEquals( + [['dest' => '\Modules\Admin\Controller:viewSettingsGeneral']], + $router->route('backend_admin -settings=general -t 123', + null, + null, + $account + ) + ); + } + + public function testWithInvalidPermissions() : void + { + $router = new SocketRouter(); + self::assertTrue($router->importFromFile(__Dir__ . '/socketRouterTestFilePermission.php')); + + $perm2 = new class( + null, + null, + BackendController::MODULE_NAME, + 0, + PermissionState::SETTINGS, + null, + null, + PermissionType::CREATE + ) extends PermissionAbstract {}; + + $perm3 = new class( + null, + null, + 'InvalidModule', + 0, + PermissionState::SETTINGS, + null, + null, + PermissionType::READ + ) extends PermissionAbstract {}; + + $perm4 = new class( + null, + null, + BackendController::MODULE_NAME, + 0, + 99, + null, + null, + PermissionType::READ + ) extends PermissionAbstract {}; + + $account2 = new Account(); + $account2->addPermission($perm2); + $account2->addPermission($perm3); + $account2->addPermission($perm4); + + self::assertNotEquals( + [['dest' => '\Modules\Admin\Controller:viewSettingsGeneral']], + $router->route('backend_admin -settings=general -t 123', + null, + null, + $account2 + ) + ); + } +} diff --git a/tests/Router/RouterTest.php b/tests/Router/WebRouterTest.php similarity index 76% rename from tests/Router/RouterTest.php rename to tests/Router/WebRouterTest.php index 70e45c97d..1037a520a 100644 --- a/tests/Router/RouterTest.php +++ b/tests/Router/WebRouterTest.php @@ -20,7 +20,7 @@ use phpOMS\Account\Account; use phpOMS\Account\PermissionAbstract; use phpOMS\Account\PermissionType; use phpOMS\Message\Http\Request; -use phpOMS\Router\Router; +use phpOMS\Router\WebRouter; use phpOMS\Router\RouteVerb; use phpOMS\Uri\Http; @@ -29,18 +29,18 @@ require_once __DIR__ . '/../Autoloader.php'; /** * @internal */ -class RouterTest extends \PHPUnit\Framework\TestCase +class WebRouterTest extends \PHPUnit\Framework\TestCase { public function testAttributes() : void { - $router = new Router(); - self::assertInstanceOf('\phpOMS\Router\Router', $router); + $router = new WebRouter(); + self::assertInstanceOf('\phpOMS\Router\WebRouter', $router); self::assertObjectHasAttribute('routes', $router); } public function testDefault() : void { - $router = new Router(); + $router = new WebRouter(); self::assertEmpty( $router->route( (new Request(new Http('')))->getUri()->getRoute() @@ -48,11 +48,22 @@ class RouterTest extends \PHPUnit\Framework\TestCase ); } - public function testGetSet() : void + public function testInvalidRoutingFile() : void { - $router = new Router(); + $router = new WebRouter(); self::assertFalse($router->importFromFile(__Dir__ . '/invalidFile.php')); - self::assertTrue($router->importFromFile(__Dir__ . '/routerTestFile.php')); + } + + public function testLoadingRoutesFromFile() : void + { + $router = new WebRouter(); + self::assertTrue($router->importFromFile(__Dir__ . '/webRouterTestFile.php')); + } + + public function testRouteMatching() : void + { + $router = new WebRouter(); + self::assertTrue($router->importFromFile(__Dir__ . '/webRouterTestFile.php')); self::assertEquals( [['dest' => '\Modules\Admin\Controller:viewSettingsGeneral']], @@ -70,6 +81,25 @@ class RouterTest extends \PHPUnit\Framework\TestCase new Http('http://test.com/backend/admin/settings/general/something?test') ))->getUri()->getRoute(), null, RouteVerb::PUT) ); + } + + public function testRouteMissMatchingForInvalidVerbs() : void + { + $router = new WebRouter(); + self::assertTrue($router->importFromFile(__Dir__ . '/webRouterTestFile.php')); + + self::assertNotEquals( + [['dest' => '\Modules\Admin\Controller:viewSettingsGeneral']], + $router->route( + (new Request( + new Http('http://test.com/backend/admin/settings/general/something?test') + ))->getUri()->getRoute(), null, RouteVerb::PUT) + ); + } + + public function testDynamicRouteAdding() : void + { + $router = new WebRouter(); self::assertNotEquals( [['dest' => '\Modules\Admin\Controller:viewSettingsGeneral']], @@ -107,8 +137,8 @@ class RouterTest extends \PHPUnit\Framework\TestCase public function testWithCSRF() : void { - $router = new Router(); - self::assertTrue($router->importFromFile(__Dir__ . '/routeTestCsrf.php')); + $router = new WebRouter(); + self::assertTrue($router->importFromFile(__Dir__ . '/webRouteTestCsrf.php')); self::assertEquals( [['dest' => '\Modules\Admin\Controller:viewCsrf']], @@ -130,10 +160,10 @@ class RouterTest extends \PHPUnit\Framework\TestCase ); } - public function testWithPermissions() : void + public function testWithValidPermissions() : void { - $router = new Router(); - self::assertTrue($router->importFromFile(__Dir__ . '/routerTestFilePermission.php')); + $router = new WebRouter(); + self::assertTrue($router->importFromFile(__Dir__ . '/webRouterTestFilePermission.php')); $perm = new class( null, @@ -160,6 +190,12 @@ class RouterTest extends \PHPUnit\Framework\TestCase $account ) ); + } + + public function testWithInvalidPermissions() : void + { + $router = new WebRouter(); + self::assertTrue($router->importFromFile(__Dir__ . '/webRouterTestFilePermission.php')); $perm2 = new class( null, diff --git a/tests/Router/socketRouterTestFile.php b/tests/Router/socketRouterTestFile.php new file mode 100644 index 000000000..70fccbdb6 --- /dev/null +++ b/tests/Router/socketRouterTestFile.php @@ -0,0 +1,8 @@ + [ + 0 => [ + 'dest' => '\Modules\Admin\Controller:viewSettingsGeneral', + ], + ], +]; diff --git a/tests/Router/socketRouterTestFilePermission.php b/tests/Router/socketRouterTestFilePermission.php new file mode 100644 index 000000000..8d36f83fd --- /dev/null +++ b/tests/Router/socketRouterTestFilePermission.php @@ -0,0 +1,18 @@ + [ + 0 => [ + 'dest' => '\Modules\Admin\Controller:viewSettingsGeneral', + 'permission' => [ + 'module' => BackendController::MODULE_NAME, + 'type' => PermissionType::READ, + 'state' => PermissionState::SETTINGS, + ], + ], + ], +]; diff --git a/tests/Router/routeTestCsrf.php b/tests/Router/webRouteTestCsrf.php similarity index 100% rename from tests/Router/routeTestCsrf.php rename to tests/Router/webRouteTestCsrf.php diff --git a/tests/Router/routerTestFile.php b/tests/Router/webRouterTestFile.php similarity index 100% rename from tests/Router/routerTestFile.php rename to tests/Router/webRouterTestFile.php diff --git a/tests/Router/routerTestFilePermission.php b/tests/Router/webRouterTestFilePermission.php similarity index 100% rename from tests/Router/routerTestFilePermission.php rename to tests/Router/webRouterTestFilePermission.php