Improve update_activitypub_profiles.php daemon

Add sanity case to ensure public key method of rsa class
This commit is contained in:
Diogo Cordeiro 2018-08-07 03:54:14 +01:00
parent b2f6f9f08e
commit 6fbf37b7fe
5 changed files with 201 additions and 170 deletions

View File

@ -413,4 +413,57 @@ class Activitypub_profile extends Managed_DataObject
// TRANS: Exception. %s is a webfinger address.
throw new Exception(sprintf(_m('Could not find a valid profile for "%s".'), $addr));
}
/**
* Update remote user profile in local instance
* Depends on do_update
*
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @param array $res remote response
* @return Profile remote Profile object
*/
public static function update_profile($aprofile, $res)
{
// ActivityPub Profile
$aprofile->uri = $res['id'];
$aprofile->nickname = $res['preferredUsername'];
$aprofile->fullname = isset($res['name']) ? $res['name'] : null;
$aprofile->bio = isset($res['summary']) ? substr(strip_tags($res['summary']), 0, 1000) : null;
$aprofile->inboxuri = $res['inbox'];
$aprofile->sharedInboxuri = isset($res['endpoints']['sharedInbox']) ? $res['endpoints']['sharedInbox'] : $res['inbox'];
$profile = $aprofile->local_profile();
$profile->modified = $aprofile->modified = common_sql_now();
$fields = [
'uri' => 'profileurl',
'nickname' => 'nickname',
'fullname' => 'fullname',
'bio' => 'bio'
];
foreach ($fields as $af => $pf) {
$profile->$pf = $aprofile->$af;
}
// Profile
$profile->update();
$aprofile->update();
// Public Key
Activitypub_rsa::update_public_key($profile, $res['publicKey']['publicKeyPem']);
// Avatar
if (isset($res['icon']['url'])) {
try {
Activitypub_explorer::update_avatar($profile, $res['icon']['url']);
} catch (Exception $e) {
// Let the exception go, it isn't a serious issue
common_debug('An error ocurred while grabbing remote avatar'.$e->getMessage());
}
}
return $profile;
}
}

View File

@ -83,7 +83,6 @@ class Activitypub_rsa extends Managed_DataObject
return $apRSA->private_key;
}
/**
* Guarantees a Public Key for a given profile.
*
@ -104,7 +103,8 @@ class Activitypub_rsa extends Managed_DataObject
} else {
// This should never happen, but try to recover!
if ($fetch) {
// TODO: Call profile updater
$res = Activitypub_explorer::get_remote_user_activity(ActivityPubPlugin::actor_uri($profile));
Activitypub_rsa::update_public_key($profile, $res['publicKey']['publicKeyPem']);
return ensure_public_key($profile, false);
} else {
throw new ServerException('Activitypub_rsa: Failed to find keys for given profile. That should have not happened!');
@ -156,4 +156,23 @@ class Activitypub_rsa extends Managed_DataObject
$public_key = $pubKey["key"];
unset($pubKey);
}
/**
* Update public key.
*
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @param Profile $profile
* @param string $public_key
*/
public static function update_public_key($profile, $public_key)
{
// Public Key
$apRSA = new Activitypub_rsa();
$apRSA->profile_id = $profile->getID();
$apRSA->public_key = $public_key;
$apRSA->modified = common_sql_now();
if(!$apRSA->update()) {
$apRSA->insert();
}
}
}

View File

@ -1,168 +0,0 @@
#!/usr/bin/env php
<?php
/**
* GNU social - a federating social network
*
* ActivityPubPlugin implementation for GNU Social
*
* LICENCE: 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 <http://www.gnu.org/licenses/>.
*
* @category Plugin
* @package GNUsocial
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @copyright 2018 Free Software Foundation http://fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link https://www.gnu.org/software/social/
*/
define('INSTALLDIR', realpath(__DIR__ . '/../../..'));
$shortoptions = 'u:af';
$longoptions = ['uri=', 'all', 'force'];
$helptext = <<<END_OF_HELP
update_activitypub_profiles.php [options]
Refetch / update ActivityPub RSA keys, profile info and avatars. Useful if you
do something like accidentally delete your avatars directory when
you have no backup.
END_OF_HELP;
require_once INSTALLDIR.'/scripts/commandline.inc';
$quiet = have_option('q', 'quiet');
if (!$quiet) {
echo "ActivityPub Profiles updater will now start!\n";
echo "Summoning Diogo Cordeiro, Richard Stallman and Chuck Norris to help us with this task!\n";
}
$user = new Activitypub_profile();
$cnt = $user->find();
if (!empty($cnt)) {
if (!$quiet) {
echo "Found {$cnt} ActivityPub profiles:\n";
}
} else {
if (have_option('u', 'uri')) {
if (!$quiet) {
echo "Couldn't find an existing ActivityPub profile with that URI.\n";
}
} else {
if (!$quiet) {
echo "Couldn't find any existing ActivityPub profiles.\n";
}
}
exit(0);
}
while ($user->fetch()) {
try {
$res = grab_remote_user($user->uri);
} catch (Exception $e) {
// let it go
}
if (!$quiet) {
echo "Updated ".update_profile($user, $res)->getBestName()."\n";
}
}
/**
* Update Profile and ActivityPub Profile objects
*
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @access public
*/
function do_update($aprofile)
{
$profile = $aprofile->local_profile();
$profile->modified = $aprofile->modified = common_sql_now();
$fields = [
'uri' => 'profileurl',
'nickname' => 'nickname',
'fullname' => 'fullname',
'bio' => 'bio'
];
foreach ($fields as $af => $pf) {
$profile->$pf = $aprofile->$af;
}
$profile->update();
$aprofile->update();
}
/**
* Save remote user profile in local instance
*
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @param array $res remote response
* @return Profile remote Profile object
*/
function update_profile($aprofile, $res)
{
// ActivityPub Profile
$aprofile->uri = $res['id'];
$aprofile->nickname = $res['preferredUsername'];
$aprofile->fullname = isset($res['name']) ? $res['name'] : null;
$aprofile->bio = isset($res['summary']) ? substr(strip_tags($res['summary']), 0, 1000) : null;
$aprofile->inboxuri = $res['inbox'];
$aprofile->sharedInboxuri = isset($res['endpoints']['sharedInbox']) ? $res['endpoints']['sharedInbox'] : $res['inbox'];
do_update($aprofile);
$profile = $aprofile->local_profile();
// Public Key
$apRSA = new Activitypub_rsa();
$apRSA->profile_id = $profile->getID();
$apRSA->public_key = $res['publicKey']['publicKeyPem'];
$apRSA->modified = common_sql_now();
if(!$apRSA->update())
$apRSA->insert();
// Avatar
if (isset($res['icon']['url'])) {
try {
Activitypub_explorer::update_avatar($profile, $res['icon']['url']);
} catch (Exception $e) {
// Let the exception go, it isn't a serious issue
common_debug('An error ocurred while grabbing remote avatar'.$e->getMessage());
}
}
return $profile;
}
/**
* Get a remote user(s) profile(s) from its URL
*
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @param string $url User's url
* @return boolean success state
*/
function grab_remote_user($url)
{
$client = new HTTPClient();
$headers = [];
$headers[] = 'Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams"';
$headers[] = 'User-Agent: GNUSocialBot v0.1 - https://gnu.io/social';
$response = $client->get($url, $headers);
$res = json_decode($response->getBody(), true);
if (Activitypub_explorer::validate_remote_response($res)) {
common_debug('ActivityPub Explorer: Found a valid remote actor for '.$url);
return $res;
}
throw new Exception('Failed to grab.');
}

