diff --git a/.drone.yml b/.drone.yml index 334b38f2..6f6ec4a8 100644 --- a/.drone.yml +++ b/.drone.yml @@ -4,6 +4,71 @@ clone: depth: 1 pipeline: + check-app-compatbility: + image: nextcloudci/php7.0:php7.0-17 + environment: + - APP_NAME=social + - CORE_BRANCH=stable15 + - DB=sqlite + commands: + # Pre-setup steps + - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh + - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DB + - cd ../server + # Code checker + - ./occ app:check-code $APP_NAME -c strong-comparison + - ./occ app:check-code $APP_NAME -c deprecation + when: + matrix: + TESTS: check-app-compatbility + syntax-php7.0: + image: nextcloudci/php7.0:php7.0-17 + environment: + - APP_NAME=social + - CORE_BRANCH=stable15 + - DB=sqlite + commands: + - composer install + - ./vendor/bin/parallel-lint --exclude ./vendor/ . + when: + matrix: + TESTS: syntax-php7.0 + syntax-php7.1: + image: nextcloudci/php7.1:php7.1-15 + environment: + - APP_NAME=social + - CORE_BRANCH=stable15 + - DB=sqlite + commands: + - composer install + - ./vendor/bin/parallel-lint --exclude ./vendor/ . + when: + matrix: + TESTS: syntax-php7.1 + syntax-php7.2: + image: nextcloudci/php7.2:php7.2-9 + environment: + - APP_NAME=social + - CORE_BRANCH=stable15 + - DB=sqlite + commands: + - composer install + - ./vendor/bin/parallel-lint --exclude ./vendor/ . + when: + matrix: + TESTS: syntax-php7.2 + syntax-php7.3: + image: nextcloudci/php7.3:php7.3-2 + environment: + - APP_NAME=social + - CORE_BRANCH=stable15 + - DB=sqlite + commands: + - composer install + - ./vendor/bin/parallel-lint --exclude ./vendor/ . + when: + matrix: + TESTS: syntax-php7.3 eslint: image: nextcloudci/jsunit:jsunit-5 commands: @@ -12,7 +77,6 @@ pipeline: when: matrix: TESTS: eslint - vue-build: image: nextcloudci/jsunit:jsunit-5 commands: @@ -20,10 +84,15 @@ pipeline: - npm run build when: matrix: - TESTS:vue-build + TESTS: vue-build matrix: include: + - TESTS: check-app-compatbility + - TESTS: syntax-php7.0 + - TESTS: syntax-php7.1 + - TESTS: syntax-php7.2 + - TESTS: syntax-php7.3 - TESTS: eslint - TESTS: vue-build diff --git a/appinfo/info.xml b/appinfo/info.xml index f8d819ed..6520f085 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -26,25 +26,24 @@ Social social https://github.com/nextcloud/social - https://github.com/nextcloud/social.git https://github.com/nextcloud/social/issues + https://github.com/nextcloud/social.git https://raw.githubusercontent.com/nextcloud/social/master/img/screenshot.png - - - Social - social.Navigation.navigate - 6 - - OCA\Social\Cron\Cache OCA\Social\Cron\Queue + + + OCA\Social\Migration\CheckInstallation + + + OCA\Social\Command\CacheRefresh OCA\Social\Command\NoteCreate @@ -52,4 +51,11 @@ OCA\Social\Command\QueueProcess + + + Social + social.Navigation.navigate + 6 + + diff --git a/appinfo/routes.php b/appinfo/routes.php index 6a595a4e..506a89aa 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -65,6 +65,7 @@ return [ ['name' => 'SocialPub#displayPost', 'url' => '/@{username}/{postId}', 'verb' => 'GET'], ['name' => 'Local#streamHome', 'url' => '/api/v1/stream/home', 'verb' => 'GET'], + ['name' => 'Local#streamNotifications', 'url' => '/api/v1/stream/notifications', 'verb' => 'GET'], ['name' => 'Local#streamTimeline', 'url' => '/api/v1/stream/timeline', 'verb' => 'GET'], ['name' => 'Local#streamFederated', 'url' => '/api/v1/stream/federated', 'verb' => 'GET'], ['name' => 'Local#streamDirect', 'url' => '/api/v1/stream/direct', 'verb' => 'GET'], diff --git a/composer.json b/composer.json index 3bf0a3c8..e0b5708d 100644 --- a/composer.json +++ b/composer.json @@ -10,6 +10,10 @@ } ], "require": { - "daita/my-small-php-tools": "dev-master" + "daita/my-small-php-tools": "dev-master", + "digitalbazaar/json-ld": "0.4.7" + }, + "require-dev": { + "jakub-onderka/php-parallel-lint": "^1.0" } } diff --git a/composer.lock b/composer.lock index 9b755307..81e358ae 100644 --- a/composer.lock +++ b/composer.lock @@ -1,10 +1,10 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "02220c2a6087d409a5e53060ee20c47e", + "content-hash": "2c21b8c338f3d2f929f349df19edbc0e", "packages": [ { "name": "daita/my-small-php-tools", @@ -12,12 +12,12 @@ "source": { "type": "git", "url": "https://github.com/daita/my-small-php-tools.git", - "reference": "7a61e966ac2a01db6fd0096f77620a7db978af18" + "reference": "29754f18951856a22c0fd5fc388b6162ea98fe8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/daita/my-small-php-tools/zipball/7a61e966ac2a01db6fd0096f77620a7db978af18", - "reference": "7a61e966ac2a01db6fd0096f77620a7db978af18", + "url": "https://api.github.com/repos/daita/my-small-php-tools/zipball/29754f18951856a22c0fd5fc388b6162ea98fe8a", + "reference": "29754f18951856a22c0fd5fc388b6162ea98fe8a", "shasum": "" }, "require": { @@ -40,10 +40,105 @@ } ], "description": "My small PHP Tools", - "time": "2018-12-04T10:09:31+00:00" + "time": "2018-12-18T00:38:01+00:00" + }, + { + "name": "digitalbazaar/json-ld", + "version": "0.4.7", + "source": { + "type": "git", + "url": "https://github.com/digitalbazaar/php-json-ld.git", + "reference": "dc1bd23f0ee2efd27ccf636d32d2738dabcee182" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/digitalbazaar/php-json-ld/zipball/dc1bd23f0ee2efd27ccf636d32d2738dabcee182", + "reference": "dc1bd23f0ee2efd27ccf636d32d2738dabcee182", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=5.3.0" + }, + "type": "library", + "autoload": { + "files": [ + "jsonld.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Digital Bazaar, Inc.", + "email": "support@digitalbazaar.com" + } + ], + "description": "A JSON-LD Processor and API implementation in PHP.", + "homepage": "https://github.com/digitalbazaar/php-json-ld", + "keywords": [ + "JSON-LD", + "Linked Data", + "RDF", + "Semantic Web", + "json", + "jsonld" + ], + "time": "2016-04-25T04:17:52+00:00" + } + ], + "packages-dev": [ + { + "name": "jakub-onderka/php-parallel-lint", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/JakubOnderka/PHP-Parallel-Lint.git", + "reference": "04fbd3f5fb1c83f08724aa58a23db90bd9086ee8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/JakubOnderka/PHP-Parallel-Lint/zipball/04fbd3f5fb1c83f08724aa58a23db90bd9086ee8", + "reference": "04fbd3f5fb1c83f08724aa58a23db90bd9086ee8", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "jakub-onderka/php-console-highlighter": "~0.3", + "nette/tester": "~1.3", + "squizlabs/php_codesniffer": "~2.7" + }, + "suggest": { + "jakub-onderka/php-console-highlighter": "Highlight syntax in code snippet" + }, + "bin": [ + "parallel-lint" + ], + "type": "library", + "autoload": { + "classmap": [ + "./" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Jakub Onderka", + "email": "ahoj@jakubonderka.cz" + } + ], + "description": "This tool check syntax of PHP files about 20x faster than serial check.", + "homepage": "https://github.com/JakubOnderka/PHP-Parallel-Lint", + "time": "2018-02-24T15:31:20+00:00" } ], - "packages-dev": [], "aliases": [], "minimum-stability": "stable", "stability-flags": { diff --git a/lib/Controller/AccountController.php b/lib/Controller/AccountController.php index e4d51fa9..4d4c119b 100644 --- a/lib/Controller/AccountController.php +++ b/lib/Controller/AccountController.php @@ -126,8 +126,6 @@ class AccountController extends Controller { * * @PublicPage * @NoCSRFRequired - * @NoAdminRequired - * @NoSubAdminRequired * * @param string $username * diff --git a/lib/Controller/ActivityPubController.php b/lib/Controller/ActivityPubController.php index 1a87c56e..a01cf951 100644 --- a/lib/Controller/ActivityPubController.php +++ b/lib/Controller/ActivityPubController.php @@ -34,8 +34,21 @@ use daita\MySmallPhpTools\Traits\Nextcloud\TNCDataResponse; use Exception; use OC\AppFramework\Http; use OCA\Social\AppInfo\Application; +use OCA\Social\Db\NotesRequest; +use OCA\Social\Exceptions\ActivityPubFormatException; +use OCA\Social\Exceptions\InvalidResourceEntryException; +use OCA\Social\Exceptions\InvalidResourceException; +use OCA\Social\Exceptions\Request410Exception; +use OCA\Social\Exceptions\RequestException; use OCA\Social\Exceptions\SignatureIsGoneException; +use OCA\Social\Exceptions\SocialAppConfigException; use OCA\Social\Exceptions\UnknownItemException; +use OCA\Social\Exceptions\UrlCloudException; +use OCA\Social\Model\ActivityPub\ACore; +use OCA\Social\Service\ActivityPub\FollowService; +use OCA\Social\Service\ActivityPub\PersonService; +use OCA\Social\Service\ActivityService; +use OCA\Social\Service\ActorService; use OCA\Social\Service\CacheActorService; use OCA\Social\Service\FollowService; use OCA\Social\Service\ImportService; @@ -166,7 +179,9 @@ class ActivityPubController extends Controller { $origin = $this->signatureService->checkRequest($this->request); $activity = $this->importService->importFromJson($body); - $activity->setOrigin($origin); + if (!$this->activityService->checkObject($activity)) { + $activity->setOrigin($origin); + } try { $this->importService->parseIncomingRequest($activity); @@ -205,7 +220,10 @@ class ActivityPubController extends Controller { // $actor = $this->actorService->getActor($username); $activity = $this->importService->importFromJson($body); - $activity->setOrigin($origin); + if (!$this->activityService->checkObject($activity)) { + $activity->setOrigin($origin); + } + try { $this->importService->parseIncomingRequest($activity); } catch (UnknownItemException $e) { diff --git a/lib/Controller/LocalController.php b/lib/Controller/LocalController.php index 89d0fed3..6ab4994f 100644 --- a/lib/Controller/LocalController.php +++ b/lib/Controller/LocalController.php @@ -163,11 +163,8 @@ class LocalController extends Controller { /** - * Create a new post. + * Delete your own post. * - * // TODO: Delete the NoCSRF check - * - * @NoCSRFRequired * @NoAdminRequired * @NoSubAdminRequired * @@ -193,9 +190,6 @@ class LocalController extends Controller { /** - * // TODO: Delete the NoCSRF check - * - * @NoCSRFRequired * @NoAdminRequired * @NoSubAdminRequired * @@ -216,10 +210,32 @@ class LocalController extends Controller { } + + /** - * // TODO: Delete the NoCSRF check - * * @NoCSRFRequired + * @NoAdminRequired + * @NoSubAdminRequired + * + * @param int $since + * @param int $limit + * + * @return DataResponse + */ + public function streamNotifications($since = 0, int $limit = 5): DataResponse { + try { + $this->initViewer(true); + $posts = $this->noteService->getStreamNotifications($this->viewer, $since, $limit); + + return $this->success($posts); + } catch (Exception $e) { + return $this->fail($e); + } + } + + + + /** * @NoAdminRequired * @NoSubAdminRequired * @@ -244,9 +260,6 @@ class LocalController extends Controller { /** - * // TODO: Delete the NoCSRF check - * - * @NoCSRFRequired * @NoAdminRequired * @NoSubAdminRequired * @@ -270,9 +283,6 @@ class LocalController extends Controller { /** * Get timeline * - * // TODO: Delete the NoCSRF check - * - * @NoCSRFRequired * @NoAdminRequired * @NoSubAdminRequired * @@ -294,9 +304,6 @@ class LocalController extends Controller { /** * Get timeline * - * // TODO: Delete the NoCSRF check - * - * @NoCSRFRequired * @NoAdminRequired * @NoSubAdminRequired * @@ -317,10 +324,6 @@ class LocalController extends Controller { /** - * - * // TODO: Delete the NoCSRF check - * - * @NoCSRFRequired * @NoAdminRequired * @NoSubAdminRequired * @@ -341,10 +344,6 @@ class LocalController extends Controller { /** - * - * // TODO: Delete the NoCSRF check - * - * @NoCSRFRequired * @NoAdminRequired * @NoSubAdminRequired * @@ -365,10 +364,6 @@ class LocalController extends Controller { /** - * - * // TODO: Delete the NoCSRF check - * - * @NoCSRFRequired * @NoAdminRequired * @NoSubAdminRequired * @@ -387,7 +382,6 @@ class LocalController extends Controller { /** - * @NoCSRFRequired * @NoAdminRequired * @NoSubAdminRequired * @@ -408,7 +402,6 @@ class LocalController extends Controller { /** - * @NoCSRFRequired * @NoAdminRequired * @NoSubAdminRequired * @@ -429,10 +422,6 @@ class LocalController extends Controller { /** - * - * // TODO: Delete the NoCSRF check - * - * @NoCSRFRequired * @NoAdminRequired * @NoSubAdminRequired * @@ -454,7 +443,6 @@ class LocalController extends Controller { /** - * @NoCSRFRequired * @NoAdminRequired * @NoSubAdminRequired * @@ -477,7 +465,6 @@ class LocalController extends Controller { /** - * @NoCSRFRequired * @NoAdminRequired * @NoSubAdminRequired * @@ -501,10 +488,6 @@ class LocalController extends Controller { /** - * - * // TODO: Delete the NoCSRF check - * - * @NoCSRFRequired * @NoAdminRequired * @NoSubAdminRequired * @@ -526,10 +509,6 @@ class LocalController extends Controller { /** - * - * // TODO: Delete the NoCSRF check - * - * @NoCSRFRequired * @NoAdminRequired * @NoSubAdminRequired * @@ -549,7 +528,6 @@ class LocalController extends Controller { } /** - * @NoCSRFRequired * @NoAdminRequired * @NoSubAdminRequired * @@ -578,10 +556,6 @@ class LocalController extends Controller { /** - * - * // TODO: Delete the NoCSRF check - * - * @NoCSRFRequired * @NoAdminRequired * @NoSubAdminRequired * @@ -620,9 +594,6 @@ class LocalController extends Controller { /** - * // TODO: Delete the NoCSRF check - * - * @NoCSRFRequired * @NoAdminRequired * @NoSubAdminRequired * diff --git a/lib/Controller/NavigationController.php b/lib/Controller/NavigationController.php index fa19d0f9..a6d55ed9 100644 --- a/lib/Controller/NavigationController.php +++ b/lib/Controller/NavigationController.php @@ -147,6 +147,7 @@ class NavigationController extends Controller { try { $data['serverData']['cloudAddress'] = $this->configService->getCloudAddress(); } catch (SocialAppConfigException $e) { + $this->checkService->checkInstallationStatus(); $cloudAddress = $this->setupCloudAddress(); if ($cloudAddress !== '') { $data['serverData']['cloudAddress'] = $cloudAddress; @@ -221,8 +222,6 @@ class NavigationController extends Controller { * * @NoCSRFRequired * @PublicPage - * @NoAdminRequired - * @NoSubAdminRequired * * @return DataResponse */ @@ -278,10 +277,6 @@ class NavigationController extends Controller { /** - * - * // TODO: Delete the NoCSRF check - * - * @NoCSRFRequired * @NoAdminRequired * @NoSubAdminRequired * @@ -302,13 +297,8 @@ class NavigationController extends Controller { /** - * - * // TODO: Delete the NoCSRF check - * * @PublicPage * @NoCSRFRequired - * @NoAdminRequired - * @NoSubAdminRequired * * @param string $id * diff --git a/lib/Controller/QueueController.php b/lib/Controller/QueueController.php index c9de7c32..dfdde52b 100644 --- a/lib/Controller/QueueController.php +++ b/lib/Controller/QueueController.php @@ -86,12 +86,8 @@ class QueueController extends Controller { /** - * // TODO: Delete the NoCSRF check - * * @PublicPage * @NoCSRFRequired - * @NoAdminRequired - * @NoSubAdminRequired * * @param string $token */ diff --git a/lib/Db/FollowsRequest.php b/lib/Db/FollowsRequest.php index eb8789ed..7cec6859 100644 --- a/lib/Db/FollowsRequest.php +++ b/lib/Db/FollowsRequest.php @@ -143,6 +143,20 @@ class FollowsRequest extends FollowsRequestBuilder { } + /** + * @return int + */ + public function countFollows() { + $qb = $this->countFollowsSelectSql(); + + $cursor = $qb->execute(); + $data = $cursor->fetch(); + $cursor->closeCursor(); + + return $this->getInt('count', $data, 0); + } + + /** * @param string $followId * diff --git a/lib/Db/NotesRequest.php b/lib/Db/NotesRequest.php index fa2f77b2..cbc825fa 100644 --- a/lib/Db/NotesRequest.php +++ b/lib/Db/NotesRequest.php @@ -133,7 +133,7 @@ class NotesRequest extends NotesRequestBuilder { $cursor->closeCursor(); if ($data === false) { - throw new NoteNotFoundException(); + throw new NoteNotFoundException('Post not found'); } return $this->parseNotesSelectSql($data); @@ -186,10 +186,42 @@ class NotesRequest extends NotesRequestBuilder { } + /** + * Should returns: + * * Public/Unlisted/Followers-only post where current $actor is tagged, + * - Events: (not yet) + * - people liking or re-posting your posts (not yet) + * - someone wants to follow you (not yet) + * - someone is following you (not yet) + * + * @param Person $actor + * @param int $since + * @param int $limit + * + * @return array + */ + public function getStreamNotifications(Person $actor, int $since = 0, int $limit = 5): array { + $qb = $this->getNotesSelectSql(); + + $this->limitPaginate($qb, $since, $limit); + $this->limitToRecipient($qb, $actor->getId(), false); + $this->leftJoinCacheActors($qb, 'attributed_to'); + + $notes = []; + $cursor = $qb->execute(); + while ($data = $cursor->fetch()) { + $notes[] = $this->parseNotesSelectSql($data); + } + $cursor->closeCursor(); + + return $notes; + } + + /** * Should returns: * * public message from actorId. - * - to followers-only if follower is logged. + * - to followers-only if follower is logged. (not yet (check ?)) * * @param string $actorId * @param int $since @@ -218,7 +250,7 @@ class NotesRequest extends NotesRequestBuilder { /** * Should returns: * * Private message. - * - group messages. + * - group messages. (not yet) * * @param Person $actor * @param int $since @@ -249,7 +281,7 @@ class NotesRequest extends NotesRequestBuilder { /** * Should returns: - * - All local public/federated posts + * * All local public/federated posts * * @param int $since * @param int $limit @@ -266,7 +298,7 @@ class NotesRequest extends NotesRequestBuilder { } $this->leftJoinCacheActors($qb, 'attributed_to'); // TODO: to: = real public, cc: = unlisted !? - $this->limitToRecipient($qb, ActivityService::TO_PUBLIC); + $this->limitToRecipient($qb, ActivityService::TO_PUBLIC, true, ['to']); $notes = []; $cursor = $qb->execute(); diff --git a/lib/Db/NotesRequestBuilder.php b/lib/Db/NotesRequestBuilder.php index 4adaf37a..443deb8e 100644 --- a/lib/Db/NotesRequestBuilder.php +++ b/lib/Db/NotesRequestBuilder.php @@ -138,7 +138,7 @@ class NotesRequestBuilder extends CoreRequestBuilder { $pf = $this->defaultSelectAlias . '.'; $on = $expr->orX(); - $on->add($this->exprLimitToRecipient($qb, $actor->getFollowers(), true)); + $on->add($this->exprLimitToRecipient($qb, $actor->getFollowers(), false)); // list of possible recipient as a follower (to, to_array, cc, ...) $recipientFields = $expr->orX(); @@ -230,11 +230,12 @@ class NotesRequestBuilder extends CoreRequestBuilder { * @param IQueryBuilder $qb * @param string $recipient * @param bool $asAuthor + * @param array $type */ protected function limitToRecipient( - IQueryBuilder &$qb, string $recipient, bool $asAuthor = false + IQueryBuilder &$qb, string $recipient, bool $asAuthor = false, array $type = [] ) { - $qb->andWhere($this->exprLimitToRecipient($qb, $recipient, $asAuthor)); + $qb->andWhere($this->exprLimitToRecipient($qb, $recipient, $asAuthor, $type)); } @@ -242,11 +243,12 @@ class NotesRequestBuilder extends CoreRequestBuilder { * @param IQueryBuilder $qb * @param string $recipient * @param bool $asAuthor + * @param array $type * * @return ICompositeExpression */ protected function exprLimitToRecipient( - IQueryBuilder &$qb, string $recipient, bool $asAuthor = false + IQueryBuilder &$qb, string $recipient, bool $asAuthor = false, array $type = [] ): ICompositeExpression { $expr = $qb->expr(); @@ -262,15 +264,42 @@ class NotesRequestBuilder extends CoreRequestBuilder { ); } - $limit->add($expr->eq('to', $qb->createNamedParameter($recipient))); - $limit->add($this->exprValueWithinJsonFormat($qb, 'to_array', $recipient)); - $limit->add($this->exprValueWithinJsonFormat($qb, 'cc', $recipient)); - $limit->add($this->exprValueWithinJsonFormat($qb, 'bcc', $recipient)); + if ($type === []) { + $type = ['to', 'cc', 'bcc']; + } + + $this->addLimitToRecipient($qb, $limit, $type, $recipient); return $limit; } + /** + * @param IQueryBuilder $qb + * @param ICompositeExpression $limit + * @param array $type + * @param string $to + */ + private function addLimitToRecipient( + IQueryBuilder $qb, ICompositeExpression &$limit, array $type, string $to + ) { + + $expr = $qb->expr(); + if (in_array('to', $type)) { + $limit->add($expr->eq('to', $qb->createNamedParameter($to))); + $limit->add($this->exprValueWithinJsonFormat($qb, 'to_array', $to)); + } + + if (in_array('cc', $type)) { + $limit->add($this->exprValueWithinJsonFormat($qb, 'cc', $to)); + } + + if (in_array('bcc', $type)) { + $limit->add($this->exprValueWithinJsonFormat($qb, 'bcc', $to)); + } + } + + /** * @param IQueryBuilder $qb * @param string $recipient diff --git a/lib/Exceptions/LinkedDataSignatureMissingException.php b/lib/Exceptions/LinkedDataSignatureMissingException.php new file mode 100644 index 00000000..f49db7b6 --- /dev/null +++ b/lib/Exceptions/LinkedDataSignatureMissingException.php @@ -0,0 +1,8 @@ + + * @copyright 2018, Maxence Lange + * @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 . + * + */ + + +namespace OCA\Social\Migration; + + +use OCA\Social\Service\CheckService; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; + + +/** + * Class CheckInstallation + * + * @package OCA\Social\Migration + */ +class CheckInstallation implements IRepairStep { + + + /** @var CheckService */ + protected $checkService; + + + /** + * CheckInstallation constructor. + * + * @param CheckService $checkService + */ + public function __construct(CheckService $checkService) { + $this->checkService = $checkService; + } + + + /** + * Returns the step's name + * + * @return string + * @since 9.1.0 + */ + public function getName() { + return 'Check the installation of the Social app.'; + } + + + /** + * @param IOutput $output + */ + public function run(IOutput $output) { + $this->checkService->checkInstallationStatus(); + } + + +} diff --git a/lib/Model/ActivityPub/ACore.php b/lib/Model/ActivityPub/ACore.php index aded0ddb..a9d69ec1 100644 --- a/lib/Model/ActivityPub/ACore.php +++ b/lib/Model/ActivityPub/ACore.php @@ -37,6 +37,8 @@ use OCA\Social\Exceptions\ActivityCantBeVerifiedException; use OCA\Social\Exceptions\InvalidOriginException; use OCA\Social\Exceptions\InvalidResourceEntryException; use OCA\Social\Exceptions\UrlCloudException; +use OCA\Social\Model\LinkedDataSignature; +use OCA\Social\Service\ActivityPub\ICoreService; use OCA\Social\Model\ActivityPub\Object\Document; @@ -72,6 +74,10 @@ class ACore extends Item implements JsonSerializable { private $icon = null; + /** @var LinkedDataSignature */ + private $signature = null; + + /** * Core constructor. * @@ -165,14 +171,45 @@ class ACore extends Item implements JsonSerializable { } + /** + * @return bool + */ + public function gotSignature(): bool { + return ($this->signature !== null); + } + + /** + * @return LinkedDataSignature + */ + public function getSignature(): LinkedDataSignature { + return $this->signature; + } + + /** + * @param LinkedDataSignature $signature + * + * @return ACore + */ + public function setSignature(LinkedDataSignature $signature): Acore { + $this->signature = $signature; + + return $this; + } + + /** * @param string $base + * @param bool $root * * @throws UrlCloudException */ - public function generateUniqueId(string $base = '') { - if ($this->getUrlCloud() === '') { - throw new UrlCloudException(); + public function generateUniqueId(string $base = '', bool $root = true) { + $url = ''; + if ($root) { + $url = $this->getUrlCloud(); + if ($url === '') { + throw new UrlCloudException(); + } } if ($base !== '') { @@ -185,7 +222,7 @@ class ACore extends Item implements JsonSerializable { mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff) ); - $this->setId($this->getUrlCloud() . $base . '/' . $uuid); + $this->setId($url . $base . '/' . $uuid); } @@ -490,13 +527,21 @@ class ACore extends Item implements JsonSerializable { * @return array */ public function jsonSerialize(): array { + + if ($this->gotSignature()) { + $this->entries['signature'] = $this->getSignature(); + } + if ($this->isRoot()) { - $this->addEntryArray( - '@context', [ - self::CONTEXT_ACTIVITYSTREAMS, - self::CONTEXT_SECURITY - ] - ); + $context = [self::CONTEXT_ACTIVITYSTREAMS]; + + if ($this->gotObject() + && $this->getObject() + ->gotSignature()) { + array_push($context, self::CONTEXT_SECURITY); + } + + $this->addEntryArray('@context', $context); } $this->addEntry('id', $this->getId()); @@ -523,7 +568,6 @@ class ACore extends Item implements JsonSerializable { $this->addEntry('published', $this->getPublished()); $this->addEntryArray('tag', $this->getTags()); -// $arr = $this->getEntries(); if ($this->gotObject()) { $this->addEntryItem('object', $this->getObject()); } else { diff --git a/lib/Model/ActivityPub/Activity/Accept.php b/lib/Model/ActivityPub/Activity/Accept.php index 9d310c2d..af82c9ec 100644 --- a/lib/Model/ActivityPub/Activity/Accept.php +++ b/lib/Model/ActivityPub/Activity/Accept.php @@ -71,11 +71,7 @@ class Accept extends ACore implements JsonSerializable { * @return array */ public function jsonSerialize(): array { - return array_merge( - parent::jsonSerialize(), - [ - ] - ); + return parent::jsonSerialize(); } } diff --git a/lib/Model/ActivityPub/Activity/Create.php b/lib/Model/ActivityPub/Activity/Create.php index a2b8690f..42a77f87 100644 --- a/lib/Model/ActivityPub/Activity/Create.php +++ b/lib/Model/ActivityPub/Activity/Create.php @@ -70,11 +70,7 @@ class Create extends ACore implements JsonSerializable { * @return array */ public function jsonSerialize(): array { - return array_merge( - parent::jsonSerialize(), - [ - ] - ); + return parent::jsonSerialize(); } } diff --git a/lib/Model/ActivityPub/Activity/Delete.php b/lib/Model/ActivityPub/Activity/Delete.php index 9e23d76e..69c20037 100644 --- a/lib/Model/ActivityPub/Activity/Delete.php +++ b/lib/Model/ActivityPub/Activity/Delete.php @@ -71,11 +71,7 @@ class Delete extends ACore implements JsonSerializable { * @return array */ public function jsonSerialize(): array { - return array_merge( - parent::jsonSerialize(), - [ - ] - ); + return parent::jsonSerialize(); } } diff --git a/lib/Model/ActivityPub/Activity/Follow.php b/lib/Model/ActivityPub/Activity/Follow.php index 1d35680a..ebb526b3 100644 --- a/lib/Model/ActivityPub/Activity/Follow.php +++ b/lib/Model/ActivityPub/Activity/Follow.php @@ -126,13 +126,19 @@ class Follow extends ACore implements JsonSerializable { * @return array */ public function jsonSerialize(): array { - return array_merge( - parent::jsonSerialize(), - [ - 'follow_id' => $this->getFollowId(), - 'accepted' => $this->isAccepted() - ] - ); + $result = parent::jsonSerialize(); + + if ($this->isCompleteDetails()) { + array_merge( + $result, + [ + 'follow_id' => $this->getFollowId(), + 'accepted' => $this->isAccepted() + ] + ); + } + + return $result; } } diff --git a/lib/Model/ActivityPub/Activity/Reject.php b/lib/Model/ActivityPub/Activity/Reject.php index 5ceb2808..77accf20 100644 --- a/lib/Model/ActivityPub/Activity/Reject.php +++ b/lib/Model/ActivityPub/Activity/Reject.php @@ -71,11 +71,7 @@ class Reject extends ACore implements JsonSerializable { * @return array */ public function jsonSerialize(): array { - return array_merge( - parent::jsonSerialize(), - [ - ] - ); + return parent::jsonSerialize(); } } diff --git a/lib/Model/ActivityPub/Activity/Undo.php b/lib/Model/ActivityPub/Activity/Undo.php index ec1cfee4..806969de 100644 --- a/lib/Model/ActivityPub/Activity/Undo.php +++ b/lib/Model/ActivityPub/Activity/Undo.php @@ -70,11 +70,7 @@ class Undo extends ACore implements JsonSerializable { * @return array */ public function jsonSerialize(): array { - return array_merge( - parent::jsonSerialize(), - [ - ] - ); + return parent::jsonSerialize(); } } diff --git a/lib/Model/ActivityPub/Object/Image.php b/lib/Model/ActivityPub/Object/Image.php index be7fd266..6d9399e2 100644 --- a/lib/Model/ActivityPub/Object/Image.php +++ b/lib/Model/ActivityPub/Object/Image.php @@ -75,11 +75,7 @@ class Image extends Document implements JsonSerializable { * @return array */ public function jsonSerialize(): array { - return array_merge( - parent::jsonSerialize(), - [ - ] - ); + return parent::jsonSerialize(); } } diff --git a/lib/Model/ActivityPub/Object/Note.php b/lib/Model/ActivityPub/Object/Note.php index 8b5c7e87..185f4ec9 100644 --- a/lib/Model/ActivityPub/Object/Note.php +++ b/lib/Model/ActivityPub/Object/Note.php @@ -35,6 +35,7 @@ use JsonSerializable; use OCA\Social\Model\ActivityPub\ACore; use OCA\Social\Service\ActivityService; + class Note extends ACore implements JsonSerializable { diff --git a/lib/Model/ActivityPub/Object/Tombstone.php b/lib/Model/ActivityPub/Object/Tombstone.php index 6247f37d..2e48b22b 100644 --- a/lib/Model/ActivityPub/Object/Tombstone.php +++ b/lib/Model/ActivityPub/Object/Tombstone.php @@ -71,11 +71,7 @@ class Tombstone extends ACore implements JsonSerializable { * @return array */ public function jsonSerialize(): array { - return array_merge( - parent::jsonSerialize(), - [ - ] - ); + return parent::jsonSerialize(); } } diff --git a/lib/Model/LinkedDataSignature.php b/lib/Model/LinkedDataSignature.php new file mode 100644 index 00000000..689a5091 --- /dev/null +++ b/lib/Model/LinkedDataSignature.php @@ -0,0 +1,318 @@ + + * @copyright 2018, Maxence Lange + * @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 . + * + */ + + +namespace OCA\Social\Model; + + +use daita\MySmallPhpTools\Traits\TArrayTools; +use JsonSerializable; +use OCA\Social\Exceptions\LinkedDataSignatureMissingException; +use OCA\Social\Model\ActivityPub\ACore; + + +/** + * Class InstancePath + * + * @package OCA\Social\Model + */ +class LinkedDataSignature implements JsonSerializable { + + + use TArrayTools; + + /** @var string */ + private $type = ''; + + /** @var string */ + private $creator = ''; + + /** @var string */ + private $created = ''; + + /** @var string */ + private $signatureValue = ''; + + /** @var string */ + private $privateKey = ''; + + /** @var string */ + private $publicKey = ''; + + /** @var array */ + private $object = []; + + + /** + * LinkedDataSignature constructor. + */ + public function __construct() { + } + + /** + * @return string + */ + public function getType(): string { + return $this->type; + } + + /** + * @param string $type + * + * @return LinkedDataSignature + */ + public function setType(string $type): LinkedDataSignature { + $this->type = $type; + + return $this; + } + + /** + * @return string + */ + public function getCreator(): string { + return $this->creator; + } + + /** + * @param string $creator + * + * @return LinkedDataSignature + */ + public function setCreator(string $creator): LinkedDataSignature { + $this->creator = $creator; + + return $this; + } + + /** + * @return string + */ + public function getCreated(): string { + return $this->created; + } + + /** + * @param string $created + * + * @return LinkedDataSignature + */ + public function setCreated(string $created): LinkedDataSignature { + $this->created = $created; + + return $this; + } + + /** + * @return string + */ + public function getSignatureValue(): string { + return $this->signatureValue; + } + + /** + * @param string $signatureValue + * + * @return LinkedDataSignature + */ + public function setSignatureValue(string $signatureValue): LinkedDataSignature { + $this->signatureValue = $signatureValue; + + return $this; + } + + + /** + * @return array + */ + public function getObject(): array { + return $this->object; + } + + /** + * @param array $object + * + * @return LinkedDataSignature + */ + public function setObject(array $object): LinkedDataSignature { + $this->object = $object; + + return $this; + } + + + /** + * @return string + */ + public function getPrivateKey(): string { + return $this->privateKey; + } + + /** + * @param string $privateKey + * + * @return LinkedDataSignature + */ + public function setPrivateKey(string $privateKey): LinkedDataSignature { + $this->privateKey = $privateKey; + + return $this; + } + + + /** + * @return string + */ + public function getPublicKey(): string { + return $this->publicKey; + } + + /** + * @param string $publicKey + * + * @return LinkedDataSignature + */ + public function setPublicKey(string $publicKey): LinkedDataSignature { + $this->publicKey = $publicKey; + + return $this; + } + + + /** + * @throws LinkedDataSignatureMissingException + */ + public function sign() { + $header = [ + '@context' => 'https://w3id.org/identity/v1', + 'creator' => $this->getCreator(), + 'created' => $this->getCreated() + ]; + + $hash = $this->hashedCanonicalize($header) . $this->hashedCanonicalize($this->getObject()); + + $algo = OPENSSL_ALGO_SHA256; + if ($this->getType() === 'RsaSignature2017') { + $algo = OPENSSL_ALGO_SHA256; + } + + if (!openssl_sign($hash, $signed, $this->getPrivateKey(), $algo)) { + throw new LinkedDataSignatureMissingException(); + } + + $this->setSignatureValue(base64_encode($signed)); + } + + + /** + * @return bool + */ + public function verify(): bool { + + $header = [ + '@context' => 'https://w3id.org/identity/v1', + 'creator' => $this->getCreator(), + 'created' => $this->getCreated() + ]; + + $hash = $this->hashedCanonicalize($header) . $this->hashedCanonicalize($this->getObject()); + $signed = base64_decode($this->getSignatureValue()); + + $algo = OPENSSL_ALGO_SHA256; + if ($this->getType() === 'RsaSignature2017') { + $algo = OPENSSL_ALGO_SHA256; + } + + if (openssl_verify($hash, $signed, $this->getPublicKey(), $algo) === 1) { + return true; + } + + return false; + } + + /** + * @param array $data + * + * @return string + */ + private function hashedCanonicalize(array $data): string { + $object = json_decode(json_encode($data), false); + $res = jsonld_normalize( + $object, + [ + 'algorithm' => 'URDNA2015', + 'format' => 'application/nquads' + ] + ); + + return hash('sha256', $res); + } + + + /** + * @param array $data + * + * @throws LinkedDataSignatureMissingException + */ + public function import(array $data) { + + if (!in_array(ACore::CONTEXT_SECURITY, $this->getArray('@context', $data, []))) { + throw new LinkedDataSignatureMissingException(); + } + + $signature = $this->getArray('signature', $data, []); + if ($signature === []) { + throw new LinkedDataSignatureMissingException(); + } + + $this->setType($this->get('type', $signature, '')); + $this->setCreator($this->get('creator', $signature, '')); + $this->setCreated($this->get('created', $signature, '')); + $this->setSignatureValue($this->get('signatureValue', $signature, '')); + + unset($data['signature']); + + $this->setObject($data); + } + + + /** + * @return array + */ + public function jsonSerialize(): array { + return [ + 'type' => $this->getType(), + 'creator' => $this->getCreator(), + 'created' => $this->getCreated(), + 'signatureValue' => $this->getSignatureValue() + ]; + } + +} + diff --git a/lib/Service/ActivityService.php b/lib/Service/ActivityService.php index 585d62dd..8aa943c7 100644 --- a/lib/Service/ActivityService.php +++ b/lib/Service/ActivityService.php @@ -39,6 +39,7 @@ use OCA\Social\Db\NotesRequest; use OCA\Social\Exceptions\ActorDoesNotExistException; use OCA\Social\Exceptions\EmptyQueueException; use OCA\Social\Exceptions\InvalidResourceException; +use OCA\Social\Exceptions\LinkedDataSignatureMissingException; use OCA\Social\Exceptions\NoHighPriorityRequestException; use OCA\Social\Exceptions\QueueStatusException; use OCA\Social\Exceptions\Request410Exception; @@ -51,6 +52,7 @@ use OCA\Social\Model\ActivityPub\Activity\Delete; use OCA\Social\Model\ActivityPub\Actor\Person; use OCA\Social\Model\ActivityPub\Object\Tombstone; use OCA\Social\Model\InstancePath; +use OCA\Social\Model\LinkedDataSignature; use OCA\Social\Model\RequestQueue; class ActivityService { @@ -65,9 +67,6 @@ class ActivityService { const TIMEOUT_ASYNC = 5; const TIMEOUT_SERVICE = 10; - const CONTEXT_ACTIVITYSTREAMS = 'https://www.w3.org/ns/activitystreams'; - const CONTEXT_SECURITY = 'https://w3id.org/security/v1'; - const TO_PUBLIC = 'https://www.w3.org/ns/activitystreams#Public'; const DATE_FORMAT = 'D, d M Y H:i:s T'; @@ -137,6 +136,7 @@ class ActivityService { public function createActivity(Person $actor, ACore $item, ACore &$activity = null): string { $activity = new Create(); + $item->setParent($activity); // $this->activityStreamsService->initCore($activity); @@ -151,6 +151,7 @@ class ActivityService { // } $activity->setActor($actor); + $this->signObject($actor, $activity); return $this->request($activity); } @@ -393,6 +394,96 @@ class ActivityService { /** + * @param IRequest $request + * + * @return string + * @throws InvalidResourceException + * @throws MalformedArrayException + * @throws RequestException + * @throws SignatureException + * @throws SocialAppConfigException + * @throws UrlCloudException + * @throws SignatureIsGoneException + * @throws InvalidOriginException + */ + public function checkRequest(IRequest $request): string { + // TODO : check host is our current host. + +// $host = $request->getHeader('host'); +// if ($host === '') { +// throw new SignatureException('host is not set'); +// } + + $dTime = new DateTime($request->getHeader('date')); + $dTime->format(self::DATE_FORMAT); + + if ($dTime->getTimestamp() < (time() - self::DATE_DELAY)) { + throw new SignatureException('object is too old'); + } + + try { + $origin = $this->checkSignature($request); + } catch (Request410Exception $e) { + throw new SignatureIsGoneException(); + } + + return $origin; + } + + + /** + * @param Person $actor + * @param ACore $object + */ + public function signObject(Person $actor, ACore &$object) { + $signature = new LinkedDataSignature(); + $signature->setPrivateKey($actor->getPrivateKey()); + $signature->setType('RsaSignature2017'); + $signature->setCreator($actor->getId() . '#main-key'); + $signature->setCreated($object->getPublished()); + $signature->setObject(json_decode(json_encode($object), true)); + + try { + $signature->sign(); + $object->setSignature($signature); + } catch (LinkedDataSignatureMissingException $e) { + } + } + + + /** + * @param ACore $object + * + * @return bool + * @throws InvalidResourceException + * @throws Request410Exception + * @throws RequestException + * @throws SocialAppConfigException + * @throws UrlCloudException + * @throws InvalidOriginException + */ + public function checkObject(ACore $object): bool { + try { + $actorId = $object->getActorId(); + + $signature = new LinkedDataSignature(); + $signature->import(json_decode($object->getSource(), true)); + $signature->setPublicKey($this->retrieveKey($actorId)); + + if ($signature->verify()) { + $object->setOrigin($this->getKeyOrigin($actorId)); + + return true; + } + } catch (LinkedDataSignatureMissingException $e) { + } + + return false; + } + + + /** + * $signature = new LinkedDataSignature(); * @param ACore $activity * * @return string diff --git a/lib/Service/CacheDocumentService.php b/lib/Service/CacheDocumentService.php index d3d8702b..5bf92f0a 100644 --- a/lib/Service/CacheDocumentService.php +++ b/lib/Service/CacheDocumentService.php @@ -103,7 +103,6 @@ class CacheDocumentService { $content = $this->retrieveContent($url); - // TODO - get mime type in a better way. // To get the mime type, we create a temp file $tmpFile = tmpfile(); $tmpPath = stream_get_meta_data($tmpFile)['uri']; diff --git a/lib/Service/CheckService.php b/lib/Service/CheckService.php index dea98418..306f13e0 100644 --- a/lib/Service/CheckService.php +++ b/lib/Service/CheckService.php @@ -24,6 +24,9 @@ namespace OCA\Social\Service; +use daita\MySmallPhpTools\Traits\TStringTools; +use OCA\Social\Db\FollowsRequest; +use OCA\Social\Model\ActivityPub\Follow; use OCP\AppFramework\Http; use OCP\Http\Client\IClientService; use OCP\ICache; @@ -31,25 +34,56 @@ use OCP\IConfig; use OCP\IRequest; use OCP\IURLGenerator; + +/** + * Class CheckService + * + * @package OCA\Social\Service + */ class CheckService { + + use TStringTools; + + private $cache; private $config; private $clientService; private $request; private $urlGenerator; + /** @var FollowsRequest */ + private $followRequest; + const CACHE_PREFIX = 'social_check_'; - public function __construct(ICache $cache, IConfig $config, IClientService $clientService, IRequest $request, IURLGenerator $urlGenerator) { + /** + * CheckService constructor. + * + * @param ICache $cache + * @param IConfig $config + * @param IClientService $clientService + * @param IRequest $request + * @param IURLGenerator $urlGenerator + * @param FollowsRequest $followRequest + */ + public function __construct( + ICache $cache, IConfig $config, IClientService $clientService, IRequest $request, + IURLGenerator $urlGenerator, FollowsRequest $followRequest + ) { $this->cache = $cache; $this->config = $config; $this->clientService = $clientService; $this->request = $request; $this->urlGenerator = $urlGenerator; + $this->followRequest = $followRequest; } + + /** + * @return array + */ public function checkDefault(): array { $checks = []; $checks['wellknown'] = $this->checkWellKnown(); @@ -60,13 +94,19 @@ class CheckService { $success = false; } } + return [ 'success' => $success, - 'checks' => $checks + 'checks' => $checks ]; } + + + /** + * @return bool + */ public function checkWellKnown(): bool { - $state = (bool) ($this->cache->get(self::CACHE_PREFIX . 'wellknown') === 'true'); + $state = (bool)($this->cache->get(self::CACHE_PREFIX . 'wellknown') === 'true'); if ($state === true) { return true; } @@ -77,7 +117,9 @@ class CheckService { return true; } - if ($this->requestWellKnown($this->request->getServerProtocol() . '://' . $this->request->getServerHost())) { + if ($this->requestWellKnown( + $this->request->getServerProtocol() . '://' . $this->request->getServerHost() + )) { return true; } @@ -88,17 +130,50 @@ class CheckService { return false; } - private function requestWellKnown($base) { + + /** + * + */ + public function checkInstallationStatus() { + $this->checkStatusTableFollows(); + } + + + public function checkStatusTableFollows() { + if ($this->followRequest->countFollows() > 0) { + return; + } + + $follow = new Follow(); + $follow->setId($this->uuid()); + $follow->setType('Unknown'); + $follow->setActorId($this->uuid()); + $follow->setObjectId($this->uuid()); + $follow->setFollowId($this->uuid()); + + $this->followRequest->save($follow); + } + + + /** + * @param string $base + * + * @return bool + */ + private function requestWellKnown(string $base) { try { $url = $base . '/.well-known/webfinger'; - $response = $this->clientService->newClient()->get($url); + $response = $this->clientService->newClient() + ->get($url); if ($response->getStatusCode() === Http::STATUS_OK) { $this->cache->set(self::CACHE_PREFIX . 'wellknown', 'true', 3600); + return true; } } catch (\GuzzleHttp\Exception\ClientException $e) { } catch (\Exception $e) { } + return false; } diff --git a/lib/Service/ImportService.php b/lib/Service/ImportService.php index 9ba9085f..935a2cb9 100644 --- a/lib/Service/ImportService.php +++ b/lib/Service/ImportService.php @@ -39,6 +39,20 @@ use OCA\Social\Exceptions\RedundancyLimitException; use OCA\Social\Exceptions\SocialAppConfigException; use OCA\Social\Exceptions\UnknownItemException; use OCA\Social\Model\ActivityPub\ACore; +use OCA\Social\Model\ActivityPub\Activity\Accept; +use OCA\Social\Model\ActivityPub\Activity\Create; +use OCA\Social\Model\ActivityPub\Activity\Delete; +use OCA\Social\Model\ActivityPub\Activity\Reject; +use OCA\Social\Model\ActivityPub\Tombstone; +use OCA\Social\Model\ActivityPub\Document; +use OCA\Social\Model\ActivityPub\Follow; +use OCA\Social\Model\ActivityPub\Image; +use OCA\Social\Model\ActivityPub\Note; +use OCA\Social\Model\ActivityPub\Activity\Undo; +use OCA\Social\Service\ActivityPub\DeleteService; +use OCA\Social\Service\ActivityPub\FollowService; +use OCA\Social\Service\ActivityPub\NoteService; +use OCA\Social\Service\ActivityPub\UndoService; class ImportService { @@ -70,10 +84,14 @@ class ImportService { * @param string $json * * @return ACore + * @throws ActivityPubFormatException + * @throws InvalidResourceEntryException + * @throws SocialAppConfigException * @throws UnknownItemException * @throws SocialAppConfigException * @throws ActivityPubFormatException * @throws RedundancyLimitException + * @throws UrlCloudException */ public function importFromJson(string $json): ACore { $data = json_decode($json, true); diff --git a/lib/Service/NoteService.php b/lib/Service/NoteService.php index 7cf1486b..2b2d8384 100644 --- a/lib/Service/NoteService.php +++ b/lib/Service/NoteService.php @@ -30,14 +30,21 @@ declare(strict_types=1); namespace OCA\Social\Service; +use daita\MySmallPhpTools\Exceptions\MalformedArrayException; use Exception; use OC\User\NoUserException; use OCA\Social\Db\NotesRequest; use OCA\Social\Exceptions\AccountAlreadyExistsException; use OCA\Social\Exceptions\ActorDoesNotExistException; +use OCA\Social\Exceptions\InvalidOriginException; use OCA\Social\Exceptions\NoteNotFoundException; +use OCA\Social\Exceptions\RedundancyLimitException; use OCA\Social\Exceptions\SocialAppConfigException; +use OCA\Social\Exceptions\UnknownItemException; use OCA\Social\Exceptions\UrlCloudException; +use OCA\Social\Exceptions\InvalidResourceException; +use OCA\Social\Exceptions\Request410Exception; +use OCA\Social\Exceptions\RequestException; use OCA\Social\Model\ActivityPub\Actor\Person; use OCA\Social\Model\ActivityPub\Object\Note; use OCA\Social\Model\InstancePath; @@ -79,12 +86,9 @@ class NoteService { * @param MiscService $miscService */ public function __construct( - NotesRequest $notesRequest, - ActivityService $activityService, - AccountService $accountService, - CacheActorService $cacheActorService, - ConfigService $configService, - MiscService $miscService + NotesRequest $notesRequest, ActivityService $activityService, + AccountService $accountService, CacheActorService $cacheActorService, + ConfigService $configService, MiscService $miscService ) { $this->notesRequest = $notesRequest; $this->activityService = $activityService; @@ -241,16 +245,29 @@ class NoteService { /** * @param Note $note * @param string $replyTo + * + * @throws InvalidOriginException + * @throws InvalidResourceException + * @throws MalformedArrayException + * @throws NoteNotFoundException + * @throws RedundancyLimitException + * @throws Request410Exception + * @throws RequestException + * @throws SocialAppConfigException + * @throws UnknownItemException */ public function replyTo(Note $note, string $replyTo) { if ($replyTo === '') { return; } + $author = $this->getAuthorFromPostId($replyTo); $note->setInReplyTo($replyTo); // TODO - type can be NOT public ! $note->addInstancePath( - new InstancePath($replyTo, InstancePath::TYPE_PUBLIC, InstancePath::PRIORITY_HIGH) + new InstancePath( + $author->getSharedInbox(), InstancePath::TYPE_INBOX, InstancePath::PRIORITY_HIGH + ) ); } @@ -294,6 +311,18 @@ class NoteService { } + /** + * @param Person $actor + * @param int $since + * @param int $limit + * + * @return Note[] + */ + public function getStreamNotifications(Person $actor, int $since = 0, int $limit = 5): array { + return $this->notesRequest->getStreamNotifications($actor, $since, $limit); + } + + /** * @param string $actorId * @param int $since @@ -352,5 +381,25 @@ class NoteService { } + /** + * @param $noteId + * + * @return Person + * @throws InvalidResourceException + * @throws MalformedArrayException + * @throws NoteNotFoundException + * @throws Request410Exception + * @throws RequestException + * @throws SocialAppConfigException + * @throws InvalidOriginException + * @throws RedundancyLimitException + * @throws UnknownItemException + */ + public function getAuthorFromPostId($noteId) { + $note = $this->notesRequest->getNoteById($noteId); + + return $this->cacheActorService->getFromId($note->getAttributedTo()); + } + } diff --git a/package-lock.json b/package-lock.json index 801247eb..16a2cdbc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,25 +14,115 @@ } }, "@babel/core": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.1.2.tgz", - "integrity": "sha512-IFeSSnjXdhDaoysIlev//UzHZbdEmm7D0EIH2qtse9xK7mXEZQpYjs2P00XlP1qYsYvid79p+Zgg6tz1mp6iVw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.2.2.tgz", + "integrity": "sha512-59vB0RWt09cAct5EIe58+NzGP4TFSD3Bz//2/ELy3ZeTeKF6VTD1AXlH8BGGbCX0PuobZBsIzO7IAI9PH67eKw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.1.2", - "@babel/helpers": "^7.1.2", - "@babel/parser": "^7.1.2", - "@babel/template": "^7.1.2", - "@babel/traverse": "^7.1.0", - "@babel/types": "^7.1.2", + "@babel/generator": "^7.2.2", + "@babel/helpers": "^7.2.0", + "@babel/parser": "^7.2.2", + "@babel/template": "^7.2.2", + "@babel/traverse": "^7.2.2", + "@babel/types": "^7.2.2", "convert-source-map": "^1.1.0", - "debug": "^3.1.0", - "json5": "^0.5.0", + "debug": "^4.1.0", + "json5": "^2.1.0", "lodash": "^4.17.10", "resolve": "^1.3.2", "semver": "^5.4.1", "source-map": "^0.5.0" + }, + "dependencies": { + "@babel/generator": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.2.2.tgz", + "integrity": "sha512-I4o675J/iS8k+P38dvJ3IBGqObLXyQLTxtrR4u9cSUJOURvafeEWb/pFMOTwtNrmq73mJzyF6ueTbO1BtN0Zeg==", + "dev": true, + "requires": { + "@babel/types": "^7.2.2", + "jsesc": "^2.5.1", + "lodash": "^4.17.10", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" + } + }, + "@babel/parser": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.2.2.tgz", + "integrity": "sha512-UNTmQ5cSLDeBGBl+s7JeowkqIHgmFAGBnLDdIzFmUNSuS5JF0XBcN59jsh/vJO/YjfsBqMxhMjoFGmNExmf0FA==", + "dev": true + }, + "@babel/template": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.2.2.tgz", + "integrity": "sha512-zRL0IMM02AUDwghf5LMSSDEz7sBCO2YnNmpg3uWTZj/v1rcG2BmQUvaGU8GhU8BvfMh1k2KIAYZ7Ji9KXPUg7g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.2.2", + "@babel/types": "^7.2.2" + } + }, + "@babel/traverse": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.2.2.tgz", + "integrity": "sha512-E5Bn9FSwHpSkUhthw/XEuvFZxIgrqb9M8cX8j5EUQtrUG5DQUy6bFyl7G7iQ1D1Czudor+xkmp81JbLVVM0Sjg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/generator": "^7.2.2", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.0.0", + "@babel/parser": "^7.2.2", + "@babel/types": "^7.2.2", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.10" + } + }, + "@babel/types": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.2.2.tgz", + "integrity": "sha512-fKCuD6UFUMkR541eDWL+2ih/xFZBXPOg/7EQFeTluMDebfqR4jrpaCjLhkWlQS4hT6nRa2PMEgXKbRB5/H2fpg==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.10", + "to-fast-properties": "^2.0.0" + } + }, + "debug": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz", + "integrity": "sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "json5": { + "version": "2.1.0", + "resolved": "http://registry.npmjs.org/json5/-/json5-2.1.0.tgz", + "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } } }, "@babel/generator": { @@ -254,14 +344,78 @@ } }, "@babel/helpers": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.1.2.tgz", - "integrity": "sha512-Myc3pUE8eswD73aWcartxB16K6CGmHDv9KxOmD2CeOs/FaEAQodr3VYGmlvOmog60vNQ2w8QbatuahepZwrHiA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.2.0.tgz", + "integrity": "sha512-Fr07N+ea0dMcMN8nFpuK6dUIT7/ivt9yKQdEEnjVS83tG2pHwPi03gYmk/tyuwONnZ+sY+GFFPlWGgCtW1hF9A==", "dev": true, "requires": { "@babel/template": "^7.1.2", - "@babel/traverse": "^7.1.0", - "@babel/types": "^7.1.2" + "@babel/traverse": "^7.1.5", + "@babel/types": "^7.2.0" + }, + "dependencies": { + "@babel/generator": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.2.2.tgz", + "integrity": "sha512-I4o675J/iS8k+P38dvJ3IBGqObLXyQLTxtrR4u9cSUJOURvafeEWb/pFMOTwtNrmq73mJzyF6ueTbO1BtN0Zeg==", + "dev": true, + "requires": { + "@babel/types": "^7.2.2", + "jsesc": "^2.5.1", + "lodash": "^4.17.10", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" + } + }, + "@babel/parser": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.2.2.tgz", + "integrity": "sha512-UNTmQ5cSLDeBGBl+s7JeowkqIHgmFAGBnLDdIzFmUNSuS5JF0XBcN59jsh/vJO/YjfsBqMxhMjoFGmNExmf0FA==", + "dev": true + }, + "@babel/traverse": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.2.2.tgz", + "integrity": "sha512-E5Bn9FSwHpSkUhthw/XEuvFZxIgrqb9M8cX8j5EUQtrUG5DQUy6bFyl7G7iQ1D1Czudor+xkmp81JbLVVM0Sjg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/generator": "^7.2.2", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.0.0", + "@babel/parser": "^7.2.2", + "@babel/types": "^7.2.2", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.10" + } + }, + "@babel/types": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.2.2.tgz", + "integrity": "sha512-fKCuD6UFUMkR541eDWL+2ih/xFZBXPOg/7EQFeTluMDebfqR4jrpaCjLhkWlQS4hT6nRa2PMEgXKbRB5/H2fpg==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.10", + "to-fast-properties": "^2.0.0" + } + }, + "debug": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz", + "integrity": "sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } } }, "@babel/highlight": { @@ -2643,6 +2797,12 @@ } } }, + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "dev": true + }, "clone-regexp": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-1.0.1.tgz", @@ -2987,9 +3147,9 @@ } }, "css-loader": { - "version": "2.0.0", - "resolved": "http://registry.npmjs.org/css-loader/-/css-loader-2.0.0.tgz", - "integrity": "sha512-3Fq8HJYs7ruBiDpJA/w2ZROtivA769ePuH3/vgPdOB+FQiotErJ7VJYRZq86SPRVFaccn1wEktUnaaUyf+Uslw==", + "version": "2.0.1", + "resolved": "http://registry.npmjs.org/css-loader/-/css-loader-2.0.1.tgz", + "integrity": "sha512-XIVwoIOzSFRVsafOKa060GJ/A70c0IP/C1oVPHEX4eHIFF39z0Jl7j8Kua1SUTiqWDupUnbY3/yQx9r7EUB35w==", "dev": true, "requires": { "icss-utils": "^4.0.0", @@ -3005,9 +3165,9 @@ }, "dependencies": { "ajv": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.1.tgz", - "integrity": "sha512-ZoJjft5B+EJBjUyu9C9Hc0OZyPZSSlOF+plzouTrg6UlA8f+e/n8NIgBFG/9tppJtpPWfthHakK7juJdNDODww==", + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.2.tgz", + "integrity": "sha512-FBHEW6Jf5TB9MGBgUUA9XHkTbjXYfAUjY43ACMfmdMRHniyoMHjHjzD50OK8LGDWQwp4rWEsIq5kEqq7rvIM1g==", "dev": true, "requires": { "fast-deep-equal": "^2.0.1", @@ -3035,9 +3195,9 @@ "dev": true }, "postcss": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.6.tgz", - "integrity": "sha512-Nq/rNjnHFcKgCDDZYO0lNsl6YWe6U7tTy+ESN+PnLxebL8uBtYX59HZqvrj7YLK5UCyll2hqDsJOo3ndzEW8Ug==", + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.7.tgz", + "integrity": "sha512-HThWSJEPkupqew2fnuQMEI2YcTj/8gMV3n80cMdJsKxfIh5tHf7nM5JigNX6LxVMqo6zkgQNAI88hyFvBk41Pg==", "dev": true, "requires": { "chalk": "^2.4.1", @@ -4294,19 +4454,19 @@ } }, "file-loader": { - "version": "1.1.11", - "resolved": "http://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", - "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==", + "version": "2.0.0", + "resolved": "http://registry.npmjs.org/file-loader/-/file-loader-2.0.0.tgz", + "integrity": "sha512-YCsBfd1ZGCyonOKLxPiKPdu+8ld9HAaMEvJewzz+b2eTF7uL5Zm/HdBF6FjCrpCMRq25Mi0U1gl4pwn2TlH7hQ==", "dev": true, "requires": { "loader-utils": "^1.0.2", - "schema-utils": "^0.4.5" + "schema-utils": "^1.0.0" }, "dependencies": { "ajv": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.4.tgz", - "integrity": "sha512-4Wyjt8+t6YszqaXnLDfMmG/8AlO5Zbcsy3ATHncCzjW/NoPzAId8AK6749Ybjmdt+kUY1gP60fCu46oDxPv/mg==", + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.1.tgz", + "integrity": "sha512-ZoJjft5B+EJBjUyu9C9Hc0OZyPZSSlOF+plzouTrg6UlA8f+e/n8NIgBFG/9tppJtpPWfthHakK7juJdNDODww==", "dev": true, "requires": { "fast-deep-equal": "^2.0.1", @@ -4334,12 +4494,13 @@ "dev": true }, "schema-utils": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", - "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", "dev": true, "requires": { "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", "ajv-keywords": "^3.1.0" } } @@ -5453,9 +5614,9 @@ }, "dependencies": { "postcss": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.6.tgz", - "integrity": "sha512-Nq/rNjnHFcKgCDDZYO0lNsl6YWe6U7tTy+ESN+PnLxebL8uBtYX59HZqvrj7YLK5UCyll2hqDsJOo3ndzEW8Ug==", + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.7.tgz", + "integrity": "sha512-HThWSJEPkupqew2fnuQMEI2YcTj/8gMV3n80cMdJsKxfIh5tHf7nM5JigNX6LxVMqo6zkgQNAI88hyFvBk41Pg==", "dev": true, "requires": { "chalk": "^2.4.1", @@ -7388,14 +7549,6 @@ "requires": { "clone": "2.x", "lodash": "4.x" - }, - "dependencies": { - "clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", - "dev": true - } } }, "node-int64": { @@ -8058,9 +8211,9 @@ }, "dependencies": { "postcss": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.6.tgz", - "integrity": "sha512-Nq/rNjnHFcKgCDDZYO0lNsl6YWe6U7tTy+ESN+PnLxebL8uBtYX59HZqvrj7YLK5UCyll2hqDsJOo3ndzEW8Ug==", + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.7.tgz", + "integrity": "sha512-HThWSJEPkupqew2fnuQMEI2YcTj/8gMV3n80cMdJsKxfIh5tHf7nM5JigNX6LxVMqo6zkgQNAI88hyFvBk41Pg==", "dev": true, "requires": { "chalk": "^2.4.1", @@ -8088,9 +8241,9 @@ }, "dependencies": { "postcss": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.6.tgz", - "integrity": "sha512-Nq/rNjnHFcKgCDDZYO0lNsl6YWe6U7tTy+ESN+PnLxebL8uBtYX59HZqvrj7YLK5UCyll2hqDsJOo3ndzEW8Ug==", + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.7.tgz", + "integrity": "sha512-HThWSJEPkupqew2fnuQMEI2YcTj/8gMV3n80cMdJsKxfIh5tHf7nM5JigNX6LxVMqo6zkgQNAI88hyFvBk41Pg==", "dev": true, "requires": { "chalk": "^2.4.1", @@ -8117,9 +8270,9 @@ }, "dependencies": { "postcss": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.6.tgz", - "integrity": "sha512-Nq/rNjnHFcKgCDDZYO0lNsl6YWe6U7tTy+ESN+PnLxebL8uBtYX59HZqvrj7YLK5UCyll2hqDsJOo3ndzEW8Ug==", + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.7.tgz", + "integrity": "sha512-HThWSJEPkupqew2fnuQMEI2YcTj/8gMV3n80cMdJsKxfIh5tHf7nM5JigNX6LxVMqo6zkgQNAI88hyFvBk41Pg==", "dev": true, "requires": { "chalk": "^2.4.1", @@ -8146,9 +8299,9 @@ }, "dependencies": { "postcss": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.6.tgz", - "integrity": "sha512-Nq/rNjnHFcKgCDDZYO0lNsl6YWe6U7tTy+ESN+PnLxebL8uBtYX59HZqvrj7YLK5UCyll2hqDsJOo3ndzEW8Ug==", + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.7.tgz", + "integrity": "sha512-HThWSJEPkupqew2fnuQMEI2YcTj/8gMV3n80cMdJsKxfIh5tHf7nM5JigNX6LxVMqo6zkgQNAI88hyFvBk41Pg==", "dev": true, "requires": { "chalk": "^2.4.1", @@ -11771,9 +11924,9 @@ "integrity": "sha512-CKITl7I1cb3X4zIHbVSyrupPTs9XxZGVV/N+P5lSxSrGW+D92gq6zuTy/XnvJOwMRkjJuiotJAQrgv+gOwSx3g==" }, "vue-jest": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/vue-jest/-/vue-jest-3.0.1.tgz", - "integrity": "sha512-otS+n341cTsp0pF7tuTu2x43b23x/+K0LZdAXV+ewKYIMZRqhuQaJTECWEt/cN/YZw2JC6hUM6xybdnOB4ZQ+g==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/vue-jest/-/vue-jest-3.0.2.tgz", + "integrity": "sha512-5XIQ1xQFW0ZnWxHWM7adVA2IqbDsdw1vhgZfGFX4oWd75J38KIS3YT41PtiE7lpMLmNM6+VJ0uprT2mhHjUgkA==", "dev": true, "requires": { "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0", @@ -12476,9 +12629,9 @@ } }, "webpack-merge": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.1.4.tgz", - "integrity": "sha512-TmSe1HZKeOPey3oy1Ov2iS3guIZjWvMT2BBJDzzT5jScHTjVC3mpjJofgueEzaEd6ibhxRDD6MIblDr8tzh8iQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.1.5.tgz", + "integrity": "sha512-sVcM+MMJv6DO0C0GLLltx8mUlGMKXE0zBsuMqZ9jz2X9gsekALw6Rs0cAfTWc97VuWS6NpVUa78959zANnMMLQ==", "dev": true, "requires": { "lodash": "^4.17.5" diff --git a/package.json b/package.json index 593d0acc..47bb68d4 100644 --- a/package.json +++ b/package.json @@ -54,14 +54,14 @@ "node": ">=10.0.0" }, "devDependencies": { - "@babel/core": "^7.1.2", + "@babel/core": "^7.2.2", "@babel/plugin-syntax-dynamic-import": "^7.2.0", "@babel/preset-env": "^7.2.0", "@vue/test-utils": "^1.0.0-beta.27", "babel-eslint": "^10.0.1", "babel-jest": "^23.6.0", "babel-loader": "^8.0.4", - "css-loader": "^2.0.0", + "css-loader": "^2.0.1", "eslint": "^4.19.1", "eslint-config-standard": "^11.0.0", "eslint-friendly-formatter": "^4.0.1", @@ -72,7 +72,7 @@ "eslint-plugin-standard": "^3.1.0", "eslint-plugin-vue": "^4.5.0", "extract-text-webpack-plugin": "^3.0.2", - "file-loader": "^1.1.11", + "file-loader": "^2.0.0", "jest": "^23.6.0", "jest-serializer-vue": "^2.0.2", "mini-css-extract-plugin": "^0.5.0", @@ -81,13 +81,13 @@ "stylelint": "^8.4.0", "stylelint-config-recommended-scss": "^3.2.0", "stylelint-webpack-plugin": "^0.10.5", - "vue-jest": "^3.0.1", + "vue-jest": "^3.0.2", "vue-loader": "^15.4.2", "vue-style-loader": "^4.1.1", "vue-template-compiler": "^2.5.21", "webpack": "^4.27.1", "webpack-cli": "^3.1.2", - "webpack-merge": "^4.1.2" + "webpack-merge": "^4.1.5" }, "jest": { "moduleFileExtensions": [