mirror of https://github.com/nextcloud/server.git
Add well known handlers API
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