Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>pull/24702/head
parent
d37034f161
commit
6995223b1e
@ -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); |
||||
} |
||||
} |
||||
Loading…
Reference in new issue