Browse Source
Add well known handlers API
Add well known handlers API
Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>pull/24702/head
No known key found for this signature in database
GPG Key ID: CC42AC2A7F0E56D8
21 changed files with 1022 additions and 36 deletions
-
4.htaccess
-
8apps/settings/js/admin.js
-
21apps/settings/lib/AppInfo/Application.php
-
67core/Controller/WellKnownController.php
-
7core/js/setupchecks.js
-
8core/js/tests/specs/setupchecksSpec.js
-
3core/routes.php
-
7lib/composer/composer/autoload_classmap.php
-
7lib/composer/composer/autoload_static.php
-
24lib/private/AppFramework/Bootstrap/RegistrationContext.php
-
124lib/private/Http/WellKnown/RequestManager.php
-
14lib/public/AppFramework/Bootstrap/IRegistrationContext.php
-
51lib/public/Http/WellKnown/GenericResponse.php
-
48lib/public/Http/WellKnown/IHandler.php
-
46lib/public/Http/WellKnown/IRequestContext.php
-
39lib/public/Http/WellKnown/IResponse.php
-
170lib/public/Http/WellKnown/JrdResponse.php
-
86tests/Core/Controller/WellKnownControllerTest.php
-
40tests/lib/Http/WellKnown/GenericResponseTest.php
-
108tests/lib/Http/WellKnown/JrdResponseTest.php
-
176tests/lib/Http/WellKnown/RequestManagerTest.php
@ -0,0 +1,67 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
/* |
|||
* @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> |
|||
* |
|||
* @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at> |
|||
* |
|||
* @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 <http://www.gnu.org/licenses/>. |
|||
*/ |
|||
|
|||
namespace OC\Core\Controller; |
|||
|
|||
use OC\Http\WellKnown\RequestManager; |
|||
use OCP\AppFramework\Controller; |
|||
use OCP\AppFramework\Http; |
|||
use OCP\AppFramework\Http\JSONResponse; |
|||
use OCP\AppFramework\Http\Response; |
|||
use OCP\IRequest; |
|||
|
|||
class WellKnownController extends Controller { |
|||
|
|||
/** @var RequestManager */ |
|||
private $requestManager; |
|||
|
|||
public function __construct(IRequest $request, |
|||
RequestManager $wellKnownManager) { |
|||
parent::__construct('core', $request); |
|||
$this->requestManager = $wellKnownManager; |
|||
} |
|||
|
|||
/** |
|||
* @PublicPage |
|||
* @NoCSRFRequired |
|||
* |
|||
* @return Response |
|||
*/ |
|||
public function handle(string $service): Response { |
|||
$response = $this->requestManager->process( |
|||
$service, |
|||
$this->request |
|||
); |
|||
|
|||
if ($response === null) { |
|||
$httpResponse = new JSONResponse(["message" => "$service not supported"], Http::STATUS_NOT_FOUND); |
|||
} else { |
|||
$httpResponse = $response->toHttpResponse(); |
|||
} |
|||
|
|||
// We add a custom header so that setup checks can detect if their requests are answered by this controller
|
|||
return $httpResponse->addHeader('X-NEXTCLOUD-WELL-KNOWN', '1'); |
|||
} |
|||
} |
|||
@ -0,0 +1,124 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
/* |
|||
* @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> |
|||
* |
|||
* @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at> |
|||
* |
|||
* @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 <http://www.gnu.org/licenses/>. |
|||
*/ |
|||
|
|||
namespace OC\Http\WellKnown; |
|||
|
|||
use OC\AppFramework\Bootstrap\Coordinator; |
|||
use OCP\AppFramework\QueryException; |
|||
use OCP\Http\WellKnown\IHandler; |
|||
use OCP\Http\WellKnown\IRequestContext; |
|||
use OCP\Http\WellKnown\IResponse; |
|||
use OCP\Http\WellKnown\JrdResponse; |
|||
use OCP\IRequest; |
|||
use OCP\IServerContainer; |
|||
use Psr\Log\LoggerInterface; |
|||
use RuntimeException; |
|||
use function array_reduce; |
|||
|
|||
class RequestManager { |
|||
|
|||
/** @var Coordinator */ |
|||
private $coordinator; |
|||
|
|||
/** @var IServerContainer */ |
|||
private $container; |
|||
|
|||
/** @var LoggerInterface */ |
|||
private $logger; |
|||
|
|||
public function __construct(Coordinator $coordinator, |
|||
IServerContainer $container, |
|||
LoggerInterface $logger) { |
|||
$this->coordinator = $coordinator; |
|||
$this->container = $container; |
|||
$this->logger = $logger; |
|||
} |
|||
|
|||
public function process(string $service, IRequest $request): ?IResponse { |
|||
$handlers = $this->loadHandlers(); |
|||
$context = new class($request) implements IRequestContext { |
|||
/** @var IRequest */ |
|||
private $request; |
|||
|
|||
public function __construct(IRequest $request) { |
|||
$this->request = $request; |
|||
} |
|||
|
|||
public function getHttpRequest(): IRequest { |
|||
return $this->request; |
|||
} |
|||
}; |
|||
|
|||
$subject = $request->getParam('resource'); |
|||
$initialResponse = new JrdResponse($subject ?? ''); |
|||
$finalResponse = array_reduce($handlers, function (?IResponse $previousResponse, IHandler $handler) use ($context, $service) { |
|||
return $handler->handle($service, $context, $previousResponse); |
|||
}, $initialResponse); |
|||
|
|||
if ($finalResponse instanceof JrdResponse && $finalResponse->isEmpty()) { |
|||
return null; |
|||
} |
|||
|
|||
return $finalResponse; |
|||
} |
|||
|
|||
/** |
|||
* @return IHandler[] |
|||
*/ |
|||
private function loadHandlers(): array { |
|||
$context = $this->coordinator->getRegistrationContext(); |
|||
|
|||
if ($context === null) { |
|||
throw new RuntimeException("Well known handlers requested before the apps had been fully registered"); |
|||
} |
|||
|
|||
$registrations = $context->getWellKnownHandlers(); |
|||
$this->logger->debug(count($registrations) . " well known handlers registered"); |
|||
|
|||
return array_filter( |
|||
array_map(function (array $registration) { |
|||
$class = $registration['class']; |
|||
|
|||
try { |
|||
$handler = $this->container->get($class); |
|||
|
|||
if (!($handler) instanceof IHandler) { |
|||
$this->logger->error("Well known handler $class is invalid"); |
|||
|
|||
return null; |
|||
} |
|||
|
|||
return $handler; |
|||
} catch (QueryException $e) { |
|||
$this->logger->error("Could not load well known handler $class", [ |
|||
'exception' => $e, |
|||
]); |
|||
|
|||
return null; |
|||
} |
|||
}, $registrations) |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,51 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
/* |
|||
* @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> |
|||
* |
|||
* @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at> |
|||
* |
|||
* @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 <http://www.gnu.org/licenses/>. |
|||
*/ |
|||
|
|||
namespace OCP\Http\WellKnown; |
|||
|
|||
use OCP\AppFramework\Http\Response; |
|||
|
|||
/** |
|||
* @since 21.0.0 |
|||
*/ |
|||
final class GenericResponse implements IResponse { |
|||
|
|||
/** @var Response */ |
|||
private $response; |
|||
|
|||
/** |
|||
* @since 21.0.0 |
|||
*/ |
|||
public function __construct(Response $response) { |
|||
$this->response = $response; |
|||
} |
|||
|
|||
/** |
|||
* @since 21.0.0 |
|||
*/ |
|||
public function toHttpResponse(): Response { |
|||
return $this->response; |
|||
} |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
/* |
|||
* @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> |
|||
* |
|||
* @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at> |
|||
* |
|||
* @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 <http://www.gnu.org/licenses/>. |
|||
*/ |
|||
|
|||
namespace OCP\Http\WellKnown; |
|||
|
|||
/** |
|||
* Interface for an app handler that reacts to requests to Nextcloud's well |
|||
* known URLs, e.g. to a WebFinger |
|||
* |
|||
* @ref https://tools.ietf.org/html/rfc5785 |
|||
* |
|||
* @since 21.0.0 |
|||
*/ |
|||
interface IHandler { |
|||
|
|||
/** |
|||
* @param string $service the name of the well known service, e.g. 'webfinger' |
|||
* @param IRequestContext $context |
|||
* @param IResponse|null $previousResponse the response of the previous handler, if any |
|||
* |
|||
* @return IResponse|null a response object if the request could be handled, null otherwise |
|||
* |
|||
* @since 21.0.0 |
|||
*/ |
|||
public function handle(string $service, IRequestContext $context, ?IResponse $previousResponse): ?IResponse; |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
/* |
|||
* @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> |
|||
* |
|||
* @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at> |
|||
* |
|||
* @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 <http://www.gnu.org/licenses/>. |
|||
*/ |
|||
|
|||
namespace OCP\Http\WellKnown; |
|||
|
|||
use OCP\IRequest; |
|||
|
|||
/** |
|||
* The context object for \OCP\Http\IWellKnownHandler::handle |
|||
* |
|||
* Objects of this type will transport any optional information, e.g. the request |
|||
* object through which the app well known handler can obtain URL parameters |
|||
* |
|||
* @since 21.0.0 |
|||
*/ |
|||
interface IRequestContext { |
|||
|
|||
/** |
|||
* @return IRequest |
|||
* |
|||
* @since 21.0.0 |
|||
*/ |
|||
public function getHttpRequest(): IRequest; |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
/* |
|||
* @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> |
|||
* |
|||
* @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at> |
|||
* |
|||
* @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 <http://www.gnu.org/licenses/>. |
|||
*/ |
|||
|
|||
namespace OCP\Http\WellKnown; |
|||
|
|||
use OCP\AppFramework\Http\Response; |
|||
|
|||
/** |
|||
* @since 21.0.0 |
|||
*/ |
|||
interface IResponse { |
|||
|
|||
/** |
|||
* @since 21.0.0 |
|||
*/ |
|||
public function toHttpResponse(): Response; |
|||
} |
|||
@ -0,0 +1,170 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
/* |
|||
* @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> |
|||
* |
|||
* @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at> |
|||
* |
|||
* @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 <http://www.gnu.org/licenses/>. |
|||
*/ |
|||
|
|||
namespace OCP\Http\WellKnown; |
|||
|
|||
use OCP\AppFramework\Http\JSONResponse; |
|||
use OCP\AppFramework\Http\Response; |
|||
use function array_filter; |
|||
|
|||
/** |
|||
* A JSON Document Format (JDF) response to a well-known request |
|||
* |
|||
* @ref https://tools.ietf.org/html/rfc6415#appendix-A
|
|||
* @ref https://tools.ietf.org/html/rfc7033#section-4.4
|
|||
* |
|||
* @since 21.0.0 |
|||
*/ |
|||
final class JrdResponse implements IResponse { |
|||
|
|||
/** @var string */ |
|||
private $subject; |
|||
|
|||
/** @var string|null */ |
|||
private $expires; |
|||
|
|||
/** @var string[] */ |
|||
private $aliases = []; |
|||
|
|||
/** @var (string|null)[] */ |
|||
private $properties = []; |
|||
|
|||
/** @var mixed[] */ |
|||
private $links; |
|||
|
|||
/** |
|||
* @param string $subject https://tools.ietf.org/html/rfc7033#section-4.4.1
|
|||
* |
|||
* @since 21.0.0 |
|||
*/ |
|||
public function __construct(string $subject) { |
|||
$this->subject = $subject; |
|||
} |
|||
|
|||
/** |
|||
* @param string $expires |
|||
* |
|||
* @return $this |
|||
* |
|||
* @since 21.0.0 |
|||
*/ |
|||
public function setExpires(string $expires): self { |
|||
$this->expires = $expires; |
|||
|
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* Add an alias |
|||
* |
|||
* @ref https://tools.ietf.org/html/rfc7033#section-4.4.2
|
|||
* |
|||
* @param string $alias |
|||
* |
|||
* @return $this |
|||
* |
|||
* @since 21.0.0 |
|||
*/ |
|||
public function addAlias(string $alias): self { |
|||
$this->aliases[] = $alias; |
|||
|
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* Add a property |
|||
* |
|||
* @ref https://tools.ietf.org/html/rfc7033#section-4.4.3
|
|||
* |
|||
* @param string $property |
|||
* @param string|null $value |
|||
* |
|||
* @return $this |
|||
* |
|||
* @since 21.0.0 |
|||
*/ |
|||
public function addProperty(string $property, ?string $value): self { |
|||
$this->properties[$property] = $value; |
|||
|
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* Add a link |
|||
* |
|||
* @ref https://tools.ietf.org/html/rfc7033#section-8.4
|
|||
* |
|||
* @param string $rel https://tools.ietf.org/html/rfc7033#section-4.4.4.1
|
|||
* @param string|null $type https://tools.ietf.org/html/rfc7033#section-4.4.4.2
|
|||
* @param string|null $href https://tools.ietf.org/html/rfc7033#section-4.4.4.3
|
|||
* @param string[]|null $titles https://tools.ietf.org/html/rfc7033#section-4.4.4.4
|
|||
* @param string|null $properties https://tools.ietf.org/html/rfc7033#section-4.4.4.5
|
|||
* |
|||
* @psalm-param array<string,(string|null)>|null $properties https://tools.ietf.org/html/rfc7033#section-4.4.4.5
|
|||
* |
|||
* @return JrdResponse |
|||
* @since 21.0.0 |
|||
*/ |
|||
public function addLink(string $rel, |
|||
?string $type, |
|||
?string $href, |
|||
?array $titles = [], |
|||
?array $properties = []): self { |
|||
$this->links[] = array_filter([ |
|||
'rel' => $rel, |
|||
'type' => $type, |
|||
'href' => $href, |
|||
'titles' => $titles, |
|||
'properties' => $properties, |
|||
]); |
|||
|
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* @since 21.0.0 |
|||
*/ |
|||
public function toHttpResponse(): Response { |
|||
return new JSONResponse(array_filter([ |
|||
'subject' => $this->subject, |
|||
'expires' => $this->expires, |
|||
'aliases' => $this->aliases, |
|||
'properties' => $this->properties, |
|||
'links' => $this->links, |
|||
])); |
|||
} |
|||
|
|||
/** |
|||
* Does this response have any data attached to it? |
|||
* |
|||
* @since 21.0.0 |
|||
*/ |
|||
public function isEmpty(): bool { |
|||
return $this->expires === null |
|||
&& empty($this->aliases) |
|||
&& empty($this->properties) |
|||
&& empty($this->links); |
|||
} |
|||
} |
|||
@ -0,0 +1,86 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
/* |
|||
* @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> |
|||
* |
|||
* @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at> |
|||
* |
|||
* @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 <http://www.gnu.org/licenses/>. |
|||
*/ |
|||
|
|||
namespace Tests\Core\Controller; |
|||
|
|||
use OC\Core\Controller\WellKnownController; |
|||
use OC\Http\WellKnown\RequestManager; |
|||
use OCP\AppFramework\Http\JSONResponse; |
|||
use OCP\Http\WellKnown\IResponse; |
|||
use OCP\IRequest; |
|||
use PHPUnit\Framework\MockObject\MockObject; |
|||
use Test\TestCase; |
|||
|
|||
class WellKnownControllerTest extends TestCase { |
|||
|
|||
/** @var IRequest|MockObject */ |
|||
private $request; |
|||
|
|||
/** @var RequestManager|MockObject */ |
|||
private $manager; |
|||
|
|||
/** @var WellKnownController */ |
|||
private $controller; |
|||
|
|||
protected function setUp(): void { |
|||
parent::setUp(); |
|||
|
|||
$this->request = $this->createMock(IRequest::class); |
|||
$this->manager = $this->createMock(RequestManager::class); |
|||
|
|||
$this->controller = new WellKnownController( |
|||
$this->request, |
|||
$this->manager, |
|||
); |
|||
} |
|||
|
|||
public function testHandleNotProcessed(): void { |
|||
$httpResponse = $this->controller->handle("nodeinfo"); |
|||
|
|||
self::assertInstanceOf(JSONResponse::class, $httpResponse); |
|||
self::assertArrayHasKey('X-NEXTCLOUD-WELL-KNOWN', $httpResponse->getHeaders()); |
|||
} |
|||
|
|||
public function testHandle(): void { |
|||
$response = $this->createMock(IResponse::class); |
|||
$jsonResponse = $this->createMock(JSONResponse::class); |
|||
$response->expects(self::once()) |
|||
->method('toHttpResponse') |
|||
->willReturn($jsonResponse); |
|||
$this->manager->expects(self::once()) |
|||
->method('process') |
|||
->with( |
|||
"nodeinfo", |
|||
$this->request |
|||
)->willReturn($response); |
|||
$jsonResponse->expects(self::once()) |
|||
->method('addHeader') |
|||
->willReturnSelf(); |
|||
|
|||
$httpResponse = $this->controller->handle("nodeinfo"); |
|||
|
|||
self::assertInstanceOf(JSONResponse::class, $httpResponse); |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
/* |
|||
* @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> |
|||
* |
|||
* @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at> |
|||
* |
|||
* @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 <http://www.gnu.org/licenses/>. |
|||
*/ |
|||
|
|||
namespace Tests\Http\WellKnown; |
|||
|
|||
use OCP\AppFramework\Http\JSONResponse; |
|||
use OCP\Http\WellKnown\GenericResponse; |
|||
use Test\TestCase; |
|||
|
|||
class GenericResponseTest extends TestCase { |
|||
public function testToHttpResponse(): void { |
|||
$httpResponse = $this->createMock(JSONResponse::class); |
|||
|
|||
$response = new GenericResponse($httpResponse); |
|||
|
|||
self::assertSame($httpResponse, $response->toHttpResponse()); |
|||
} |
|||
} |
|||
@ -0,0 +1,108 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
/* |
|||
* @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> |
|||
* |
|||
* @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at> |
|||
* |
|||
* @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 <http://www.gnu.org/licenses/>. |
|||
*/ |
|||
|
|||
namespace Test\Http\WellKnown; |
|||
|
|||
use OCP\AppFramework\Http\JSONResponse; |
|||
use OCP\Http\WellKnown\JrdResponse; |
|||
use Test\TestCase; |
|||
|
|||
class JrdResponseTest extends TestCase { |
|||
public function testEmptyToHttpResponse(): void { |
|||
$response = new JrdResponse("subject"); |
|||
$httpResponse = $response->toHttpResponse(); |
|||
|
|||
self::assertTrue($response->isEmpty()); |
|||
self::assertInstanceOf(JSONResponse::class, $httpResponse); |
|||
/** @var JSONResponse $httpResponse */ |
|||
self::assertEquals( |
|||
[ |
|||
'subject' => 'subject', |
|||
], |
|||
$httpResponse->getData() |
|||
); |
|||
} |
|||
|
|||
public function testComplexToHttpResponse(): void { |
|||
$response = new JrdResponse("subject"); |
|||
$response->addAlias('alias'); |
|||
$response->addAlias('blias'); |
|||
$response->addProperty('propa', 'a'); |
|||
$response->addProperty('propb', null); |
|||
$response->setExpires('tomorrow'); |
|||
$response->addLink('rel', null, null); |
|||
$response->addLink('rel', 'type', null); |
|||
$response->addLink('rel', 'type', 'href', ['title' => 'titlevalue']); |
|||
$response->addLink('rel', 'type', 'href', ['title' => 'titlevalue'], ['propx' => 'valx']); |
|||
$httpResponse = $response->toHttpResponse(); |
|||
|
|||
self::assertFalse($response->isEmpty()); |
|||
self::assertInstanceOf(JSONResponse::class, $httpResponse); |
|||
/** @var JSONResponse $httpResponse */ |
|||
self::assertEquals( |
|||
[ |
|||
'subject' => 'subject', |
|||
'aliases' => [ |
|||
'alias', |
|||
'blias', |
|||
], |
|||
'properties' => [ |
|||
'propa' => 'a', |
|||
'propb' => null, |
|||
], |
|||
'expires' => 'tomorrow', |
|||
'links' => [ |
|||
[ |
|||
'rel' => 'rel', |
|||
], |
|||
[ |
|||
'rel' => 'rel', |
|||
'type' => 'type', |
|||
], |
|||
[ |
|||
'rel' => 'rel', |
|||
'type' => 'type', |
|||
'href' => 'href', |
|||
'titles' => [ |
|||
'title' => 'titlevalue', |
|||
], |
|||
], |
|||
[ |
|||
'rel' => 'rel', |
|||
'type' => 'type', |
|||
'href' => 'href', |
|||
'titles' => [ |
|||
'title' => 'titlevalue', |
|||
], |
|||
'properties' => [ |
|||
'propx' => 'valx', |
|||
], |
|||
], |
|||
] |
|||
], |
|||
$httpResponse->getData() |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,176 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
/* |
|||
* @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> |
|||
* |
|||
* @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at> |
|||
* |
|||
* @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 <http://www.gnu.org/licenses/>. |
|||
*/ |
|||
|
|||
namespace Test\Http\WellKnown; |
|||
|
|||
use OC\AppFramework\Bootstrap\Coordinator; |
|||
use OC\AppFramework\Bootstrap\RegistrationContext; |
|||
use OC\Http\WellKnown\RequestManager; |
|||
use OCP\AppFramework\QueryException; |
|||
use OCP\Http\WellKnown\IHandler; |
|||
use OCP\Http\WellKnown\IRequestContext; |
|||
use OCP\Http\WellKnown\IResponse; |
|||
use OCP\Http\WellKnown\JrdResponse; |
|||
use OCP\IRequest; |
|||
use OCP\IServerContainer; |
|||
use PHPUnit\Framework\MockObject\MockObject; |
|||
use Psr\Log\LoggerInterface; |
|||
use RuntimeException; |
|||
use Test\TestCase; |
|||
use function get_class; |
|||
|
|||
class RequestManagerTest extends TestCase { |
|||
|
|||
/** @var Coordinator|MockObject */ |
|||
private $coordinator; |
|||
|
|||
/** @var IServerContainer|MockObject */ |
|||
private $container; |
|||
|
|||
/** @var MockObject|LoggerInterface */ |
|||
private $logger; |
|||
|
|||
/** @var RequestManager */ |
|||
private $manager; |
|||
|
|||
protected function setUp(): void { |
|||
parent::setUp(); |
|||
|
|||
$this->coordinator = $this->createMock(Coordinator::class); |
|||
$this->container = $this->createMock(IServerContainer::class); |
|||
$this->logger = $this->createMock(LoggerInterface::class); |
|||
|
|||
$this->manager = new RequestManager( |
|||
$this->coordinator, |
|||
$this->container, |
|||
$this->logger, |
|||
); |
|||
} |
|||
|
|||
public function testProcessAppsNotRegistered(): void { |
|||
$request = $this->createMock(IRequest::class); |
|||
$this->expectException(RuntimeException::class); |
|||
|
|||
$this->manager->process("webfinger", $request); |
|||
} |
|||
|
|||
public function testProcessNoHandlersRegistered(): void { |
|||
$request = $this->createMock(IRequest::class); |
|||
$registrationContext = $this->createMock(RegistrationContext::class); |
|||
$this->coordinator->expects(self::once()) |
|||
->method('getRegistrationContext') |
|||
->willReturn($registrationContext); |
|||
$registrationContext->expects(self::once()) |
|||
->method('getWellKnownHandlers') |
|||
->willReturn([]); |
|||
|
|||
$response = $this->manager->process("webfinger", $request); |
|||
|
|||
self::assertNull($response); |
|||
} |
|||
|
|||
public function testProcessHandlerNotLoadable(): void { |
|||
$request = $this->createMock(IRequest::class); |
|||
$registrationContext = $this->createMock(RegistrationContext::class); |
|||
$this->coordinator->expects(self::once()) |
|||
->method('getRegistrationContext') |
|||
->willReturn($registrationContext); |
|||
$handler = new class { |
|||
}; |
|||
$registrationContext->expects(self::once()) |
|||
->method('getWellKnownHandlers') |
|||
->willReturn([ |
|||
[ |
|||
'class' => get_class($handler), |
|||
], |
|||
]); |
|||
$this->container->expects(self::once()) |
|||
->method('get') |
|||
->with(get_class($handler)) |
|||
->willThrowException(new QueryException("")); |
|||
$this->logger->expects(self::once()) |
|||
->method('error'); |
|||
|
|||
$response = $this->manager->process("webfinger", $request); |
|||
|
|||
self::assertNull($response); |
|||
} |
|||
|
|||
public function testProcessHandlerOfWrongType(): void { |
|||
$request = $this->createMock(IRequest::class); |
|||
$registrationContext = $this->createMock(RegistrationContext::class); |
|||
$this->coordinator->expects(self::once()) |
|||
->method('getRegistrationContext') |
|||
->willReturn($registrationContext); |
|||
$handler = new class { |
|||
}; |
|||
$registrationContext->expects(self::once()) |
|||
->method('getWellKnownHandlers') |
|||
->willReturn([ |
|||
[ |
|||
'class' => get_class($handler), |
|||
], |
|||
]); |
|||
$this->container->expects(self::once()) |
|||
->method('get') |
|||
->with(get_class($handler)) |
|||
->willReturn($handler); |
|||
$this->logger->expects(self::once()) |
|||
->method('error'); |
|||
|
|||
$response = $this->manager->process("webfinger", $request); |
|||
|
|||
self::assertNull($response); |
|||
} |
|||
|
|||
public function testProcess(): void { |
|||
$request = $this->createMock(IRequest::class); |
|||
$registrationContext = $this->createMock(RegistrationContext::class); |
|||
$this->coordinator->expects(self::once()) |
|||
->method('getRegistrationContext') |
|||
->willReturn($registrationContext); |
|||
$handler = new class implements IHandler { |
|||
public function handle(string $service, IRequestContext $context, ?IResponse $previousResponse): ?IResponse { |
|||
return (new JrdResponse($service))->addAlias('alias'); |
|||
} |
|||
}; |
|||
$registrationContext->expects(self::once()) |
|||
->method('getWellKnownHandlers') |
|||
->willReturn([ |
|||
[ |
|||
'class' => get_class($handler), |
|||
], |
|||
]); |
|||
$this->container->expects(self::once()) |
|||
->method('get') |
|||
->with(get_class($handler)) |
|||
->willReturn($handler); |
|||
|
|||
$response = $this->manager->process("webfinger", $request); |
|||
|
|||
self::assertNotNull($response); |
|||
self::assertInstanceOf(JrdResponse::class, $response); |
|||
} |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue