[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; 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 * Getter for the number of subscribers of a
* given local profile * given local profile

View File

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