mirror of
https://github.com/pixelfed/pixelfed.git
synced 2024-12-25 22:43:18 +00:00
Merge pull request #4455 from pixelfed/staging
Handle Update.Person activities
This commit is contained in:
commit
61346b104c
9 changed files with 502 additions and 6 deletions
|
@ -60,7 +60,7 @@ class RemoteAvatarFetch implements ShouldQueue
|
||||||
{
|
{
|
||||||
$profile = $this->profile;
|
$profile = $this->profile;
|
||||||
|
|
||||||
if(config_cache('pixelfed.cloud_storage') == false && config_cache('federation.avatars.store_local') == false) {
|
if(boolval(config_cache('pixelfed.cloud_storage')) == false && boolval(config_cache('federation.avatars.store_local')) == false) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ class RemoteAvatarFetch implements ShouldQueue
|
||||||
$avatar->remote_url = $icon['url'];
|
$avatar->remote_url = $icon['url'];
|
||||||
$avatar->save();
|
$avatar->save();
|
||||||
|
|
||||||
MediaStorageService::avatar($avatar, config_cache('pixelfed.cloud_storage') == false);
|
MediaStorageService::avatar($avatar, boolval(config_cache('pixelfed.cloud_storage')) == false);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
97
app/Jobs/AvatarPipeline/RemoteAvatarFetchFromUrl.php
Normal file
97
app/Jobs/AvatarPipeline/RemoteAvatarFetchFromUrl.php
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs\AvatarPipeline;
|
||||||
|
|
||||||
|
use App\Avatar;
|
||||||
|
use App\Profile;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use App\Util\ActivityPub\Helpers;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Zttp\Zttp;
|
||||||
|
use App\Http\Controllers\AvatarController;
|
||||||
|
use Cache;
|
||||||
|
use Storage;
|
||||||
|
use Log;
|
||||||
|
use Illuminate\Http\File;
|
||||||
|
use App\Services\AccountService;
|
||||||
|
use App\Services\MediaStorageService;
|
||||||
|
use App\Services\ActivityPubFetchService;
|
||||||
|
|
||||||
|
class RemoteAvatarFetchFromUrl implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
protected $profile;
|
||||||
|
protected $url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the job if its models no longer exist.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public $deleteWhenMissingModels = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of times the job may be attempted.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $tries = 1;
|
||||||
|
public $timeout = 300;
|
||||||
|
public $maxExceptions = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(Profile $profile, $url)
|
||||||
|
{
|
||||||
|
$this->profile = $profile;
|
||||||
|
$this->url = $url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$profile = $this->profile;
|
||||||
|
|
||||||
|
Cache::forget('avatar:' . $profile->id);
|
||||||
|
AccountService::del($profile->id);
|
||||||
|
|
||||||
|
if(boolval(config_cache('pixelfed.cloud_storage')) == false && boolval(config_cache('federation.avatars.store_local')) == false) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($profile->domain == null || $profile->private_key) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$avatar = Avatar::whereProfileId($profile->id)->first();
|
||||||
|
|
||||||
|
if(!$avatar) {
|
||||||
|
$avatar = new Avatar;
|
||||||
|
$avatar->profile_id = $profile->id;
|
||||||
|
$avatar->is_remote = true;
|
||||||
|
$avatar->remote_url = $this->url;
|
||||||
|
$avatar->save();
|
||||||
|
} else {
|
||||||
|
$avatar->remote_url = $this->url;
|
||||||
|
$avatar->is_remote = true;
|
||||||
|
$avatar->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MediaStorageService::avatar($avatar, boolval(config_cache('pixelfed.cloud_storage')) == false, true);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
94
app/Jobs/ProfilePipeline/HandleUpdateActivity.php
Normal file
94
app/Jobs/ProfilePipeline/HandleUpdateActivity.php
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
<?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 Cache;
|
||||||
|
use Purify;
|
||||||
|
use App\Jobs\AvatarPipeline\RemoteAvatarFetchFromUrl;
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($payload['object']['icon']) && isset($payload['object']['icon']['url'])) {
|
||||||
|
RemoteAvatarFetchFromUrl::dispatch($profile, $payload['object']['icon']['url'])->onQueue('low');
|
||||||
|
} else {
|
||||||
|
$profile->avatar->update(['remote_url' => null]);
|
||||||
|
Cache::forget('avatar:' . $profile->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
|
@ -178,6 +178,14 @@ class Profile extends Model
|
||||||
return url('/storage/avatars/default.jpg');
|
return url('/storage/avatars/default.jpg');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( $avatar->is_remote &&
|
||||||
|
$avatar->remote_url &&
|
||||||
|
boolval(config_cache('pixelfed.cloud_storage')) == false &&
|
||||||
|
boolval(config_cache('federation.avatars.store_local')) == true
|
||||||
|
) {
|
||||||
|
return $avatar->remote_url;
|
||||||
|
}
|
||||||
|
|
||||||
if($path === 'public/avatars/default.jpg') {
|
if($path === 'public/avatars/default.jpg') {
|
||||||
return url('/storage/avatars/default.jpg');
|
return url('/storage/avatars/default.jpg');
|
||||||
}
|
}
|
||||||
|
|
|
@ -191,7 +191,7 @@ class MediaStorageService {
|
||||||
unlink($tmpName);
|
unlink($tmpName);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function fetchAvatar($avatar, $local = false)
|
protected function fetchAvatar($avatar, $local = false, $skipRecentCheck = false)
|
||||||
{
|
{
|
||||||
$url = $avatar->remote_url;
|
$url = $avatar->remote_url;
|
||||||
$driver = $local ? 'local' : config('filesystems.cloud');
|
$driver = $local ? 'local' : config('filesystems.cloud');
|
||||||
|
@ -215,9 +215,14 @@ class MediaStorageService {
|
||||||
$mime = $head['mime'];
|
$mime = $head['mime'];
|
||||||
$max_size = (int) config('pixelfed.max_avatar_size') * 1000;
|
$max_size = (int) config('pixelfed.max_avatar_size') * 1000;
|
||||||
|
|
||||||
|
if(!$skipRecentCheck) {
|
||||||
if($avatar->last_fetched_at && $avatar->last_fetched_at->gt(now()->subDay())) {
|
if($avatar->last_fetched_at && $avatar->last_fetched_at->gt(now()->subDay())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Cache::forget('avatar:' . $avatar->profile_id);
|
||||||
|
AccountService::del($avatar->profile_id);
|
||||||
|
|
||||||
// handle pleroma edge case
|
// handle pleroma edge case
|
||||||
if(Str::endsWith($mime, '; charset=utf-8')) {
|
if(Str::endsWith($mime, '; charset=utf-8')) {
|
||||||
|
@ -266,7 +271,7 @@ class MediaStorageService {
|
||||||
$avatar->save();
|
$avatar->save();
|
||||||
|
|
||||||
Cache::forget('avatar:' . $avatar->profile_id);
|
Cache::forget('avatar:' . $avatar->profile_id);
|
||||||
Cache::forget(AccountService::CACHE_KEY . $avatar->profile_id);
|
AccountService::del($avatar->profile_id);
|
||||||
|
|
||||||
unlink($tmpName);
|
unlink($tmpName);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
app/Util/ActivityPub/Validator/UpdatePersonValidator.php
Normal file
119
app/Util/ActivityPub/Validator/UpdatePersonValidator.php
Normal file
|
@ -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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,8 @@ use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class StoryValidationTest extends TestCase
|
class StoryValidationTest extends TestCase
|
||||||
{
|
{
|
||||||
|
public $activity;
|
||||||
|
|
||||||
public function setUp(): void
|
public function setUp(): void
|
||||||
{
|
{
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
161
tests/Unit/ActivityPub/UpdatePersonValidationTest.php
Normal file
161
tests/Unit/ActivityPub/UpdatePersonValidationTest.php
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Unit\ActivityPub;
|
||||||
|
|
||||||
|
use App\Util\ActivityPub\Validator\UpdatePersonValidator;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class UpdatePersonValidationTest extends TestCase
|
||||||
|
{
|
||||||
|
public $activity;
|
||||||
|
|
||||||
|
public function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->activity = json_decode('{"type":"Update","object":{"url":"http://mastodon.example.org/@gargron","type":"Person","summary":"<p>Some bio</p>","publicKey":{"publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0gs3VnQf6am3R+CeBV4H\nlfI1HZTNRIBHgvFszRZkCERbRgEWMu+P+I6/7GJC5H5jhVQ60z4MmXcyHOGmYMK/\n5XyuHQz7V2Ssu1AxLfRN5Biq1ayb0+DT/E7QxNXDJPqSTnstZ6C7zKH/uAETqg3l\nBonjCQWyds+IYbQYxf5Sp3yhvQ80lMwHML3DaNCMlXWLoOnrOX5/yK5+dedesg2\n/HIvGk+HEt36vm6hoH7bwPuEkgA++ACqwjXRe5Mta7i3eilHxFaF8XIrJFARV0t\nqOu4GID/jG6oA+swIWndGrtR2QRJIt9QIBFfK3HG5M0koZbY1eTqwNFRHFL3xaD\nUQIDAQAB\n-----END PUBLIC KEY-----\n","owner":"http://mastodon.example.org/users/gargron","id":"http://mastodon.example.org/users/gargron#main-key"},"preferredUsername":"gargron","outbox":"http://mastodon.example.org/users/gargron/outbox","name":"gargle","manuallyApprovesFollowers":false,"inbox":"http://mastodon.example.org/users/gargron/inbox","id":"http://mastodon.example.org/users/gargron","following":"http://mastodon.example.org/users/gargron/following","followers":"http://mastodon.example.org/users/gargron/followers","endpoints":{"sharedInbox":"http://mastodon.example.org/inbox"},"attachment":[{"type":"PropertyValue","name":"foo","value":"updated"},{"type":"PropertyValue","name":"foo1","value":"updated"}],"icon":{"type":"Image","mediaType":"image/jpeg","url":"https://cd.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"},"image":{"type":"Image","mediaType":"image/png","url":"https://cd.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"}},"id":"http://mastodon.example.org/users/gargron#updates/1519563538","actor":"http://mastodon.example.org/users/gargron","@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"}]}', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function schemaTest()
|
||||||
|
{
|
||||||
|
$this->assertTrue(UpdatePersonValidator::validate($this->activity));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function invalidContext()
|
||||||
|
{
|
||||||
|
$activity = $this->activity;
|
||||||
|
unset($activity['@context']);
|
||||||
|
$activity['@@context'] = 'https://www.w3.org/ns/activitystreams';
|
||||||
|
$this->assertFalse(UpdatePersonValidator::validate($activity));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function missingContext()
|
||||||
|
{
|
||||||
|
$activity = $this->activity;
|
||||||
|
unset($activity['@context']);
|
||||||
|
$this->assertFalse(UpdatePersonValidator::validate($activity));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function missingId()
|
||||||
|
{
|
||||||
|
$activity = $this->activity;
|
||||||
|
unset($activity['id']);
|
||||||
|
$this->assertFalse(UpdatePersonValidator::validate($activity));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function missingType()
|
||||||
|
{
|
||||||
|
$activity = $this->activity;
|
||||||
|
unset($activity['type']);
|
||||||
|
$this->assertFalse(UpdatePersonValidator::validate($activity));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function invalidType()
|
||||||
|
{
|
||||||
|
$activity = $this->activity;
|
||||||
|
$activity['type'] = 'Create';
|
||||||
|
$this->assertFalse(UpdatePersonValidator::validate($activity));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function invalidObjectType()
|
||||||
|
{
|
||||||
|
$activity = $this->activity;
|
||||||
|
$activity['object']['type'] = 'Note';
|
||||||
|
$this->assertFalse(UpdatePersonValidator::validate($activity));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function invalidActorMatchingObjectId()
|
||||||
|
{
|
||||||
|
$activity = $this->activity;
|
||||||
|
$activity['object']['id'] = 'https://example.org/@user';
|
||||||
|
$this->assertFalse(UpdatePersonValidator::validate($activity));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function invalidActorUrlMatchingObjectId()
|
||||||
|
{
|
||||||
|
$activity = $this->activity;
|
||||||
|
$activity['object']['id'] = $activity['object']['id'] . 'test';
|
||||||
|
$this->assertFalse(UpdatePersonValidator::validate($activity));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function missingActorPublicKey()
|
||||||
|
{
|
||||||
|
$activity = $this->activity;
|
||||||
|
unset($activity['object']['publicKey']);
|
||||||
|
$this->assertFalse(UpdatePersonValidator::validate($activity));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function invalidActorPublicKey()
|
||||||
|
{
|
||||||
|
$activity = $this->activity;
|
||||||
|
$activity['object']['publicKey'] = null;
|
||||||
|
$this->assertFalse(UpdatePersonValidator::validate($activity));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function invalidActorPublicKeyId()
|
||||||
|
{
|
||||||
|
$activity = $this->activity;
|
||||||
|
$activity['object']['publicKey']['id'] = null;
|
||||||
|
$this->assertFalse(UpdatePersonValidator::validate($activity));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function invalidActorPublicKeyIdHost()
|
||||||
|
{
|
||||||
|
$activity = $this->activity;
|
||||||
|
$activity['object']['publicKey']['id'] = 'https://example.org/test';
|
||||||
|
$this->assertFalse(UpdatePersonValidator::validate($activity));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function invalidActorAvatar()
|
||||||
|
{
|
||||||
|
$activity = $this->activity;
|
||||||
|
$activity['object']['icon']['type'] = 'TikTok';
|
||||||
|
$this->assertFalse(UpdatePersonValidator::validate($activity));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function invalidActorAvatarMediaType()
|
||||||
|
{
|
||||||
|
$activity = $this->activity;
|
||||||
|
$activity['object']['icon']['mediaType'] = 'video/mp4';
|
||||||
|
$this->assertFalse(UpdatePersonValidator::validate($activity));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function validActorAvatarMediaTypePng()
|
||||||
|
{
|
||||||
|
$activity = $this->activity;
|
||||||
|
$activity['object']['icon']['mediaType'] = 'image/png';
|
||||||
|
$this->assertTrue(UpdatePersonValidator::validate($activity));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function validActorAvatarMediaTypeJpeg()
|
||||||
|
{
|
||||||
|
$activity = $this->activity;
|
||||||
|
$activity['object']['icon']['mediaType'] = 'image/jpeg';
|
||||||
|
$this->assertTrue(UpdatePersonValidator::validate($activity));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function validActorAvatarMediaUrl()
|
||||||
|
{
|
||||||
|
$activity = $this->activity;
|
||||||
|
$activity['object']['icon']['url'] = 'http://example.org/avatar.png';
|
||||||
|
$this->assertTrue(UpdatePersonValidator::validate($activity));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue