Jelajahi Sumber

Merge pull request #2159 from pixelfed/staging

AP bugfixes
daniel 5 tahun lalu
induk
melakukan
75cb3c8dc9

+ 5 - 1
CHANGELOG.md

@@ -28,7 +28,11 @@
 - Updated NotificationCard.vue component, add follow requests at top of card, remove card-header ([5e48ffca](https://github.com/pixelfed/pixelfed/commit/5e48ffca))
 - Updated RemoteProfile.vue component, add warning for empty profiles and last_fetched_at ([66f44a9d](https://github.com/pixelfed/pixelfed/commit/66f44a9d))
 - Updated ApiV1Controller, enforce public timeline setting ([285bd485](https://github.com/pixelfed/pixelfed/commit/285bd485))
-- Update SearchController, fix self search bug and rank local matches higher ([f67fada2](https://github.com/pixelfed/pixelfed/commit/f67fada2))
+- Updated SearchController, fix self search bug and rank local matches higher ([f67fada2](https://github.com/pixelfed/pixelfed/commit/f67fada2))
+- Updated FederationController, improve webfinger logic, fixes ([#2180](https://github.com/pixelfed/pixelfed/issues/2180)) ([302ff874](https://github.com/pixelfed/pixelfed/commit/302ff874))
+- Updated ApiV1Controller, fix broken auth check on public timelines. Fixes ([#2168](https://github.com/pixelfed/pixelfed/issues/2168)) ([aa49afc7](https://github.com/pixelfed/pixelfed/commit/aa49afc7))
+- Updated SearchApiV2Service, fix offset bug ([#2116](https://github.com/pixelfed/pixelfed/issues/2116)) ([a0c0c84d](https://github.com/pixelfed/pixelfed/commit/a0c0c84d))
+- Updated api routes, fixes ([#2114](https://github.com/pixelfed/pixelfed/issues/2114)) ([50bbeddd](https://github.com/pixelfed/pixelfed/commit/50bbeddd))
 
 
 ## [v0.10.9 (2020-04-17)](https://github.com/pixelfed/pixelfed/compare/v0.10.8...v0.10.9)

+ 0 - 2
app/Http/Controllers/Api/ApiV1Controller.php

@@ -1397,8 +1397,6 @@ class ApiV1Controller extends Controller
      */
     public function timelinePublic(Request $request)
     {
-        abort_if(!config('instance.timeline.local.is_public') && !$request->user(), 403);
-        
         $this->validate($request,[
           'page'        => 'nullable|integer|max:40',
           'min_id'      => 'nullable|integer|min:0|max:' . PHP_INT_MAX,

+ 3 - 0
app/Http/Controllers/DiscoverController.php

@@ -135,6 +135,7 @@ class DiscoverController extends Controller
 
     public function profilesDirectory(Request $request)
     {
+      return redirect('/')->with('statusRedirect', 'The Profile Directory is unavailable at this time.');
       return view('discover.profiles.home');
     }
 
@@ -144,6 +145,8 @@ class DiscoverController extends Controller
         'page' => 'integer|max:10'
       ]);
 
+      return ['error' => 'Temporarily unavailable.'];
+
       $page = $request->input('page') ?? 1;
       $key = 'discover:profiles:page:' . $page;
       $ttl = now()->addHours(12);

+ 4 - 4
app/Http/Controllers/FederationController.php

@@ -46,14 +46,14 @@ class FederationController extends Controller
 
     public function webfinger(Request $request)
     {
-        abort_if(!config('federation.webfinger.enabled'), 404);
+        abort_if(!config('federation.webfinger.enabled'), 400);
 
-        $this->validate($request, ['resource'=>'required|string|min:3|max:255']);
+        abort_if(!$request->filled('resource'), 400);
 
         $resource = $request->input('resource');
         $parsed = Nickname::normalizeProfileUrl($resource);
         if($parsed['domain'] !== config('pixelfed.domain.app')) {
-            abort(404);
+            abort(400);
         }
         $username = $parsed['username'];
         $profile = Profile::whereNull('domain')->whereUsername($username)->firstOrFail();
@@ -108,7 +108,7 @@ class FederationController extends Controller
             return ProfileController::accountCheck($profile);
         }
         $body = $request->getContent();
-        $bodyDecoded = json_decode($body, true, 8);
+        $bodyDecoded = json_decode($body, true, 12);
         if($this->verifySignature($request, $profile) == true) {
             InboxWorker::dispatch($request->headers->all(), $profile, $bodyDecoded);
         } else if($this->blindKeyRotation($request, $profile) == true) {

+ 8 - 26
app/Services/SearchApiV2Service.php

@@ -86,13 +86,12 @@ class SearchApiV2Service
 
 	protected function accounts()
 	{
-		$limit = $this->query->input('limit', 20);
+		$limit = $this->query->input('limit') ?? 20;
+		$offset = $this->query->input('offset') ?? 0;
 		$query = '%' . $this->query->input('q') . '%';
 		$results = Profile::whereNull('status')
 			->where('username', 'like', $query)
-			->when($this->query->input('offset') != null, function($q, $offset) {
-				return $q->offset($offset);
-			})
+			->offset($offset)
 			->limit($limit)
 			->get();
 
@@ -104,13 +103,12 @@ class SearchApiV2Service
 
 	protected function hashtags()
 	{
-		$limit = $this->query->input('limit', 20);
+		$limit = $this->query->input('limit') ?? 20;
+		$offset = $this->query->input('offset') ?? 0;
 		$query = '%' . $this->query->input('q') . '%';
 		return Hashtag::whereIsBanned(false)
 			->where('name', 'like', $query)
-			->when($this->query->input('offset') != null, function($q, $offset) {
-				return $q->offset($offset);
-			})
+			->offset($offset)
 			->limit($limit)
 			->get()
 			->map(function($tag) {
@@ -124,21 +122,8 @@ class SearchApiV2Service
 
 	protected function statuses()
 	{
-		$limit = $this->query->input('limit', 20);
-		$query = '%' . $this->query->input('q') . '%';
-		$results = Status::where('caption', 'like', $query)
-			->whereScope('public')
-			->when($this->query->input('offset') != null, function($q, $offset) {
-				return $q->offset($offset);
-			})
-			->limit($limit)
-			->orderByDesc('created_at')
-			->get();
-
-		$fractal = new Fractal\Manager();
-		$fractal->setSerializer(new ArraySerializer());
-		$resource = new Fractal\Resource\Collection($results, new StatusTransformer());
-		return $fractal->createData($resource)->toArray();
+		// Removed until we provide more relevent sorting/results
+		return [];
 	}
 
 	protected function statusesById()
@@ -148,9 +133,6 @@ class SearchApiV2Service
 		$query = '%' . $this->query->input('q') . '%';
 		$results = Status::where('caption', 'like', $query)
 			->whereProfileId($accountId)
-			->when($this->query->input('offset') != null, function($q, $offset) {
-				return $q->offset($offset);
-			})
 			->limit($limit)
 			->get();
 

+ 16 - 8
app/Util/ActivityPub/Inbox.php

@@ -18,10 +18,11 @@ use App\Util\ActivityPub\Helpers;
 use App\Jobs\LikePipeline\LikePipeline;
 use App\Jobs\FollowPipeline\FollowPipeline;
 
-use App\Util\ActivityPub\Validator\{
-    Accept,
-    Follow
-};
+use App\Util\ActivityPub\Validator\Accept as AcceptValidator;
+use App\Util\ActivityPub\Validator\Announce as AnnounceValidator;
+use App\Util\ActivityPub\Validator\Follow as FollowValidator;
+use App\Util\ActivityPub\Validator\Like as LikeValidator;
+use App\Util\ActivityPub\Validator\UndoFollow as UndoFollowValidator;
 
 class Inbox
 {
@@ -41,9 +42,15 @@ class Inbox
     {
         $this->handleVerb();
 
-        (new Activity())->create([
-            'data' => json_encode($this->payload)
-        ]);
+        if(!Activity::where('data->id', $this->payload['id'])->exists()){
+            (new Activity())->create([
+                'to_id' => $this->profile->id,
+                'data' => json_encode($this->payload)
+            ]);
+        }
+
+        return;
+
     }
 
     public function handleVerb()
@@ -59,11 +66,12 @@ class Inbox
                 break;
 
             case 'Announce':
+                if(AnnounceValidator::validate($this->payload) == false) { return; }
                 $this->handleAnnounceActivity();
                 break;
 
             case 'Accept':
-                if(Accept::validate($this->payload) == false) { return; }
+                if(AcceptValidator::validate($this->payload) == false) { return; }
                 $this->handleAcceptActivity();
                 break;
 

+ 2 - 2
app/Util/ActivityPub/Validator/Announce.php

@@ -16,11 +16,11 @@ class Announce {
 				'required',
 				Rule::in(['Announce'])
 			],
-			'actor' => 'required|url|active_url',
+			'actor' => 'required|url',
 			'published' => 'required|date',
 			'to'	=> 'required',
 			'cc'	=> 'required',
-			'object' => 'required|url|active_url'
+			'object' => 'required|url'
 		])->passes();
 
 		return $valid;

+ 2 - 2
app/Util/ActivityPub/Validator/Follow.php

@@ -16,8 +16,8 @@ class Follow {
 				'required',
 				Rule::in(['Follow'])
 			],
-			'actor' => 'required|url|active_url',
-			'object' => 'required|url|active_url'
+			'actor' => 'required|url',
+			'object' => 'required|url'
 		])->passes();
 
 		return $valid;

+ 2 - 2
app/Util/ActivityPub/Validator/Like.php

@@ -16,8 +16,8 @@ class Like {
 				'required',
 				Rule::in(['Like'])
 			],
-			'actor' => 'required|url|active_url',
-			'object' => 'required|url|active_url'
+			'actor' => 'required|url',
+			'object' => 'required|url'
 		])->passes();
 
 		return $valid;

+ 3 - 3
package-lock.json

@@ -5088,9 +5088,9 @@
             "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
         },
         "jquery": {
-            "version": "3.4.1",
-            "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.4.1.tgz",
-            "integrity": "sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw=="
+            "version": "3.5.0",
+            "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.0.tgz",
+            "integrity": "sha512-Xb7SVYMvygPxbFMpTFQiHh1J7HClEaThguL15N/Gg37Lri/qKyhRGZYzHRyLH8Stq3Aow0LsHO2O2ci86fCrNQ=="
         },
         "js-base64": {
             "version": "2.5.2",

+ 1 - 1
package.json

@@ -14,7 +14,7 @@
         "axios": "^0.18.1",
         "bootstrap": "^4.4.1",
         "cross-env": "^5.2.1",
-        "jquery": "^3.4.1",
+        "jquery": "^3.5.0",
         "lodash": ">=4.17.13",
         "popper.js": "^1.16.1",
         "resolve-url-loader": "^2.3.2",

+ 10 - 0
resources/lang/fr/exception.php

@@ -0,0 +1,10 @@
+<?php
+
+return [
+	'compose' => [
+		'invalid' => [
+			'album' => 'Doit contenir une seule photo ou vidéo ou plusieurs photos.',
+		],
+	],
+
+];

+ 1 - 0
resources/lang/fr/navmenu.php

@@ -14,4 +14,5 @@ return [
     'admin'             =>    'Admin',
     'logout'            =>    'Se déconnecter',
 	'directMessages'    =>    'Messages Directs',
+    'composePost'       =>    'Composer une publication',
 ];

+ 29 - 3
resources/views/timeline/home.blade.php

@@ -1,15 +1,41 @@
 @extends('layouts.app')
+{{-- @extends('layouts.blank') --}}
 
 @section('content')
-
-<timeline scope="home" layout="feed"></timeline>
-
+@if(session('statusRedirect'))
+<div class="alert alert-warning border-bottom">
+	<div class="row">
+		<div class="col-2">
+			<p class="mb-0"></p>
+		</div>
+		<div class="col-8">
+			<p class="font-weight-bold text-center mb-0">
+				{{ session('statusRedirect') }}
+			</p>
+		</div>
+		<div class="col-2 cursor-pointer" onclick="this.parentNode.parentNode.style.display='none'">
+			<p class="mb-0">
+				<i class="fas fa-times"></i>
+			</p>
+		</div>
+	</div>
+</div>
+@endif
 <noscript>
 	<div class="container">
 		<p class="pt-5 text-center lead">Please enable javascript to view this content.</p>
 	</div>
 </noscript>
 
+<timeline scope="home" layout="feed"></timeline>
+<div class="modal pr-0" tabindex="-1" role="dialog" id="composeModal">
+	<div class="modal-dialog" role="document">
+		<div class="modal-content">
+			<compose-modal></compose-modal>
+		</div>
+	</div>
+</div>
+
 @endsection
 
 @push('scripts')

+ 17 - 15
routes/api.php

@@ -7,6 +7,7 @@ $middleware = ['auth:api','twofactor','validemail','localization'];
 Route::post('/users/{username}/inbox', 'FederationController@userInbox');
 
 Route::group(['prefix' => 'api'], function() use($middleware) {
+
 	Route::group(['prefix' => 'v1'], function() use($middleware) {
 		Route::post('apps', 'Api\ApiV1Controller@apps');
 		Route::get('instance', 'Api\ApiV1Controller@instance');
@@ -28,7 +29,7 @@ Route::group(['prefix' => 'api'], function() use($middleware) {
 		Route::post('accounts/{id}/unmute', 'Api\ApiV1Controller@accountUnmuteById')->middleware($middleware);
 		Route::get('accounts/{id}/lists', 'Api\ApiV1Controller@accountListsById')->middleware($middleware);
 		Route::get('lists/{id}/accounts', 'Api\ApiV1Controller@accountListsById')->middleware($middleware);
-		Route::get('accounts/{id}', 'Api\ApiV1Controller@accountById')->middleware($middleware);
+		Route::get('accounts/{id}', 'Api\ApiV1Controller@accountById');
 
 		Route::post('avatar/update', 'ApiController@avatarUpdate')->middleware($middleware);
 		Route::get('blocks', 'Api\ApiV1Controller@accountBlocks')->middleware($middleware);
@@ -67,18 +68,19 @@ Route::group(['prefix' => 'api'], function() use($middleware) {
 		Route::get('timelines/public', 'Api\ApiV1Controller@timelinePublic');
 		Route::get('timelines/tag/{hashtag}', 'Api\ApiV1Controller@timelineHashtag')->middleware($middleware);
 	});
-    Route::group(['prefix' => 'stories'], function () use($middleware) {
-    	Route::get('v1/me', 'StoryController@apiV1Me');
-        Route::get('v1/recent', 'StoryController@apiV1Recent');
-        Route::post('v1/add', 'StoryController@apiV1Add')->middleware(array_merge($middleware, ['throttle:maxStoriesPerDay,1440']));
-        Route::get('v1/item/{id}', 'StoryController@apiV1Item');
-        Route::get('v1/fetch/{id}', 'StoryController@apiV1Fetch');
-        Route::get('v1/profile/{id}', 'StoryController@apiV1Profile');
-        Route::get('v1/exists/{id}', 'StoryController@apiV1Exists');
-        Route::delete('v1/delete/{id}', 'StoryController@apiV1Delete')->middleware(array_merge($middleware, ['throttle:maxStoryDeletePerDay,1440']));
-        Route::post('v1/viewed', 'StoryController@apiV1Viewed');
-    });
-    Route::group(['prefix' => 'v2'], function() use($middleware) {
-    	Route::get('search', 'Api\ApiV1Controller@searchV2')->middleware($middleware);
-    });
+	Route::group(['prefix' => 'stories'], function () use($middleware) {
+		Route::get('v1/me', 'StoryController@apiV1Me');
+		Route::get('v1/recent', 'StoryController@apiV1Recent');
+		Route::post('v1/add', 'StoryController@apiV1Add')->middleware(array_merge($middleware, ['throttle:maxStoriesPerDay,1440']));
+		Route::get('v1/item/{id}', 'StoryController@apiV1Item');
+		Route::get('v1/fetch/{id}', 'StoryController@apiV1Fetch');
+		Route::get('v1/profile/{id}', 'StoryController@apiV1Profile');
+		Route::get('v1/exists/{id}', 'StoryController@apiV1Exists');
+		Route::delete('v1/delete/{id}', 'StoryController@apiV1Delete')->middleware(array_merge($middleware, ['throttle:maxStoryDeletePerDay,1440']));
+		Route::post('v1/viewed', 'StoryController@apiV1Viewed');
+	});
+	Route::group(['prefix' => 'v2'], function() use($middleware) {
+		Route::get('search', 'Api\ApiV1Controller@searchV2')->middleware($middleware);
+	});
+	
 });

+ 0 - 27
tests/Unit/ActivityPub/FollowTest.php

@@ -1,27 +0,0 @@
-<?php
-
-namespace Tests\Unit;
-
-use App\Util\ActivityPub\Helpers;
-use Tests\TestCase;
-use Illuminate\Foundation\Testing\WithFaker;
-use Illuminate\Foundation\Testing\RefreshDatabase;
-
-class FollowTest extends TestCase
-{
-	public function setUp(): void
-	{
-		parent::setUp();
-
-		$this->mastodon = '{"type":"Follow","signature":{"type":"RsaSignature2017","signatureValue":"Kn1/UkAQGJVaXBfWLAHcnwHg8YMAUqlEaBuYLazAG+pz5hqivsyrBmPV186Xzr+B4ZLExA9+SnOoNx/GOz4hBm0kAmukNSILAsUd84tcJ2yT9zc1RKtembK4WiwOw7li0+maeDN0HaB6t+6eTqsCWmtiZpprhXD8V1GGT8yG7X24fQ9oFGn+ng7lasbcCC0988Y1eGqNe7KryxcPuQz57YkDapvtONzk8gyLTkZMV4De93MyRHq6GVjQVIgtiYabQAxrX6Q8C+4P/jQoqdWJHEe+MY5JKyNaT/hMPt2Md1ok9fZQBGHlErk22/zy8bSN19GdG09HmIysBUHRYpBLig==","creator":"http://mastodon.example.org/users/admin#main-key","created":"2018-02-17T13:29:31Z"},"object":"http://localtesting.pleroma.lol/users/lain","nickname":"lain","id":"http://mastodon.example.org/users/admin#follows/2","actor":"http://mastodon.example.org/users/admin","@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1",{"toot":"http://joinmastodon.org/ns#","sensitive":"as:sensitive","ostatus":"http://ostatus.org#","movedTo":"as:movedTo","manuallyApprovesFollowers":"as:manuallyApprovesFollowers","inReplyToAtomUri":"ostatus:inReplyToAtomUri","conversation":"ostatus:conversation","atomUri":"ostatus:atomUri","Hashtag":"as:Hashtag","Emoji":"toot:Emoji"}]}';
-
-	}
-
-	/** @test */
-	public function validateMastodonFollowObject()
-	{
-		$mastodon = json_decode($this->mastodon, true);
-		$mastodon = Helpers::validateObject($mastodon);
-		$this->assertTrue($mastodon);
-	}
-}

+ 42 - 0
tests/Unit/ActivityPub/Verb/AcceptVerbTest.php

@@ -39,6 +39,42 @@ class AcceptVerbTest extends TestCase
 				'object' => 'https://example.org/u/alice'
 			]
 		];
+		$this->mastodonAccept = [
+			"@context" => [
+				"https://www.w3.org/ns/activitystreams",
+				"https://w3id.org/security/v1",
+				[
+					"toot" => "https://joinmastodon.org/ns#",
+					"sensitive" => "as:sensitive",
+					"ostatus" => "https://ostatus.org#",
+					"movedTo" => "as:movedTo",
+					"manuallyApprovesFollowers" => "as:manuallyApprovesFollowers",
+					"inReplyToAtomUri" => "ostatus:inReplyToAtomUri",
+					"conversation" => "ostatus:conversation",
+					"atomUri" => "ostatus:atomUri",
+					"Hashtag" => "as:Hashtag",
+					"Emoji" => "toot:Emoji",
+				],
+			],
+
+			"type" => "Accept",
+
+			"object" => [
+				"type" => "Follow",
+				"object" => "https://mastodon.example.org/users/admin",
+				"id" => "https://pixelfed.dev/users/dsup#follows/4",
+				"actor" => "https://pixelfed.dev/users/dsup",
+			],
+			"nickname" => "dsup",
+			"id" => "https://mastodon.example.org/users/admin#accepts/follows/4",
+			"actor" => "https://mastodon.example.org/users/admin",
+			"signature" => [
+				"type" => "RsaSignature2017",
+				"signatureValue" => "rBzK4Kqhd4g7HDS8WE5oRbWQb2R+HF/6awbUuMWhgru/xCODT0SJWSri0qWqEO4fPcpoUyz2d25cw6o+iy9wiozQb3hQNnu69AR+H5Mytc06+g10KCHexbGhbAEAw/7IzmeXELHUbaqeduaDIbdt1zw4RkwLXdqgQcGXTJ6ND1wM3WMHXQCK1m0flasIXFoBxpliPAGiElV8s0+Ltuh562GvflG3kB3WO+j+NaR0ZfG5G9N88xMj9UQlCKit5gpAE5p6syUsCU2WGBHywTumv73i3OVTIFfq+P9AdMsRuzw1r7zoKEsthW4aOzLQDi01ZjvdBz8zH6JnjDU7SMN/Ig==",
+				"creator" => "https://mastodon.example.org/users/admin#main-key",
+				"created" => "2018-02-17T14:36:41Z",
+			],
+		];
 	}
 
 	/** @test */
@@ -52,4 +88,10 @@ class AcceptVerbTest extends TestCase
 	{
 		$this->assertFalse(Accept::validate($this->invalidAccept));
 	}
+
+	/** @test */
+	public function mastodon_accept()
+	{
+		$this->assertTrue(Accept::validate($this->mastodonAccept));
+	}
 }

+ 44 - 0
tests/Unit/ActivityPub/Verb/AnnounceTest.php

@@ -126,6 +126,44 @@ class AnnounceTest extends TestCase
 			],
 			"object" => "https://example.org/p/bob/100000000000000",
 		];
+
+		$this->mastodonAnnounce = [
+			"type" => "Announce",
+			"to" => [
+				"https://www.w3.org/ns/activitystreams#Public",
+			],
+			"signature" => [
+				"type" => "RsaSignature2017",
+				"signatureValue" => "T95DRE0eAligvMuRMkQA01lsoz2PKi4XXF+cyZ0BqbrO12p751TEWTyyRn5a+HH0e4kc77EUhQVXwMq80WAYDzHKVUTf2XBJPBa68vl0j6RXw3+HK4ef5hR4KWFNBU34yePS7S1fEmc1mTG4Yx926wtmZwDpEMTp1CXOeVEjCYzmdyHpepPPH2ZZettiacmPRSqBLPGWZoot7kH/SioIdnrMGY0I7b+rqkIdnnEcdhu9N1BKPEO9Sr+KmxgAUiidmNZlbBXX6gCxp8BiIdH4ABsIcwoDcGNkM5EmWunGW31LVjsEQXhH5c1Wly0ugYYPCg/0eHLNBOhKkY/teSM8Lg==",
+				"creator" => "https://mastodon.example.org/users/admin#main-key",
+				"created" => "2018-02-17T19:39:15Z",
+			],
+			"published" => "2018-02-17T19:39:15Z",
+			"object" => "https://mastodon.example.org/@admin/99541947525187367",
+			"id" => "https://mastodon.example.org/users/admin/statuses/99542391527669785/activity",
+			"cc" => [
+				"https://mastodon.example.org/users/admin",
+				"https://mastodon.example.org/users/admin/followers",
+			],
+			"atomUri" => "https://mastodon.example.org/users/admin/statuses/99542391527669785/activity",
+			"actor" => "https://mastodon.example.org/users/admin",
+			"@context" => [
+				"https://www.w3.org/ns/activitystreams",
+				"https://w3id.org/security/v1",
+				[
+					"toot" => "https://joinmastodon.org/ns#",
+					"sensitive" => "as:sensitive",
+					"ostatus" => "https://ostatus.org#",
+					"movedTo" => "as:movedTo",
+					"manuallyApprovesFollowers" => "as:manuallyApprovesFollowers",
+					"inReplyToAtomUri" => "ostatus:inReplyToAtomUri",
+					"conversation" => "ostatus:conversation",
+					"atomUri" => "ostatus:atomUri",
+					"Hashtag" => "as:Hashtag",
+					"Emoji" => "toot:Emoji",
+				],
+			],
+		];
 	}
 
 	/** @test */
@@ -165,4 +203,10 @@ class AnnounceTest extends TestCase
 		$this->assertFalse(Announce::validate($this->invalidActor));
 		$this->assertFalse(Announce::validate($this->invalidActor2));
 	}
+
+	/** @test */
+	public function mastodon_announce()
+	{
+		$this->assertTrue(Announce::validate($this->mastodonAnnounce));
+	}
 }

+ 53 - 0
tests/Unit/ActivityPub/Verb/FollowTest.php

@@ -0,0 +1,53 @@
+<?php
+
+namespace Tests\Unit\ActivityPub\Verb;
+
+use Tests\TestCase;
+use Illuminate\Foundation\Testing\WithFaker;
+use Illuminate\Foundation\Testing\RefreshDatabase;
+use App\Util\ActivityPub\Validator\Follow;
+
+class FollowTest extends TestCase
+{
+
+	public function setUp(): void
+	{
+		parent::setUp();
+
+		$this->basicFollow = [
+			"type" => "Follow",
+			"signature" => [
+				"type" => "RsaSignature2017",
+				"signatureValue" => "Kn1/UkAQGJVaXBfWLAHcnwHg8YMAUqlEaBuYLazAG+pz5hqivsyrBmPV186Xzr+B4ZLExA9+SnOoNx/GOz4hBm0kAmukNSILAsUd84tcJ2yT9zc1RKtembK4WiwOw7li0+maeDN0HaB6t+6eTqsCWmtiZpprhXD8V1GGT8yG7X24fQ9oFGn+ng7lasbcCC0988Y1eGqNe7KryxcPuQz57YkDapvtONzk8gyLTkZMV4De93MyRHq6GVjQVIgtiYabQAxrX6Q8C+4P/jQoqdWJHEe+MY5JKyNaT/hMPt2Md1ok9fZQBGHlErk22/zy8bSN19GdG09HmIysBUHRYpBLig==",
+				"creator" => "http://mastodon.example.org/users/admin#main-key",
+				"created" => "2018-02-17T13:29:31Z",
+			],
+			"object" => "http://pixelfed.dev/users/dsup",
+			"nickname" => "dsup",
+			"id" => "http://mastodon.example.org/users/admin#follows/2",
+			"actor" => "http://mastodon.example.org/users/admin",
+			"@context" => [
+				"https://www.w3.org/ns/activitystreams",
+				"https://w3id.org/security/v1",
+				[
+					"toot" => "http://joinmastodon.org/ns#",
+					"sensitive" => "as:sensitive",
+					"ostatus" => "http://ostatus.org#",
+					"movedTo" => "as:movedTo",
+					"manuallyApprovesFollowers" => "as:manuallyApprovesFollowers",
+					"inReplyToAtomUri" => "ostatus:inReplyToAtomUri",
+					"conversation" => "ostatus:conversation",
+					"atomUri" => "ostatus:atomUri",
+					"Hashtag" => "as:Hashtag",
+					"Emoji" => "toot:Emoji",
+				],
+			],
+		];
+	}
+
+	/** @test */
+	public function basic_follow()
+	{
+		$this->assertTrue(Follow::validate($this->basicFollow));
+	}
+}

+ 53 - 0
tests/Unit/ActivityPub/Verb/LikeTest.php

@@ -0,0 +1,53 @@
+<?php
+
+namespace Tests\Unit\ActivityPub\Verb;
+
+use Tests\TestCase;
+use Illuminate\Foundation\Testing\WithFaker;
+use Illuminate\Foundation\Testing\RefreshDatabase;
+use App\Util\ActivityPub\Validator\Like;
+
+class LikeTest extends TestCase
+{
+
+	public function setUp(): void
+	{
+		parent::setUp();
+
+		$this->basicLike = [
+			"type" => "Like",
+			"signature" => [
+				"type" => "RsaSignature2017",
+				"signatureValue" => "fdxMfQSMwbC6wP6sh6neS/vM5879K67yQkHTbiT5Npr5wAac0y6+o3Ij+41tN3rL6wfuGTosSBTHOtta6R4GCOOhCaCSLMZKypnp1VltCzLDoyrZELnYQIC8gpUXVmIycZbREk22qWUe/w7DAFaKK4UscBlHDzeDVcA0K3Se5Sluqi9/Zh+ldAnEzj/rSEPDjrtvf5wGNf3fHxbKSRKFt90JvKK6hS+vxKUhlRFDf6/SMETw+EhwJSNW4d10yMUakqUWsFv4Acq5LW7l+HpYMvlYY1FZhNde1+uonnCyuQDyvzkff8zwtEJmAXC4RivO/VVLa17SmqheJZfI8oluVg==",
+				"creator" => "http://mastodon.example.org/users/admin#main-key",
+				"created" => "2018-02-17T18:57:49Z",
+			],
+			"object" => "http://pixelfed.dev/p/1",
+			"nickname" => "dsup",
+			"id" => "http://mastodon.example.org/users/admin#likes/2",
+			"actor" => "http://mastodon.example.org/users/admin",
+			"@context" => [
+				"https://www.w3.org/ns/activitystreams",
+				"https://w3id.org/security/v1",
+				[
+					"toot" => "http://joinmastodon.org/ns#",
+					"sensitive" => "as:sensitive",
+					"ostatus" => "http://ostatus.org#",
+					"movedTo" => "as:movedTo",
+					"manuallyApprovesFollowers" => "as:manuallyApprovesFollowers",
+					"inReplyToAtomUri" => "ostatus:inReplyToAtomUri",
+					"conversation" => "ostatus:conversation",
+					"atomUri" => "ostatus:atomUri",
+					"Hashtag" => "as:Hashtag",
+					"Emoji" => "toot:Emoji",
+				],
+			],
+		];
+	}
+
+	/** @test */
+	public function basic_like()
+	{
+		$this->assertTrue(Like::validate($this->basicLike));
+	}
+}