diff options
author | Maxence Lange <maxence@artificial-owl.com> | 2020-09-21 14:47:31 -0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-09-21 14:47:31 -0100 |
commit | 88cd3e9259172f5e1beeace4c79677330457b117 (patch) | |
tree | 6bc61c276e8dc5be7079395e7bdad0da72eef9d3 /lib | |
parent | f433e88e533bceb3e053eb3c2b8d9cf19bf9c99e (diff) | |
parent | e57fcd2a36c43d3c3ff6905d09d1c23da5a6d231 (diff) |
Merge pull request #985 from nextcloud/feature/noid/compat-with-clients
[wip] Oauth for 3rdparty clients
Diffstat (limited to 'lib')
45 files changed, 4553 insertions, 309 deletions
diff --git a/lib/Command/Timeline.php b/lib/Command/Timeline.php index e7062ba1..d8334b34 100644 --- a/lib/Command/Timeline.php +++ b/lib/Command/Timeline.php @@ -149,7 +149,7 @@ class Timeline extends ExtendedBase { $this->streamRequest->setViewer($actor); switch ($timeline) { case 'home': - $stream = $this->streamRequest->getTimelineHome(0, $this->count); + $stream = $this->streamRequest->getTimelineHome_dep(0, $this->count); $this->outputStreams($stream); break; diff --git a/lib/Controller/ActivityPubController.php b/lib/Controller/ActivityPubController.php index df6215aa..1138bc64 100644 --- a/lib/Controller/ActivityPubController.php +++ b/lib/Controller/ActivityPubController.php @@ -34,7 +34,7 @@ use daita\MySmallPhpTools\Traits\Nextcloud\TNCDataResponse; use daita\MySmallPhpTools\Traits\TAsync; use daita\MySmallPhpTools\Traits\TStringTools; use Exception; -use OC\AppFramework\Http; +use OCP\AppFramework\Http; use OCA\Social\AppInfo\Application; use OCA\Social\Exceptions\AccountDoesNotExistException; use OCA\Social\Exceptions\ItemUnknownException; @@ -203,7 +203,6 @@ class ActivityPubController extends Controller { * @return Response */ public function sharedInbox(): Response { - try { $body = file_get_contents('php://input'); $this->miscService->log('[<<] sharedInbox: ' . $body, 1); @@ -282,6 +281,29 @@ class ActivityPubController extends Controller { /** + * Method is called when a remote ActivityPub server wants to GET in the INBOX of a USER + * Checking that the user exists, and that the header is properly signed. + * + * @NoCSRFRequired + * @PublicPage + * + * @param string $username + * + * @return Response + */ + public function getInbox(string $username): Response { + try { + $body = file_get_contents('php://input'); + $actor = $this->cacheActorService->getFromLocalAccount($username); + + return $this->success(); + } catch (Exception $e) { + return new DataResponse([], Http::STATUS_NOT_FOUND); + } + } + + + /** * Outbox. does nothing. * * @NoCSRFRequired diff --git a/lib/Controller/ApiController.php b/lib/Controller/ApiController.php new file mode 100644 index 00000000..1b4be8a7 --- /dev/null +++ b/lib/Controller/ApiController.php @@ -0,0 +1,350 @@ +<?php +declare(strict_types=1); + + +/** + * Nextcloud - Social Support + * + * This file is licensed under the Affero General Public License version 3 or + * later. See the COPYING file. + * + * @author Maxence Lange <maxence@artificial-owl.com> + * @copyright 2018, Maxence Lange <maxence@artificial-owl.com> + * @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 OCA\Social\Controller; + + +use daita\MySmallPhpTools\Traits\Nextcloud\TNCDataResponse; +use Exception; +use OCA\Social\AppInfo\Application; +use OCA\Social\Exceptions\AccountDoesNotExistException; +use OCA\Social\Exceptions\ClientNotFoundException; +use OCA\Social\Exceptions\InstanceDoesNotExistException; +use OCA\Social\Model\ActivityPub\ACore; +use OCA\Social\Model\ActivityPub\Actor\Person; +use OCA\Social\Model\ActivityPub\Stream; +use OCA\Social\Model\Client\Options\TimelineOptions; +use OCA\Social\Model\Client\SocialClient; +use OCA\Social\Service\AccountService; +use OCA\Social\Service\CacheActorService; +use OCA\Social\Service\ClientService; +use OCA\Social\Service\ConfigService; +use OCA\Social\Service\FollowService; +use OCA\Social\Service\InstanceService; +use OCA\Social\Service\MiscService; +use OCA\Social\Service\StreamService; +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\DataResponse; +use OCP\IRequest; +use OCP\IUserSession; + + +/** + * Class ApiController + * + * @package OCA\Social\Controller + */ +class ApiController extends Controller { + + + use TNCDataResponse; + + + /** @var IUserSession */ + private $userSession; + + /** @var InstanceService */ + private $instanceService; + + /** @var ClientService */ + private $clientService; + + /** @var AccountService */ + private $accountService; + + /** @var CacheActorService */ + private $cacheActorService; + + /** @var FollowService */ + private $followService; + + /** @var StreamService */ + private $streamService; + + /** @var ConfigService */ + private $configService; + + /** @var MiscService */ + private $miscService; + + + /** @var string */ + private $bearer = ''; + + /** @var SocialClient */ + private $client; + + /** @var Person */ + private $viewer; + + + /** + * ActivityStreamController constructor. + * + * @param IRequest $request + * @param IUserSession $userSession + * @param InstanceService $instanceService + * @param ClientService $clientService + * @param AccountService $accountService + * @param CacheActorService $cacheActorService + * @param FollowService $followService + * @param StreamService $streamService + * @param ConfigService $configService + * @param MiscService $miscService + */ + public function __construct( + IRequest $request, IUserSession $userSession, InstanceService $instanceService, + ClientService $clientService, AccountService $accountService, CacheActorService $cacheActorService, + FollowService $followService, StreamService $streamService, ConfigService $configService, + MiscService $miscService + ) { + parent::__construct(Application::APP_NAME, $request); + + $this->userSession = $userSession; + $this->instanceService = $instanceService; + $this->clientService = $clientService; + $this->accountService = $accountService; + $this->cacheActorService = $cacheActorService; + $this->followService = $followService; + $this->streamService = $streamService; + $this->configService = $configService; + $this->miscService = $miscService; + + $authHeader = trim($this->request->getHeader('Authorization')); + if (strpos($authHeader, ' ')) { + list($authType, $authToken) = explode(' ', $authHeader); + if (strtolower($authType) === 'bearer') { + $this->bearer = $authToken; + } + } + } + + + /** + * @NoCSRFRequired + * @PublicPage + * + * @return DataResponse + */ + public function appsCredentials() { + try { + $this->initViewer(true); + + if ($this->client === null) { + return new DataResponse( + [ + 'name' => 'Nextcloud Social', + 'website' => 'https://github.com/nextcloud/social/' + ], Http::STATUS_OK + ); + } else { + return new DataResponse( + [ + 'name' => $this->client->getAppName(), + 'website' => $this->client->getAppWebsite() + ], Http::STATUS_OK + ); + } + } catch (Exception $e) { + return $this->error($e->getMessage()); + } + + } + + + /** + * @NoCSRFRequired + * @PublicPage + * + * @return DataResponse + */ + public function verifyCredentials() { + try { + $this->initViewer(true); + + return new DataResponse($this->viewer, Http::STATUS_OK); + } catch (Exception $e) { + return $this->error($e->getMessage()); + } + } + + + /** + * @NoCSRFRequired + * @PublicPage + * + * @return DataResponse + */ + public function customEmojis(): DataResponse { + return new DataResponse([], Http::STATUS_OK); + } + + + /** + * @NoCSRFRequired + * @PublicPage + * + * @return DataResponse + */ + public function savedSearches(): DataResponse { + try { + $this->initViewer(true); + + return new DataResponse([], Http::STATUS_OK); + } catch (Exception $e) { + return $this->error($e->getMessage()); + } + } + + + /** + * @NoCSRFRequired + * @PublicPage + * + * @return DataResponse + */ + public function notifications(): DataResponse { + try { + $this->initViewer(true); + + return new DataResponse([], Http::STATUS_OK); + } catch (Exception $e) { + return $this->error($e->getMessage()); + } + } + + + /** + * @NoCSRFRequired + * @PublicPage + * + * @return DataResponse + * @throws InstanceDoesNotExistException + */ + public function instance(): DataResponse { + $local = $this->instanceService->getLocal(Stream::FORMAT_LOCAL); + + return new DataResponse($local, Http::STATUS_OK); + } + + + /** + * @NoCSRFRequired + * @PublicPage + * + * @param string $timeline + * @param int $limit + * + * @return DataResponse + */ + public function timelines(string $timeline, int $limit = 20): DataResponse { + $options = new TimelineOptions($this->request); + $options->setFormat(Stream::FORMAT_LOCAL); + $options->setTimeline($timeline); + $options->setLimit($limit); + + try { + $this->initViewer(true); + $posts = $this->streamService->getTimeline($options); + + return new DataResponse($posts, Http::STATUS_OK); + } catch (Exception $e) { + return $this->error($e->getMessage()); + } + } + + + /** + * + * @param bool $exception + * + * @return bool + * @throws ClientNotFoundException + */ + private function initViewer(bool $exception = false): bool { + try { + $userId = $this->currentSession(); + + $this->miscService->log( + '[ApiController] initViewer: ' . $userId . ' (bearer=' . $this->bearer . ')', 0 + ); + + $account = $this->accountService->getActorFromUserId($userId); + $this->viewer = $this->cacheActorService->getFromLocalAccount($account->getPreferredUsername()); + $this->viewer->setExportFormat(ACore::FORMAT_LOCAL); + + $this->streamService->setViewer($this->viewer); + $this->followService->setViewer($this->viewer); + $this->cacheActorService->setViewer($this->viewer); + + return true; + } catch (Exception $e) { + if ($exception) { + throw new ClientNotFoundException('the access_token was revoked'); + } + } + + return false; + } + + + /** + * @return string + * @throws AccountDoesNotExistException + * @throws ClientNotFoundException + */ + private function currentSession(): string { + $user = $this->userSession->getUser(); + if ($user !== null) { + return $user->getUID(); + } + + if ($this->bearer !== '') { + $this->client = $this->clientService->getFromToken($this->bearer); + + return $this->client->getAuthUserId(); + } + + throw new AccountDoesNotExistException('userId not defined'); + } + + + /** + * @param string $error + * + * @return DataResponse + */ + private function error(string $error): DataResponse { + return new DataResponse(['error' => $error], Http::STATUS_UNAUTHORIZED); + } + +} + + diff --git a/lib/Controller/NavigationController.php b/lib/Controller/NavigationController.php index b1b0dcd3..8111804f 100644 --- a/lib/Controller/NavigationController.php +++ b/lib/Controller/NavigationController.php @@ -35,7 +35,7 @@ use daita\MySmallPhpTools\Traits\Nextcloud\TNCDataResponse; use daita\MySmallPhpTools\Traits\TArrayTools; use Exception; use OC; -use OC\AppFramework\Http; +use OCP\AppFramework\Http; use OC\User\NoUserException; use OCA\Social\AppInfo\Application; use OCA\Social\Exceptions\AccountAlreadyExistsException; diff --git a/lib/Controller/OAuthController.php b/lib/Controller/OAuthController.php new file mode 100644 index 00000000..3f46cdc9 --- /dev/null +++ b/lib/Controller/OAuthController.php @@ -0,0 +1,342 @@ +<?php +declare(strict_types=1); + + +/** + * Nextcloud - Social Support + * + * This file is licensed under the Affero General Public License version 3 or + * later. See the COPYING file. + * + * @author Maxence Lange <maxence@artificial-owl.com> + * @copyright 2018, Maxence Lange <maxence@artificial-owl.com> + * @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 OCA\Social\Controller; + + +use daita\MySmallPhpTools\Traits\Nextcloud\TNCDataResponse; +use Exception; +use OCA\Social\AppInfo\Application; +use OCA\Social\Exceptions\ClientException; +use OCA\Social\Exceptions\ClientNotFoundException; +use OCA\Social\Exceptions\InstanceDoesNotExistException; +use OCA\Social\Model\Client\SocialClient; +use OCA\Social\Service\AccountService; +use OCA\Social\Service\CacheActorService; +use OCA\Social\Service\ClientService; +use OCA\Social\Service\ConfigService; +use OCA\Social\Service\InstanceService; +use OCA\Social\Service\MiscService; +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\Http\Response; +use OCP\IRequest; +use OCP\IURLGenerator; +use OCP\IUserSession; + + +class OAuthController extends Controller { + + + use TNCDataResponse; + + + /** @var IUserSession */ + private $userSession; + + /** @var IURLGenerator */ + private $urlGenerator; + + /** @var InstanceService */ + private $instanceService; + + /** @var AccountService */ + private $accountService; + + /** @var CacheActorService */ + private $cacheActorService; + + /** @var ClientService */ + private $clientService; + + /** @var ConfigService */ + private $configService; + + /** @var MiscService */ + private $miscService; + + + /** + * ActivityStreamController constructor. + * + * @param IRequest $request + * @param IUserSession $userSession + * @param IURLGenerator $urlGenerator + * @param InstanceService $instanceService + * @param AccountService $accountService + * @param CacheActorService $cacheActorService + * @param ClientService $clientService + * @param ConfigService $configService + * @param MiscService $miscService + */ + public function __construct( + IRequest $request, IUserSession $userSession, IURLGenerator $urlGenerator, + InstanceService $instanceService, AccountService $accountService, + CacheActorService $cacheActorService, ClientService $clientService, ConfigService $configService, + MiscService $miscService + ) { + parent::__construct(Application::APP_NAME, $request); + + $this->userSession = $userSession; + $this->urlGenerator = $urlGenerator; + $this->instanceService = $instanceService; + $this->accountService = $accountService; + $this->cacheActorService = $cacheActorService; + $this->clientService = $clientService; + $this->configService = $configService; + $this->miscService = $miscService; + + $body = file_get_contents('php://input'); + $this->miscService->log('[OAuthController] input: ' . $body, 0); + } + + + /** + * @NoCSRFRequired + * @PublicPage + * + * @return Response + */ + public function nodeinfo(): Response { + $nodeInfo = [ + 'links' => [ + 'rel' => 'http://nodeinfo.diaspora.software/ns/schema/2.0', + 'href' => $this->urlGenerator->linkToRouteAbsolute('social.OAuth.nodeinfo2') + ] + ]; + + return new DataResponse($nodeInfo, Http::STATUS_OK); + } + + + /** + * @NoCSRFRequired + * @PublicPage + * + * @return Response + */ + public function nodeinfo2() { + try { + $local = $this->instanceService->getLocal(); + $name = $local->getTitle(); + + $version = $local->getVersion(); + $usage = $local->getUsage(); + $openReg = $local->isRegistrations(); + } catch (InstanceDoesNotExistException $e) { + $name = 'Nextcloud Social'; + $version = $this->configService->getAppValue('installed_version'); + $usage = []; + $openReg = false; + } + + $nodeInfo = [ + "version" => "2.0", + "software" => [ + "name" => $name, + "version" => $version + ], + "protocols" => [ + "activitypub" + ], + "usage" => $usage, + "openRegistrations" => $openReg + ]; + + return new DataResponse($nodeInfo, Http::STATUS_OK); + } + + + /** + * @NoCSRFRequired + * @PublicPage + * + * @param string $website + * @param string $redirect_uris + * @param string $scopes + * @param string $client_name + * + * @return Response + * @throws ClientException + */ + public function apps( + string $client_name = '', string $redirect_uris = '', string $website = '', string $scopes = 'read' + ): Response { + // TODO: manage array from request + if (!is_array($redirect_uris)) { + $redirect_uris = [$redirect_uris]; + } + + $client = new SocialClient(); + $client->setAppWebsite($website); + $client->setAppRedirectUris($redirect_uris); + $client->setAppScopes($client->getScopesFromString($scopes)); + $client->setAppName($client_name); + + $this->clientService->createApp($client); + + return new DataResponse( + [ + 'id' => $client->getId(), + 'name' => $client->getAppName(), + 'website' => $client->getAppWebsite(), + 'scopes' => implode(' ', $client->getAppScopes()), + 'client_id' => $client->getAppClientId(), + 'client_secret' => $client->getAppClientSecret() + |