Browse Source

Update ap inbox

Daniel Supernault 2 years ago
parent
commit
29961c4a80

+ 88 - 0
app/Jobs/ProfilePipeline/HandleUpdateActivity.php

@@ -0,0 +1,88 @@
+<?php
+
+namespace App\Jobs\ProfilePipeline;
+
+use Illuminate\Bus\Queueable;
+use Illuminate\Contracts\Queue\ShouldBeUnique;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Foundation\Bus\Dispatchable;
+use Illuminate\Queue\InteractsWithQueue;
+use Illuminate\Queue\SerializesModels;
+use App\Avatar;
+use App\Profile;
+use App\Util\ActivityPub\Helpers;
+use Purify;
+use App\Jobs\AvatarPipeline\RemoteAvatarFetch;
+use App\Util\Lexer\Autolink;
+
+class HandleUpdateActivity implements ShouldQueue
+{
+    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
+
+    protected $payload;
+
+    /**
+     * Create a new job instance.
+     *
+     * @return void
+     */
+    public function __construct($payload)
+    {
+        $this->payload = $payload;
+    }
+
+    /**
+     * Execute the job.
+     *
+     * @return void
+     */
+    public function handle(): void
+    {
+        $payload = $this->payload;
+
+        if(empty($payload) || !isset($payload['actor'])) {
+            return;
+        }
+
+        $profile = Profile::whereRemoteUrl($payload['actor'])->first();
+
+        if(!$profile || $profile->domain === null || $profile->private_key) {
+            return;
+        }
+
+        if($profile->sharedInbox == null || $profile->sharedInbox != $payload['object']['endpoints']['sharedInbox']) {
+            $profile->sharedInbox = $payload['object']['endpoints']['sharedInbox'];
+        }
+
+        if($profile->public_key !== $payload['object']['publicKey']['publicKeyPem']) {
+            $profile->public_key = $payload['object']['publicKey']['publicKeyPem'];
+        }
+
+        if($profile->bio !== $payload['object']['summary']) {
+            $len = strlen(strip_tags($payload['object']['summary']));
+            if($len) {
+                if($len > 500) {
+                    $updated = strip_tags($payload['object']['summary']);
+                    $updated = substr($updated, 0, config('pixelfed.max_bio_length'));
+                    $profile->bio = Autolink::create()->autolink($updated);
+                } else {
+                    $profile->bio = Purify::clean($payload['object']['summary']);
+                }
+            } else {
+                $profile->bio = null;
+            }
+        }
+
+        if($profile->name !== $payload['object']['name']) {
+            $profile->name = Purify::clean(substr($payload['object']['name'], 0, config('pixelfed.max_name_length')));
+        }
+
+        if($profile->isDirty()) {
+            $profile->save();
+        }
+
+        RemoteAvatarFetch::dispatch($profile)->onQueue('low');
+
+        return;
+    }
+}

+ 10 - 0
app/Util/ActivityPub/Inbox.php

@@ -29,6 +29,7 @@ use App\Jobs\DeletePipeline\DeleteRemoteStatusPipeline;
 use App\Jobs\StoryPipeline\StoryExpire;
 use App\Jobs\StoryPipeline\StoryExpire;
 use App\Jobs\StoryPipeline\StoryFetch;
 use App\Jobs\StoryPipeline\StoryFetch;
 use App\Jobs\StatusPipeline\StatusRemoteUpdatePipeline;
 use App\Jobs\StatusPipeline\StatusRemoteUpdatePipeline;
+use App\Jobs\ProfilePipeline\HandleUpdateActivity;
 
 
 use App\Util\ActivityPub\Validator\Accept as AcceptValidator;
 use App\Util\ActivityPub\Validator\Accept as AcceptValidator;
 use App\Util\ActivityPub\Validator\Add as AddValidator;
 use App\Util\ActivityPub\Validator\Add as AddValidator;
@@ -36,6 +37,7 @@ use App\Util\ActivityPub\Validator\Announce as AnnounceValidator;
 use App\Util\ActivityPub\Validator\Follow as FollowValidator;
 use App\Util\ActivityPub\Validator\Follow as FollowValidator;
 use App\Util\ActivityPub\Validator\Like as LikeValidator;
 use App\Util\ActivityPub\Validator\Like as LikeValidator;
 use App\Util\ActivityPub\Validator\UndoFollow as UndoFollowValidator;
 use App\Util\ActivityPub\Validator\UndoFollow as UndoFollowValidator;
+use App\Util\ActivityPub\Validator\UpdatePersonValidator;
 
 
 use App\Services\PollService;
 use App\Services\PollService;
 use App\Services\FollowerService;
 use App\Services\FollowerService;
@@ -1217,10 +1219,18 @@ class Inbox
 			return;
 			return;
 		}
 		}
 
 
