From a17c010bb9b8d53e544375722ef21d5a02bad26d Mon Sep 17 00:00:00 2001 From: tenma Date: Fri, 27 Mar 2020 19:25:43 +0000 Subject: [PATCH] [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 --- .../classes/Activitypub_profile.php | 15 ++ plugins/ActivityPub/lib/explorer.php | 132 ++++++++++-------- 2 files changed, 89 insertions(+), 58 deletions(-) diff --git a/plugins/ActivityPub/classes/Activitypub_profile.php b/plugins/ActivityPub/classes/Activitypub_profile.php index 26a2bbc5f9..28fea5aa27 100644 --- a/plugins/ActivityPub/classes/Activitypub_profile.php +++ b/plugins/ActivityPub/classes/Activitypub_profile.php @@ -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 + */ + 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 diff --git a/plugins/ActivityPub/lib/explorer.php b/plugins/ActivityPub/lib/explorer.php index d1f0d02bf8..08e66ad1ce 100644 --- a/plugins/ActivityPub/lib/explorer.php +++ b/plugins/ActivityPub/lib/explorer.php @@ -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 + * @param string $url actor's url + * @return array aliases + * @throws Exception (If the Discovery's HTTP requests fail) + * @author Bruno Casteleiro */ - 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]; } - // Try standard ActivityPub route - // Is this a known filthy little mudblood? - $aprofile = self::get_aprofile_by_url($uri); - if ($aprofile instanceof Activitypub_profile) { - // 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.'); - // 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) { - try { - $profile = Profile::getByID((int)substr($uri, $ACTIVITYPUB_BASE_ACTOR_URI_length)); - common_debug('ActivityPub Explorer: Found a Profile 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; - } catch (Exception $e) { - // Let the exception go on its merry way. - common_debug('ActivityPub Explorer: Unable to find a Profile for ' . $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($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(); + // We found something! + $this->discovered_actor_profiles[] = $profile; + return true; + } else { + 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($alias, 0, $ACTIVITYPUB_BASE_ACTOR_URI_length) === $ACTIVITYPUB_BASE_ACTOR_URI) { + try { + $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; + return true; + } catch (Exception $e) { + // Let the exception go on its merry way. + 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); - } + $client = new HTTPClient(); + $response = $client->get($url, ACTIVITYPUB_HTTP_CLIENT_HEADERS); + $res = json_decode($response->getBody(), true); + 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']);