diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index dc5f9dc9e8e05..9dd36283d22ae 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -36,6 +36,10 @@ 'OCP\\AppFramework\\Db\\MultipleObjectsReturnedException' => $baseDir . '/lib/public/AppFramework/Db/MultipleObjectsReturnedException.php', 'OCP\\AppFramework\\Db\\QBMapper' => $baseDir . '/lib/public/AppFramework/Db/QBMapper.php', 'OCP\\AppFramework\\Http' => $baseDir . '/lib/public/AppFramework/Http.php', + 'OCP\\AppFramework\\Http\\Attribute\\NoAdminRequired' => $baseDir . '/lib/public/AppFramework/Http/Attribute/NoAdminRequired.php', + 'OCP\\AppFramework\\Http\\Attribute\\NoCSRFRequired' => $baseDir . '/lib/public/AppFramework/Http/Attribute/NoCSRFRequired.php', + 'OCP\\AppFramework\\Http\\Attribute\\PublicPage' => $baseDir . '/lib/public/AppFramework/Http/Attribute/PublicPage.php', + 'OCP\\AppFramework\\Http\\Attribute\\SubAdminRequired' => $baseDir . '/lib/public/AppFramework/Http/Attribute/SubAdminRequired.php', 'OCP\\AppFramework\\Http\\ContentSecurityPolicy' => $baseDir . '/lib/public/AppFramework/Http/ContentSecurityPolicy.php', 'OCP\\AppFramework\\Http\\DataDisplayResponse' => $baseDir . '/lib/public/AppFramework/Http/DataDisplayResponse.php', 'OCP\\AppFramework\\Http\\DataDownloadResponse' => $baseDir . '/lib/public/AppFramework/Http/DataDownloadResponse.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 455fb01b18eee..d9bf826be3eed 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -65,6 +65,10 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OCP\\AppFramework\\Db\\MultipleObjectsReturnedException' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Db/MultipleObjectsReturnedException.php', 'OCP\\AppFramework\\Db\\QBMapper' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Db/QBMapper.php', 'OCP\\AppFramework\\Http' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http.php', + 'OCP\\AppFramework\\Http\\Attribute\\NoAdminRequired' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/NoAdminRequired.php', + 'OCP\\AppFramework\\Http\\Attribute\\NoCSRFRequired' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/NoCSRFRequired.php', + 'OCP\\AppFramework\\Http\\Attribute\\PublicPage' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/PublicPage.php', + 'OCP\\AppFramework\\Http\\Attribute\\SubAdminRequired' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/SubAdminRequired.php', 'OCP\\AppFramework\\Http\\ContentSecurityPolicy' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/ContentSecurityPolicy.php', 'OCP\\AppFramework\\Http\\DataDisplayResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/DataDisplayResponse.php', 'OCP\\AppFramework\\Http\\DataDownloadResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/DataDownloadResponse.php', diff --git a/lib/private/AppFramework/Utility/ControllerMethodReflector.php b/lib/private/AppFramework/Utility/ControllerMethodReflector.php index 77417974d9aff..c6f6b626d4022 100644 --- a/lib/private/AppFramework/Utility/ControllerMethodReflector.php +++ b/lib/private/AppFramework/Utility/ControllerMethodReflector.php @@ -34,23 +34,31 @@ namespace OC\AppFramework\Utility; +use Attribute; use OCP\AppFramework\Utility\IControllerMethodReflector; +use ReflectionAttribute; +use ReflectionMethod; +use RuntimeException; +use function array_map; /** * Reads and parses annotations from doc comments */ class ControllerMethodReflector implements IControllerMethodReflector { - public $annotations = []; + private $annotations = []; private $types = []; private $parameters = []; + /** @var ReflectionMethod|null */ + private $reflectedMethod; + /** * @param object $object an object or classname * @param string $method the method which we want to inspect */ public function reflect($object, string $method) { - $reflection = new \ReflectionMethod($object, $method); - $docs = $reflection->getDocComment(); + $this->reflectedMethod = new \ReflectionMethod($object, $method); + $docs = $this->reflectedMethod->getDocComment(); if ($docs !== false) { // extract everything prefixed by @ and first letter uppercase @@ -77,7 +85,7 @@ public function reflect($object, string $method) { $this->types = array_combine($matches['var'], $matches['type']); } - foreach ($reflection->getParameters() as $param) { + foreach ($this->reflectedMethod->getParameters() as $param) { // extract type information from PHP 7 scalar types and prefer them over phpdoc annotations $type = $param->getType(); if ($type instanceof \ReflectionNamedType) { @@ -118,6 +126,7 @@ public function getParameters(): array { * Check if a method contains an annotation * @param string $name the name of the annotation * @return bool true if the annotation is found + * @deprecated 22.0.0 php8 attributes replace phpdoc annotations */ public function hasAnnotation(string $name): bool { $name = strtolower($name); @@ -130,6 +139,7 @@ public function hasAnnotation(string $name): bool { * @param string $name the name of the annotation * @param string $key the string of the annotation * @return string + * @deprecated 22.0.0 php8 attributes replace phpdoc annotations */ public function getAnnotationParameter(string $name, string $key): string { $name = strtolower($name); @@ -139,4 +149,28 @@ public function getAnnotationParameter(string $name, string $key): string { return ''; } + + public function hasAttribute(string $attributeClass): bool { + if ($this->reflectedMethod === null) { + throw new RuntimeException('No method reflected'); + } + if (PHP_VERSION_ID < 80000) { + return false; + } + + return !empty($this->reflectedMethod->getAttributes($attributeClass)); + } + + public function getAttributeInstances(?string $attributeClass = null): array { + if ($this->reflectedMethod === null) { + throw new RuntimeException('No method reflected'); + } + if (PHP_VERSION_ID < 80000) { + return []; + } + + return array_map(function(ReflectionAttribute $reflectionAttribute) { + return $reflectionAttribute->newInstance(); + }, $this->reflectedMethod->getAttributes($attributeClass)) ; + } } diff --git a/lib/public/AppFramework/Http/Attribute/NoAdminRequired.php b/lib/public/AppFramework/Http/Attribute/NoAdminRequired.php new file mode 100644 index 0000000000000..e1db26de26605 --- /dev/null +++ b/lib/public/AppFramework/Http/Attribute/NoAdminRequired.php @@ -0,0 +1,38 @@ + + * + * @author 2021 Christoph Wurst + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace OCP\AppFramework\Http\Attribute; + +use Attribute; + +/** + * Signals that a controller method can be accessed without admin privilege + * + * @since 22.0.0 + */ +#[Attribute] +class NoAdminRequired { + +} diff --git a/lib/public/AppFramework/Http/Attribute/NoCSRFRequired.php b/lib/public/AppFramework/Http/Attribute/NoCSRFRequired.php new file mode 100644 index 0000000000000..b91ac0b415ac0 --- /dev/null +++ b/lib/public/AppFramework/Http/Attribute/NoCSRFRequired.php @@ -0,0 +1,38 @@ + + * + * @author 2021 Christoph Wurst + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace OCP\AppFramework\Http\Attribute; + +use Attribute; + +/** + * Signals that a controller method does not need a valid CSRF token + * + * @since 22.0.0 + */ +#[Attribute] +class NoCSRFRequired { + +} diff --git a/lib/public/AppFramework/Http/Attribute/PublicPage.php b/lib/public/AppFramework/Http/Attribute/PublicPage.php new file mode 100644 index 0000000000000..82d2170ff2450 --- /dev/null +++ b/lib/public/AppFramework/Http/Attribute/PublicPage.php @@ -0,0 +1,38 @@ + + * + * @author 2021 Christoph Wurst + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace OCP\AppFramework\Http\Attribute; + +use Attribute; + +/** + * Signals that a controller method can be accessed without authentication + * + * @since 22.0.0 + */ +#[Attribute] +class PublicPage { + +} diff --git a/lib/public/AppFramework/Http/Attribute/SubAdminRequired.php b/lib/public/AppFramework/Http/Attribute/SubAdminRequired.php new file mode 100644 index 0000000000000..db2610be92a97 --- /dev/null +++ b/lib/public/AppFramework/Http/Attribute/SubAdminRequired.php @@ -0,0 +1,38 @@ + + * + * @author 2021 Christoph Wurst + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace OCP\AppFramework\Http\Attribute; + +use Attribute; + +/** + * Signals that a controller method needs admin or sub admin privilege to be called + * + * @since 22.0.0 + */ +#[Attribute] +class SubAdminRequired { + +} diff --git a/lib/public/AppFramework/Utility/IControllerMethodReflector.php b/lib/public/AppFramework/Utility/IControllerMethodReflector.php index 05851ad668f25..bf6a071e9c912 100644 --- a/lib/public/AppFramework/Utility/IControllerMethodReflector.php +++ b/lib/public/AppFramework/Utility/IControllerMethodReflector.php @@ -28,6 +28,9 @@ namespace OCP\AppFramework\Utility; +use Attribute; +use RuntimeException; + /** * Interface ControllerMethodReflector * @@ -69,6 +72,33 @@ public function getParameters(): array; * @param string $name the name of the annotation * @return bool true if the annotation is found * @since 8.0.0 + * @deprecated 22.0.0 php8 attributes replace phpdoc annotations */ public function hasAnnotation(string $name): bool; + + /** + * Check if a method has the specified attribute + * + * For PHP older than 8.0 this will always return false due to the lack of attribute support + * + * @param string $attributeClass + * @psalm-param class-string $attributeClass + * @return bool + * @throws RuntimeException if no method was reflected before calling this method + * @since 22.0.0 + */ + public function hasAttribute(string $attributeClass): bool; + + /** + * Get an attribute of the reflected method, if specified + * + * For PHP older than 8.0 this will always return an empty array due to the lack of attribute support + * + * @param string|null $attributeClass + * @psalm-param class-string $attributeClass + * @return Attribute[] + * @throws RuntimeException if no method was reflected before calling this method + * @since 22.0.0 + */ + public function getAttributeInstances(?string $attributeClass = null): array; }