[ActivityPub] Autofix profile URIs through alias discovering

Activitypub_profile:
- Add updateUri method

explorer:
- Add grab_aliases method
- Update grab_local_user's online course to grab and test aliases
This commit is contained in:
tenma 2020-03-27 19:25:43 +00:00 committed by Diogo Cordeiro
parent 2183875e9e
commit c6543e1f95
2 changed files with 89 additions and 58 deletions

View File

@ -495,6 +495,21 @@ class Activitypub_profile extends Managed_DataObject
return $profile;
}
/**
* Update remote user profile URI in local instance
*
* @param string $uri
* @return void
* @throws Exception (if the update fails)
* @author Bruno Casteleiro <brunoccast@fc.up.pt>
*/
public function updateUri(string $uri)
{
$orig = clone($this);
$this->uri = $uri;
$this->updateWithKeys($orig);
}
/**
* Getter for the number of subscribers of a
* given local profile

View File

@ -39,7 +39,6 @@ defined('GNUSOCIAL') || die();
class Activitypub_explorer
{
private $discovered_actor_profiles = [];
private $temp_res; // global variable to hold a temporary http response
/**
* Shortcut function to get a single profile from its URL.
@ -118,26 +117,26 @@ class Activitypub_explorer
}
/**
* This ensures that we are using a valid ActivityPub URI
* Fetch all the aliases for some actor
*
* @param string $url
* @return bool success state (related to the response)
* @throws Exception (If the HTTP request fails)
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @param string $url actor's url
* @return array aliases
* @throws Exception (If the Discovery's HTTP requests fail)
* @author Bruno Casteleiro <brunoccast@fc.up.pt>
*/
private function ensure_proper_remote_uri($url)
private function grab_aliases(string $url): array
{
$client = new HTTPClient();
$response = $client->get($url, ACTIVITYPUB_HTTP_CLIENT_HEADERS);
$res = json_decode($response->getBody(), true);
if (self::validate_remote_response($res)) {
$this->temp_res = $res;
return true;
} else {
common_debug('ActivityPub Explorer: Invalid potential remote actor while ensuring URI: ' . $url . '. He returned the following: ' . json_encode($res, JSON_UNESCAPED_SLASHES));
$disco = new Discovery();
$xrd = $disco->lookup($url);
$all_ids = array_merge([$xrd->subject], $xrd->aliases);
if (!in_array($url, $all_ids)) {
common_debug('grab_aliases: The URI we got was not listed itself when doing discovery on it');
return [];
}
return false;
return $all_ids;
}
/**
@ -155,44 +154,65 @@ class Activitypub_explorer
{
if ($online) {
common_debug('ActivityPub Explorer: Searching locally for ' . $uri . ' with online resources.');
$all_ids = $this->grab_aliases($uri);
} else {
common_debug('ActivityPub Explorer: Searching locally for ' . $uri . ' offline.');
}
// Ensure proper remote URI
// If an exception occurs here it's better to just leave everything
// break than to continue processing
if ($online && $this->ensure_proper_remote_uri($uri)) {
$uri = $this->temp_res["id"];
$all_ids = [$uri];
}
if (empty($all_ids)) {
common_debug('AcvitityPub Explorer: Unable to find a local profile for ' . $uri);
return false;
}
foreach ($all_ids as $alias) {
// Try standard ActivityPub route
// Is this a known filthy little mudblood?
$aprofile = self::get_aprofile_by_url($uri);
$aprofile = self::get_aprofile_by_url($alias);
if ($aprofile instanceof Activitypub_profile) {
common_debug('ActivityPub Explorer: Found a local Aprofile for ' . $alias);
// double check to confirm this alias as a legitimate one
if ($online) {
common_debug('ActivityPub Explorer: Double-checking ' . $alias . ' to confirm it as a legitimate alias');
$disco = new Discovery();
$xrd = $disco->lookup($aprofile->getUri());
$doublecheck_aliases = array_merge(array($xrd->subject), $xrd->aliases);
if (in_array($uri, $doublecheck_aliases)) {
// the original URI is present, we're sure now!
// update aprofile's URI and proceed
common_debug('ActivityPub Explorer: ' . $alias . ' is a legitimate alias');
$aprofile->updateUri($uri);
} else {
common_debug('ActivityPub Explorer: ' . $alias . ' is not an alias we can trust');
continue;
}
}
// Assert: This AProfile has a Profile, no try catch.
$profile = $aprofile->local_profile();
common_debug('ActivityPub Explorer: Found a local Aprofile for ' . $uri);
// We found something!
$this->discovered_actor_profiles[] = $profile;
unset($this->temp_res); // IMPORTANT to avoid _dangerous_ noise in the Explorer system
return true;
} else {
common_debug('ActivityPub Explorer: Unable to find a local Aprofile for ' . $uri . ' - looking for a Profile instead.');
common_debug('ActivityPub Explorer: Unable to find a local Aprofile for ' . $alias . ' - looking for a Profile instead.');
// Well, maybe it is a pure blood?
// Iff, we are in the same instance:
$ACTIVITYPUB_BASE_ACTOR_URI = common_local_url('userbyid', ['id' => null], null, null, false, true); // @FIXME: Could this be too hardcoded?
$ACTIVITYPUB_BASE_ACTOR_URI_length = strlen($ACTIVITYPUB_BASE_ACTOR_URI);
if (substr($uri, 0, $ACTIVITYPUB_BASE_ACTOR_URI_length) === $ACTIVITYPUB_BASE_ACTOR_URI) {
if (substr($alias, 0, $ACTIVITYPUB_BASE_ACTOR_URI_length) === $ACTIVITYPUB_BASE_ACTOR_URI) {
try {
$profile = Profile::getByID((int)substr($uri, $ACTIVITYPUB_BASE_ACTOR_URI_length));
common_debug('ActivityPub Explorer: Found a Profile for ' . $uri);
$profile = Profile::getByID((int)substr($alias, $ACTIVITYPUB_BASE_ACTOR_URI_length));
common_debug('ActivityPub Explorer: Found a Profile for ' . $alias);
// We found something!
$this->discovered_actor_profiles[] = $profile;
unset($this->temp_res); // IMPORTANT to avoid _dangerous_ noise in the Explorer system
return true;
} catch (Exception $e) {
// Let the exception go on its merry way.
common_debug('ActivityPub Explorer: Unable to find a Profile for ' . $uri);
common_debug('ActivityPub Explorer: Unable to find a Profile for ' . $alias);
}
}
}
}
@ -221,14 +241,10 @@ class Activitypub_explorer
private function grab_remote_user($url)
{
common_debug('ActivityPub Explorer: Trying to grab a remote actor for ' . $url);
if (!isset($this->temp_res)) {
$client = new HTTPClient();
$response = $client->get($url, ACTIVITYPUB_HTTP_CLIENT_HEADERS);
$res = json_decode($response->getBody(), true);
} else {
$res = $this->temp_res;
unset($this->temp_res);
}
if (isset($res['type']) && $res['type'] === 'OrderedCollection' && isset($res['first'])) { // It's a potential collection of actors!!!
common_debug('ActivityPub Explorer: Found a collection of actors for ' . $url);
$this->travel_collection($res['first']);