View File

@ -0,0 +1,104 @@
#!/usr/bin/env php
<?php
/**
* GNU social - a federating social network
*
* ActivityPubPlugin implementation for GNU Social
*
* LICENCE: 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 <http://www.gnu.org/licenses/>.
*
* @category Plugin
* @package GNUsocial
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @copyright 2018 Free Software Foundation http://fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link https://www.gnu.org/software/social/
*/
define('INSTALLDIR', realpath(__DIR__ . '/../../..'));
$shortoptions = 'u:af';
$longoptions = ['uri=', 'all', 'force'];
$helptext = <<<END_OF_HELP
update_activitypub_profiles.php [options]
Refetch / update ActivityPub RSA keys, profile info and avatars. Useful if you
do something like accidentally delete your avatars directory when
you have no backup.
-u --uri ActivityPub profile URI to update
-a --all update all
END_OF_HELP;
require_once INSTALLDIR.'/scripts/commandline.inc';
$quiet = have_option('q', 'quiet');
if (!$quiet) {
echo "ActivityPub Profiles updater will now start!\n";
echo "Summoning Diogo Cordeiro, Richard Stallman and Chuck Norris to help us with this task!\n";
}
if (have_option('u', 'uri')) {
$uri = get_option_value('u', 'uri');
$discovery = new Activitypub_explorer();
$discovery = $discovery->lookup($uri);
if (empty($discovery)) {
echo "Bad URI\n";
exit(1);
}
$user = $discovery->lookup($uri)[0];
try {
$res = Activitypub_explorer::get_remote_user_activity($uri);
} catch (Exception $e) {
echo $e->getMessage()."\n";
exit(1);
}
if (!$quiet) {
echo "Updated ".Activitypub_profile::update_profile($user, $res)->getBestName()."\n";
}
} else if (!have_option('a', 'all')) {
show_help();
exit(1);
}
$user = new Activitypub_profile();
$cnt = $user->find();
if (!empty($cnt)) {
if (!$quiet) {
echo "Found {$cnt} ActivityPub profiles:\n";
}
} else {
if (have_option('u', 'uri')) {
if (!$quiet) {
echo "Couldn't find an existing ActivityPub profile with that URI.\n";
}
} else {
if (!$quiet) {
echo "Couldn't find any existing ActivityPub profiles.\n";
}
}
exit(0);
}
while ($user->fetch()) {
try {
$res = Activitypub_explorer::get_remote_user_activity($user->uri);
if (!$quiet) {
echo "Updated ".Activitypub_profile::update_profile($user, $res)->getBestName()."\n";
}
} catch (Exception $e) {
// let it go
}
}

View File

@ -438,4 +438,27 @@ class Activitypub_explorer
return true;
}
/**
* Get a remote user array from its URL (this function is only used for
* profile updating and shall not be used for anything else)
*
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @param string $url User's url
* @throws Exception
*/
public static function get_remote_user_activity($url)
{
$client = new HTTPClient();
$headers = [];
$headers[] = 'Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams"';
$headers[] = 'User-Agent: GNUSocialBot v0.1 - https://gnu.io/social';
$response = $client->get($url, $headers);
$res = json_decode($response->getBody(), true);
if (Activitypub_explorer::validate_remote_response($res)) {
common_debug('ActivityPub Explorer: Found a valid remote actor for '.$url);
return $res;
}
throw new Exception('ActivityPub Explorer: Failed to get activity.');
}
}