+		if(!Helpers::validateUrl($activity['id'])) {
+			return;
+		}
+
 		if($activity['type'] === 'Note') {
 		if($activity['type'] === 'Note') {
 			if(Status::whereObjectUrl($activity['id'])->exists()) {
 			if(Status::whereObjectUrl($activity['id'])->exists()) {
 				StatusRemoteUpdatePipeline::dispatch($activity);
 				StatusRemoteUpdatePipeline::dispatch($activity);
 			}
 			}
+		} else if ($activity['type'] === 'Person') {
+			if(UpdatePersonValidator::validate($this->payload)) {
+				HandleUpdateActivity::dispatch($this->payload)->onQueue('low');
+			}
 		}
 		}
 	}
 	}
 }
 }

+ 119 - 0
app/Util/ActivityPub/Validator/UpdatePersonValidator.php

@@ -0,0 +1,119 @@
+<?php
+
+namespace App\Util\ActivityPub\Validator;
+
+use Validator;
+use Closure;
+use Illuminate\Validation\Rule;
+use \App\Rules\SameHostDomain;
+
+class UpdatePersonValidator
+{
+    public static function validate($payload)
+    {
+        $valid = Validator::make($payload, [
+            '@context' => 'required',
+            'id' => 'required|string|url',
+            'type' => [
+                'required',
+                Rule::in(['Update'])
+            ],
+            'actor' => 'required|url',
+            'object' => 'required',
+            'object.id' => [
+                'required',
+                'url',
+                'same:actor',
+                function (string $attribute, mixed $value, Closure $fail) use($payload) {
+                    self::sameHost($attribute, $value, $fail, $payload['actor']);
+                },
+            ],
+            'object.type' => [
+                'required',
+                Rule::in(['Person'])
+            ],
+            'object.publicKey' => 'required',
+            'object.publicKey.id' => [
+                'required',
+                'url',
+                function (string $attribute, mixed $value, Closure $fail) use($payload) {
+                    self::sameHost($attribute, $value, $fail, $payload['actor']);
+                },
+            ],
+            'object.publicKey.owner' => [
+                'required',
+                'url',
+                'same:actor',
+                function (string $attribute, mixed $value, Closure $fail) use($payload) {
+                    self::sameHost($attribute, $value, $fail, $payload['actor']);
+                },
+            ],
+            'object.publicKey.publicKeyPem' => 'required|string',
+            'object.url' => [
+                'required',
+                'url',
+                function (string $attribute, mixed $value, Closure $fail) use($payload) {
+                    self::sameHost($attribute, $value, $fail, $payload['actor']);
+                },
+            ],
+            'object.summary' => 'required|string|nullable',
+            'object.preferredUsername' => 'required|string',
+            'object.name' => 'required|string|nullable',
+            'object.inbox' => [
+                'required',
+                'url',
+                function (string $attribute, mixed $value, Closure $fail) use($payload) {
+                    self::sameHost($attribute, $value, $fail, $payload['actor']);
+                },
+            ],
+            'object.outbox' => [
+                'required',
+                'url',
+                function (string $attribute, mixed $value, Closure $fail) use($payload) {
+                    self::sameHost($attribute, $value, $fail, $payload['actor']);
+                },
+            ],
+            'object.following' => [
+                'required',
+                'url',
+                function (string $attribute, mixed $value, Closure $fail) use($payload) {
+                    self::sameHost($attribute, $value, $fail, $payload['actor']);
+                },
+            ],
+            'object.followers' => [
+                'required',
+                'url',
+                function (string $attribute, mixed $value, Closure $fail) use($payload) {
+                    self::sameHost($attribute, $value, $fail, $payload['actor']);
+                },
+            ],
+            'object.manuallyApprovesFollowers' => 'required',
+            'object.icon' => 'sometimes|nullable',
+            'object.icon.type' => 'sometimes|required_with:object.icon.url,object.icon.mediaType|in:Image',
+            'object.icon.url' => 'sometimes|required_with:object.icon.type,object.icon.mediaType|url',
+            'object.icon.mediaType' => 'sometimes|required_with:object.icon.url,object.icon.type|in:image/jpeg,image/png,image/jpg',
+            'object.endpoints' => 'sometimes',
+            'object.endpoints.sharedInbox' => [
+                'sometimes',
+                'url',
+                function (string $attribute, mixed $value, Closure $fail) use($payload) {
+                    self::sameHost($attribute, $value, $fail, $payload['actor']);
+                },
+            ]
+        ])->passes();
+
+        return $valid;
+    }
+
+    public static function sameHost(string $attribute, mixed $value, Closure $fail, string $comparedHost)
+    {
+        if(empty($value)) {
+            $fail('The ' . $attribute . ' is invalid or empty');
+        }
+        $host = parse_url($value, PHP_URL_HOST);
+        $idHost = parse_url($comparedHost, PHP_URL_HOST);
+        if ($host !== $idHost) {
+            $fail('The ' . $attribute . ' is invalid');
+        }
+    }
+}