diff options
author | Maxence Lange <maxence@artificial-owl.com> | 2020-08-28 01:35:57 -0100 |
---|---|---|
committer | Maxence Lange <maxence@artificial-owl.com> | 2020-08-28 01:36:19 -0100 |
commit | 8965acea039fc93b41f31d104c1b7330f11905df (patch) | |
tree | 23cdc662e920bfc578cfbf2a09a2bb5211f5ab6e /lib | |
parent | e986ca6a4f6596036f5ee32b4a70639c7f891492 (diff) |
oauth, first throw
Signed-off-by: Maxence Lange <maxence@artificial-owl.com>
Diffstat (limited to 'lib')
27 files changed, 2165 insertions, 301 deletions
diff --git a/lib/Controller/ActivityPubController.php b/lib/Controller/ActivityPubController.php index 52a4f701..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; diff --git a/lib/Controller/ApiController.php b/lib/Controller/ApiController.php new file mode 100644 index 00000000..d27febc2 --- /dev/null +++ b/lib/Controller/ApiController.php @@ -0,0 +1,208 @@ +<?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\ClientAuthDoesNotExistException; +use OCA\Social\Model\ActivityPub\Actor\Person; +use OCA\Social\Model\ActivityPub\Stream; +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\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 extends Controller { + + + use TNCDataResponse; + + + /** @var IUserSession */ + private $userSession; + + /** @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 Person */ + private $viewer; + + + /** + * ActivityStreamController constructor. + * + * @param IRequest $request + * @param IUserSession $userSession + * @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, ClientService $clientService, + AccountService $accountService, + CacheActorService $cacheActorService, FollowService $followService, StreamService $streamService, + ConfigService $configService, MiscService $miscService + ) { + parent::__construct(Application::APP_NAME, $request); + + $this->userSession = $userSession; + $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')); + list($authType, $authToken) = explode(' ', $authHeader); + if (strtolower($authType) === 'bearer') { + $this->bearer = $authToken; + } + } + + + /** + * @NoCSRFRequired + * @NoAdminRequired + * @PublicPage + * + * @param string $timeline + * @param int $limit + * + * @return DataResponse + */ + public function timelines(string $timeline, int $limit = 20): DataResponse { + try { + $this->initViewer(true); + $posts = $this->streamService->getStreamHome(0, $limit, Stream::FORMAT_LOCAL); + + return new DataResponse($posts, Http::STATUS_OK); + } catch (Exception $e) { + return $this->fail($e); + } + } + + + /** + * + * @param bool $exception + * + * @return bool + * @throws AccountDoesNotExistException + */ + private function initViewer(bool $exception = false): bool { + $userId = $this->currentSession($exception); + + try { + $this->viewer = $this->accountService->getActorFromUserId($userId); + + $this->streamService->setViewer($this->viewer); + $this->followService->setViewer($this->viewer); + $this->cacheActorService->setViewer($this->viewer); + } catch (Exception $e) { + if ($exception) { + throw new AccountDoesNotExistException( + 'unable to initViewer - ' . get_class($e) . ' - ' . $e->getMessage() + ); + } + + return false; + } + + return true; + } + + + /** + * @param bool $exception + * + * @return string + * @throws AccountDoesNotExistException + */ + private function currentSession(bool $exception): string { + $user = $this->userSession->getUser(); + if ($user !== null) { + return $user->getUID(); + } + + if ($this->bearer !== '') { + try { + $clientAuth = $this->clientService->getAuthFromToken($this->bearer); + + return $clientAuth->getUserId(); + } catch (ClientAuthDoesNotExistException $e) { + } + } + + throw new AccountDoesNotExistException('userId not defined'); + } + +} + + diff --git a/lib/Controller/NavigationController.php b/lib/Controller/NavigationController.php index 918684ff..f982da5e 100644 --- a/lib/Controller/NavigationController.php +++ b/lib/Controller/NavigationController.php @@ -34,7 +34,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/ActivityStreamController.php b/lib/Controller/OAuthController.php index 030e1d62..89fba1b0 100644 --- a/lib/Controller/ActivityStreamController.php +++ b/lib/Controller/OAuthController.php @@ -31,38 +31,41 @@ namespace OCA\Social\Controller; use daita\MySmallPhpTools\Traits\Nextcloud\TNCDataResponse; -use Exception; -use OC\AppFramework\Http; use OC\User\NoUserException; use OCA\Social\AppInfo\Application; use OCA\Social\Exceptions\AccountAlreadyExistsException; -use OCA\Social\Exceptions\AccountDoesNotExistException; use OCA\Social\Exceptions\ActorDoesNotExistException; +use OCA\Social\Exceptions\ClientAppDoesNotExistException; +use OCA\Social\Exceptions\ClientAuthDoesNotExistException; +use OCA\Social\Exceptions\ClientException; use OCA\Social\Exceptions\ItemAlreadyExistsException; use OCA\Social\Exceptions\SocialAppConfigException; use OCA\Social\Exceptions\UrlCloudException; -use OCA\Social\Model\ActivityPub\Actor\Person; -use OCA\Social\Model\ActivityPub\Stream; -use OCA\Social\Model\ActivityStream\ClientApp; +use OCA\Social\Model\Client\ClientApp; +use OCA\Social\Model\Client\ClientAuth; +use OCA\Social\Model\Client\ClientToken; 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\MiscService; -use OCA\Social\Service\StreamService; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\Response; use OCP\IRequest; +use OCP\IUserSession; -class ActivityStreamController extends Controller { +class OAuthController extends Controller { use TNCDataResponse; + /** @var IUserSession */ + private $userSession; + /** @var AccountService */ private $accountService; @@ -72,12 +75,6 @@ class ActivityStreamController extends Controller { /** @var ClientService */ private $clientService; - /** @var FollowService */ - private $followService; - - /** @var StreamService */ - private $streamService; - /** @var ConfigService */ private $configService; @@ -85,46 +82,33 @@ class ActivityStreamController extends Controller { private $miscService; - /** @var string */ - private $bearer = ''; - - /** @var Person */ - private $viewer; - - /** * ActivityStreamController constructor. * * @param IRequest $request + * @param IUserSession $userSession * @param AccountService $accountService * @param CacheActorService $cacheActorService * @param ClientService $clientService - * @param FollowService $followService - * @param StreamService $streamService * @param ConfigService $configService * @param MiscService $miscService */ public function __construct( - IRequest $request, AccountService $accountService, CacheActorService $cacheActorService, - ClientService $clientService, FollowService $followService, StreamService $streamService, - ConfigService $configService, + IRequest $request, IUserSession $userSession, AccountService $accountService, + CacheActorService $cacheActorService, ClientService $clientService, ConfigService $configService, MiscService $miscService ) { parent::__construct(Application::APP_NAME, $request); + $this->userSession = $userSession; $this->accountService = $accountService; $this->cacheActorService = $cacheActorService; $this->clientService = $clientService; - $this->followService = $followService; - $this->streamService = $streamService; $this->configService = $configService; $this->miscService = $miscService; - $authHeader = trim($this->request->getHeader('Authorization')); - list($authType, $authToken) = explode(' ', $authHeader); - if (strtolower($authType) === 'bearer') { - $this->bearer = $authToken; - } + $body = file_get_contents('php://input'); + $this->miscService->log('[ClientService] input: ' . $body, 1); } @@ -138,64 +122,88 @@ class ActivityStreamController extends Controller { * @param string $client_name * * @return Response + * @throws ClientException */ public function apps( - string $website = '', string $redirect_uris = '', string $scopes = '', string $client_name = '' + 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]; + } + $clientApp = new ClientApp(); $clientApp->setWebsite($website); - $clientApp->setRedirectUri($redirect_uris); + $clientApp->setRedirectUris($redirect_uris); $clientApp->setScopesFromString($scopes); $clientApp->setName($client_name); $this->clientService->createClient($clientApp); - $this->miscService->log('### ' . json_encode($clientApp)); - return $this->directSuccess($clientApp); + return new DataResponse($clientApp, Http::STATUS_OK); } /** * @NoCSRFRequired * @NoAdminRequired - * @PublicPage + * + * @param string $client_id + * @param string $redirect_uri + * @param string $response_type + * @param string $scope * * @return DataResponse - * @throws SocialAppConfigException * @throws AccountAlreadyExistsException * @throws ActorDoesNotExistException + * @throws ClientAppDoesNotExistException + * @throws ClientException * @throws ItemAlreadyExistsException - * @throws UrlCloudException * @throws NoUserException + * @throws SocialAppConfigException + * @throws UrlCloudException */ - public function authorize(): DataResponse { - $userId = 'cult'; + public function authorize( + string $client_id, string $redirect_uri, string $response_type, string $scope = 'read' + ): DataResponse { + $user = $this->userSession->getUser(); + $account = $this->accountService->getActorFromUserId($user->getUID()); - $account = $this->accountService->getActorFromUserId($userId); - $clientId = (string)$this->request->getParam('client_id', ''); - $responseType = (string)$this->request->getParam('response_type', ''); - $redirectUri = (string)$this->request->getParam('redirect_uri', ''); - - if ($responseType !== 'code') { + if ($response_type !== 'code') { return new DataResponse(['error' => 'invalid_type'], Http::STATUS_BAD_REQUEST); } -// $this->clientService->assignAccount($clientId, $account); - $code = 'test1234'; + $clientApp = $this->clientService->getClientByClientId($client_id); + $this->clientService->confirmData( + $clientApp, + [ + 'scopes' => $scope, + ] + ); + + $clientAuth = new ClientAuth(); + $clientAuth->setRedirectUri($redirect_uri); + $clientAuth->setScopes(explode(' ', $scope)); + $clientAuth->setAccount($account->getPreferredUsername()); + $clientAuth->setUserId($user->getUID()); - if ($redirectUri !== '') { - header('Location: ' . $redirectUri . '?code=' . $code); + $this->clientService->authClient($clientApp, $clientAuth); + $code = $clientAuth->getCode(); + + if ($redirect_uri !== 'urn:ietf:wg:oauth:2.0:oob') { + header('Location: ' . $redirect_uri . '?code=' . $code); exit(); } -// return new DataResponse( -// [ + // TODO : finalize result if no redirect_url + return new DataResponse( + [ // 'access_token' => '', // "token_type" => "Bearer", // "scope" => "read write follow push", // "created_at" => 1573979017 -// ], Http::STATUS_OK -// ); + ], Http::STATUS_OK + ); } @@ -205,21 +213,60 @@ class ActivityStreamController extends Controller { * @NoAdminRequired * @PublicPage * + * @param string $client_id + * @param string $client_secret + * @param string $redirect_uri + * @param string $grant_type + * @param string $scope + * @param string $code + * * @return DataResponse + * @throws ClientAppDoesNotExistException + * @throws ClientException + * @throws ClientAuthDoesNotExistException */ - public function token() { - $body = file_get_contents('php://input'); - $this->miscService->log('[<<] : ' . $body, 1); -////code=test1234&grant_type=authorization_code& -//client_secret=amJWTrlnZEhe44aXHsW2xlsTLD8g0DqabDDJ7jdp& -//redirect_uri=https%3A%2F%2Forg.mariotaku.twidere%2Fauth%2Fcallback%2Fmastodon& -//client_id=ihyiNapjftENlY2dZCbbfLHYoloB1HbpWQyLGtvr + public function token( + string $client_id, string $client_secret, string $redirect_uri, string $grant_type, + string $scope = 'read', string $code = '' + ) { + $clientApp = $this->clientService->getClientByClientId($client_id); + $this->clientService->confirmData( + $clientApp, + [ + 'client_secret' => $client_secret, + 'redirect_uri' => $redirect_uri, + 'scopes' => $scope + ] + ); + + $clientToken = new ClientToken(); + $clientToken->setScopes(explode(' ', $scope)); + + if ($grant_type === 'authorization_code') { + if ($code === '') { + return new DataResponse(['error' => 'missing code'], Http::STATUS_BAD_REQUEST); + } + + $clientAuth = $this->clientService->getAuthByCode($code); + + $this->clientService->generateToken($clientApp, $clientAuth, $clientToken); + + } else if ($grant_type === 'client_credentials') { + // TODO: manage client_credentials + } else { + return new DataResponse(['error' => 'invalid value for grant_type'], Http::STATUS_BAD_REQUEST); + } + + if ($clientToken->getToken() === '') { + return new DataResponse(['error' => 'issue generating access_token'], Http::STATUS_BAD_REQUEST); + } + return new DataResponse( [ - "access_token" => "ZA-Yj3aBD8U8Cm7lKUp-lm9O9BmDgdhHzDeqsY8tlL0", - "token_type" => "Bearer", - "scope" => "read write follow push", - "created_at" => time() + "access_token" => $clientToken->getToken(), + "token_type" => 'Bearer', + "scope" => $scope, + "created_at" => $clientToken->getCreation() ], 200 ); } @@ -286,63 +333,6 @@ class ActivityStreamController extends Controller { } - /** - * @NoCSRFRequired - * @NoAdminRequired - * @PublicPage - * - * @param string $timeline - * @param int $limit - * - * @return DataResponse - */ - public function timelines(string $timeline, int $limit = 20): DataResponse { - try { - $this->initViewer(true); - $posts = $this->streamService->getStreamHome(0, $limit, Stream::FORMAT_LOCAL); - - return new DataResponse($posts, 200); - } catch (Exception $e) { - return $this->fail($e); - } - } - - - /** - * - * @param bool $exception - * - * @return bool - * @throws AccountDoesNotExistException - */ - private function initViewer(bool $exception = false): bool { - if ($this->bearer === '') { -// if ($exception) { -// throw new AccountDoesNotExistException('userId not defined'); -// } -// -// return false; - } - - try { - $this->viewer = $this->cacheActorService->getFromLocalAccount('cult'); - - $this->streamService->setViewer($this->viewer); - $this->followService->setViewer($this->viewer); - $this->cacheActorService->setViewer($this->viewer); - } catch (Exception $e) { - if ($exception) { - throw new AccountDoesNotExistException( - 'unable to initViewer - ' . get_class($e) . ' - ' . $e->getMessage() - ); - } - - return false; - } - - return true; - } - } diff --git a/lib/Db/ClientAppRequest.php b/lib/Db/ClientAppRequest.php index 7c8f64ac..8a7eb375 100644 --- a/lib/Db/ClientAppRequest.php +++ b/lib/Db/ClientAppRequest.php @@ -35,12 +35,12 @@ use daita\MySmallPhpTools\Traits\TArrayTools; use DateTime; use Exception; use OCA\Social\Exceptions\ClientAppDoesNotExistException; -use OCA\Social\Model\ActivityStream\ClientApp; +use OCA\Social\Model\Client\ClientApp; use OCP\DB\QueryBuilder\IQueryBuilder; /** - * Class ActionsRequest + * Class ClientAppRequest * * @package OCA\Social\Db */ @@ -59,9 +59,10 @@ class ClientAppRequest extends ClientAppRequestBuilder { $qb = $this->getClientAppInsertSql(); $qb->setValue('name', $qb->createNamedParameter($clientApp->getName())) ->setValue('website', $qb->createNamedParameter($clientApp->getWebsite())) - ->setValue('redirect_uri', $qb->createNamedParameter($clientApp->getRedirectUri())) + ->setValue('redirect_uris', $qb->createNamedParameter(json_encode($clientApp->getRedirectUris()))) ->setValue('client_id', $qb->createNamedParameter($clientApp->getClientId())) - ->setValue('client_secret', $qb->createNamedParameter($clientApp->getClientSecret())); + ->setValue('client_secret', $qb->createNamedParameter($clientApp->getClientSecret())) + ->setValue('scopes', $qb->createNamedParameter(json_encode($clientApp->getScopes()))); try { $qb->setValue( @@ -100,7 +101,6 @@ class ClientAppRequest extends ClientAppRequestBuilder { */ public function getByClientId(string $clientId): ClientApp { $qb = $this->getClientAppSelectSql(); - $qb->limitToClientId($clientId); return $this->getClientAppFromRequest($qb); diff --git a/lib/Db/ClientAppRequestBuilder.php b/lib/Db/ClientAppRequestBuilder.php index bd43e12b..e687021a 100644 --- a/lib/Db/ClientAppRequestBuilder.php +++ b/lib/Db/ClientAppRequestBuilder.php @@ -34,7 +34,7 @@ namespace OCA\Social\Db; use daita\MySmallPhpTools\Exceptions\RowNotFoundException; use daita\MySmallPhpTools\Traits\TArrayTools; use OCA\Social\Exceptions\ClientAppDoesNotExistException; -use OCA\Social\Model\ActivityStream\ClientApp; +use OCA\Social\Model\Client\ClientApp; /** @@ -55,7 +55,7 @@ class ClientAppRequestBuilder extends CoreRequestBuilder { */ protected function getClientAppInsertSql(): SocialQueryBuilder { $qb = $this->getQueryBuilder(); - $qb->insert(self::TABLE_CLIENT_APP); + $qb->insert(self::TABLE_CLIENT); return $qb; } @@ -68,7 +68,7 @@ class ClientAppRequestBuilder extends CoreRequestBuilder { */ protected function getClientAppUpdateSql(): SocialQueryBuilder { $qb = $this->getQueryBuilder(); - $qb->update(self::TABLE_CLIENT_APP); + $qb->update(self::TABLE_CLIENT); return $qb; } @@ -84,13 +84,13 @@ class ClientAppRequestBuilder extends CoreRequestBuilder { /** @noinspection PhpMethodParametersCountMismatchInspection */ $qb->select( - 'ca.id', 'ca.name', 'ca.website', 'ca.redirect_uri', 'ca.client_id', 'ca.client_secret', - 'ca.creation' + 'cl.id', 'cl.name', 'cl.website', 'cl.redirect_uris', 'cl.client_id', 'cl.client_secret', + 'cl.scopes', 'cl.creation' ) - ->from(self::TABLE_CLIENT_APP, 'ca'); + ->from(self::TABLE_CLIENT, 'cl'); - $this->defaultSelectAlias = 'ca'; - $qb->setDefaultSelectAlias('ca'); + $this->defaultSelectAlias = 'cl'; + $qb->setDefaultSelectAlias('cl'); return $qb; } @@ -103,7 +103,7 @@ class ClientAppRequestBuilder extends CoreRequestBuilder { */ protected function getClientAppDeleteSql(): SocialQueryBuilder { $qb = $this->getQueryBuilder(); - $qb->delete(self::TABLE_CLIENT_APP); + $qb->delete(self::TABLE_CLIENT); return $qb; } diff --git a/lib/Db/ClientAuthRequest.php b/lib/Db/ClientAuthRequest.php new file mode 100644 index 00000000..cba766c4 --- /dev/null +++ b/lib/Db/ClientAuthRequest.php @@ -0,0 +1,100 @@ +<?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 |