Add new admin debug module for ActivityPub

This commit is contained in:
Hypolite Petovan 2020-07-20 00:39:17 -04:00
parent c26cc5b75e
commit 0a71495fa4
4 changed files with 170 additions and 0 deletions

View file

@ -121,6 +121,7 @@ abstract class BaseAdmin extends BaseModule
'webfinger' => ['webfinger' , DI::l10n()->t('check webfinger') , 'webfinger'], 'webfinger' => ['webfinger' , DI::l10n()->t('check webfinger') , 'webfinger'],
'itemsource' => ['admin/item/source' , DI::l10n()->t('Item Source') , 'itemsource'], 'itemsource' => ['admin/item/source' , DI::l10n()->t('Item Source') , 'itemsource'],
'babel' => ['babel' , DI::l10n()->t('Babel') , 'babel'], 'babel' => ['babel' , DI::l10n()->t('Babel') , 'babel'],
'debug/ap' => ['debug/ap' , DI::l10n()->t('ActivityPub Conversion') , 'debug/ap'],
]], ]],
]; ];

View file

@ -0,0 +1,144 @@
<?php
/**
* @copyright Copyright (C) 2020, Friendica
*
* @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 <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Module\Debug;
use Friendica\BaseModule;
use Friendica\Content\Text;
use Friendica\Core\Logger;
use Friendica\Core\Renderer;
use Friendica\DI;
use Friendica\Model\Item;
use Friendica\Model\Tag;
use Friendica\Protocol\ActivityPub;
use Friendica\Util\JsonLD;
use Friendica\Util\XML;
class ActivityPubConversion extends BaseModule
{
public static function content(array $parameters = [])
{
function visible_whitespace($s)
{
return '<pre>' . htmlspecialchars($s) . '</pre>';
}
$results = [];
if (!empty($_REQUEST['source'])) {
try {
$source = json_decode($_REQUEST['source'], true);
$trust_source = true;
$uid = local_user();
$push = false;
if (!$source) {
throw new \Exception('Failed to decode source JSON');
}
$formatted = json_encode($source, JSON_PRETTY_PRINT);
$results[] = [
'title' => DI::l10n()->t('Formatted'),
'content' => visible_whitespace(trim(var_export($formatted, true), "'")),
];
$results[] = [
'title' => DI::l10n()->t('Source'),
'content' => visible_whitespace(var_export($source, true))
];
$activity = JsonLD::compact($source);
if (!$activity) {
throw new \Exception('Failed to compact JSON');
}
$results[] = [
'title' => DI::l10n()->t('Activity'),
'content' => visible_whitespace(var_export($activity, true))
];
$type = JsonLD::fetchElement($activity, '@type');
if (!$type) {
throw new \Exception('Empty type');
}
if (!JsonLD::fetchElement($activity, 'as:object', '@id')) {
throw new \Exception('Empty object');
}
if (!JsonLD::fetchElement($activity, 'as:actor', '@id')) {
throw new \Exception('Empty actor');
}
// Don't trust the source if "actor" differs from "attributedTo". The content could be forged.
if ($trust_source && ($type == 'as:Create') && is_array($activity['as:object'])) {
$actor = JsonLD::fetchElement($activity, 'as:actor', '@id');
$attributed_to = JsonLD::fetchElement($activity['as:object'], 'as:attributedTo', '@id');
$trust_source = ($actor == $attributed_to);
if (!$trust_source) {
throw new \Exception('Not trusting actor: ' . $actor . '. It differs from attributedTo: ' . $attributed_to);
}
}
// $trust_source is called by reference and is set to true if the content was retrieved successfully
$object_data = ActivityPub\Receiver::prepareObjectData($activity, $uid, $push, $trust_source);
if (empty($object_data)) {
throw new \Exception('No object data found');
}
if (!$trust_source) {
throw new \Exception('No trust for activity type "' . $type . '", so we quit now.');
}
if (!empty($body) && empty($object_data['raw'])) {
$object_data['raw'] = $body;
}
// Internal flag for thread completion. See Processor.php
if (!empty($activity['thread-completion'])) {
$object_data['thread-completion'] = $activity['thread-completion'];
}
$results[] = [
'title' => DI::l10n()->t('Object data'),
'content' => visible_whitespace(var_export($object_data, true))
];
$item = ActivityPub\Processor::createItem($object_data);
$results[] = [
'title' => DI::l10n()->t('Result Item'),
'content' => visible_whitespace(var_export($item, true))
];
} catch (\Throwable $e) {
$results[] = [
'title' => DI::l10n()->t('Error'),
'content' => $e->getMessage(),
];
}
}
$tpl = Renderer::getMarkupTemplate('debug/activitypubconversion.tpl');
$o = Renderer::replaceMacros($tpl, [
'$source' => ['source', DI::l10n()->t('Source activity'), $_REQUEST['source'] ?? '', ''],
'$results' => $results
]);
return $o;
}
}

View file

@ -107,6 +107,7 @@ return [
'/apps' => [Module\Apps::class, [R::GET]], '/apps' => [Module\Apps::class, [R::GET]],
'/attach/{item:\d+}' => [Module\Attach::class, [R::GET]], '/attach/{item:\d+}' => [Module\Attach::class, [R::GET]],
'/babel' => [Module\Debug\Babel::class, [R::GET, R::POST]], '/babel' => [Module\Debug\Babel::class, [R::GET, R::POST]],
'/debug/ap' => [Module\Debug\ActivityPubConversion::class, [R::GET, R::POST]],
'/bookmarklet' => [Module\Bookmarklet::class, [R::GET]], '/bookmarklet' => [Module\Bookmarklet::class, [R::GET]],
'/community[/{content}[/{accounttype}]]' => [Module\Conversation\Community::class, [R::GET]], '/community[/{content}[/{accounttype}]]' => [Module\Conversation\Community::class, [R::GET]],

View file

@ -0,0 +1,24 @@
<h2>ActivityPub Conversion</h2>
<form action="debug/ap" method="post" class="panel panel-default">
<div class="panel-body">
<div class="form-group">
{{include file="field_textarea.tpl" field=$source}}
</div>
<p><button type="submit" class="btn btn-primary">Submit</button></p>
</div>
</form>
{{if $results}}
<div class="babel-results">
{{foreach $results as $result}}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{{$result.title}}</h3>
</div>
<div class="panel-body">
{{$result.content nofilter}}
</div>
</div>
{{/foreach}}
</div>
{{/if}}