summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorMaxence Lange <maxence@artificial-owl.com>2020-09-21 14:47:31 -0100
committerGitHub <noreply@github.com>2020-09-21 14:47:31 -0100
commit88cd3e9259172f5e1beeace4c79677330457b117 (patch)
tree6bc61c276e8dc5be7079395e7bdad0da72eef9d3 /lib
parentf433e88e533bceb3e053eb3c2b8d9cf19bf9c99e (diff)
parente57fcd2a36c43d3c3ff6905d09d1c23da5a6d231 (diff)
Merge pull request #985 from nextcloud/feature/noid/compat-with-clients
[wip] Oauth for 3rdparty clients
Diffstat (limited to 'lib')
-rw-r--r--lib/Command/Timeline.php2
-rw-r--r--lib/Controller/ActivityPubController.php26
-rw-r--r--lib/Controller/ApiController.php350
-rw-r--r--lib/Controller/NavigationController.php2
-rw-r--r--lib/Controller/OAuthController.php342
-rw-r--r--lib/Db/ActionsRequestBuilder.php5
-rw-r--r--lib/Db/CacheActorsRequest.php11
-rw-r--r--lib/Db/CacheActorsRequestBuilder.php17
-rw-r--r--lib/Db/ClientRequest.php171
-rw-r--r--lib/Db/ClientRequestBuilder.php159
-rw-r--r--lib/Db/CoreRequestBuilder.php114
-rw-r--r--lib/Db/FollowsRequest.php61
-rw-r--r--lib/Db/FollowsRequestBuilder.php41
-rw-r--r--lib/Db/InstancesRequest.php82
-rw-r--r--lib/Db/InstancesRequestBuilder.php171
-rw-r--r--lib/Db/SocialCoreQueryBuilder.php16
-rw-r--r--lib/Db/SocialCrossQueryBuilder.php113
-rw-r--r--lib/Db/SocialLimitsQueryBuilder.php44
-rw-r--r--lib/Db/SocialQueryBuilder.php19
-rw-r--r--lib/Db/StreamDestRequest.php6
-rw-r--r--lib/Db/StreamRequest.php99
-rw-r--r--lib/Db/StreamRequestBuilder.php15
-rw-r--r--lib/Exceptions/ClientException.php (renamed from lib/Exceptions/FollowDoesNotExistException.php)2
-rw-r--r--lib/Exceptions/ClientNotFoundException.php39
-rw-r--r--lib/Exceptions/FollowNotFoundException.php39
-rw-r--r--lib/Exceptions/InstanceDoesNotExistException.php39
-rw-r--r--lib/Interfaces/Object/DocumentInterface.php7
-rw-r--r--lib/Interfaces/Object/FollowInterface.php4
-rw-r--r--lib/Migration/Version0003Date20200611000001.php267
-rw-r--r--lib/Migration/Version0003Date20200823023911.php454
-rw-r--r--lib/Migration/Version0003Date20200921103342.php (renamed from lib/Migration/Version0003Date20200730213528.php)51
-rw-r--r--lib/Model/ActivityPub/ACore.php54
-rw-r--r--lib/Model/ActivityPub/Actor/Person.php313
-rw-r--r--lib/Model/ActivityPub/Item.php22
-rw-r--r--lib/Model/ActivityPub/Stream.php85
-rw-r--r--lib/Model/Client/Options/CoreOptions.php69
-rw-r--r--lib/Model/Client/Options/TimelineOptions.php296
-rw-r--r--lib/Model/Client/SocialClient.php469
-rw-r--r--lib/Model/Instance.php441
-rw-r--r--lib/Service/AccountService.php9
-rw-r--r--lib/Service/ClientService.php207
-rw-r--r--lib/Service/FollowService.php10
-rw-r--r--lib/Service/InstanceService.php90
-rw-r--r--lib/Service/PostService.php2
-rw-r--r--lib/Service/StreamService.php27
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()
+