[TheFreeNetwork] Do not allow lower priority protocols to handle remote actors already handled by the higher ones

This commit is contained in:
Diogo Cordeiro 2020-08-26 23:55:36 +01:00
parent db593496a7
commit ef6a986dc6
7 changed files with 66 additions and 12 deletions

View File

@ -0,0 +1,40 @@
<?php
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social 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.
//
// GNU social 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 GNU social. If not, see <http://www.gnu.org/licenses/>.
/**
* Class for exception thrown when something was already handled.
* Useful to use as a lock when handling a same something in different queues and only one should be attended
*
* @category Exception
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
defined('GNUSOCIAL') || die();
/**
* Exception thrown when something was already handled.
*
* @copyright 2019 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
class AlreadyHandledException extends ServerException
{
public function __construct($msg)
{
parent::__construct($msg, 202);
}
}

View File

@ -1,7 +1,7 @@
StartTFNLookup: tries to locate a duplicated remote profile by URI; federation plugins must trigger this event before profile insertion StartTFNLookup: tries to locate a duplicated remote profile by URI; federation plugins must trigger this event before profile insertion
@param string $uri URI of the remote profile to be inserted @param string $uri URI of the remote profile to be inserted
@param string $class profile class of the federation protocol that triggered the event @param string $class profile class of the federation protocol that triggered the event
@param int|null &$profile_id profile ID associated with the duplicated remote profile found @param int|null &$profile_id profile ID associated with the duplicated remote profile found, throws AlreadyHandledException to warn that the duplicate has priority
EndTFNLookup: deletes a previous duplicated remote profile found; federation plugins must trigget this event after successfully inserting a new profile EndTFNLookup: deletes a previous duplicated remote profile found; federation plugins must trigget this event after successfully inserting a new profile
@param string $class profile class of the federation protocol that triggered the event @param string $class profile class of the federation protocol that triggered the event

View File

@ -4,7 +4,7 @@ Making this possible essentially consists in not allowing duplication of remote
and to ensure that each profile is handled by one and only one federation protocol at a time. and to ensure that each profile is handled by one and only one federation protocol at a time.
Each newly added federation protocol **must** support all the already supported functionalities by the other federation Each newly added federation protocol **must** support all the already supported functionalities by the other federation
protocols, otherwise this module will be moving between FullFeaturedFederationProtocolProfile and LackyFederationProtocolProfile all the time. protocols, otherwise if the LackyFederationProtocolProfile is preferred, the remote actors using it will be limited.
You **must** feed this Module with the list of preferences for each federation protocol, more on that in the following section. You **must** feed this Module with the list of preferences for each federation protocol, more on that in the following section.

View File

@ -42,6 +42,7 @@ class TheFreeNetworkModule extends Module
const MODULE_VERSION = '0.1.0alpha0'; const MODULE_VERSION = '0.1.0alpha0';
public $protocols = null; // protocols TFN should handle public $protocols = null; // protocols TFN should handle
public $priority = []; // protocols preferences
private $lrdd = false; // whether LRDD plugin is active or not private $lrdd = false; // whether LRDD plugin is active or not
@ -56,9 +57,12 @@ class TheFreeNetworkModule extends Module
// require needed classes // require needed classes
$plugin_dir = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins'; $plugin_dir = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins';
$p = 0;
foreach ($this->protocols as $protocol => $class) { foreach ($this->protocols as $protocol => $class) {
$this->priority[$class] = $p++;
require_once $plugin_dir . DIRECTORY_SEPARATOR . $protocol . DIRECTORY_SEPARATOR . 'classes' . DIRECTORY_SEPARATOR . $class . '.php'; require_once $plugin_dir . DIRECTORY_SEPARATOR . $protocol . DIRECTORY_SEPARATOR . 'classes' . DIRECTORY_SEPARATOR . $class . '.php';
} }
unset($p);
// $lrdd flag // $lrdd flag
$this->lrdd = PluginList::isPluginActive("LRDD"); $this->lrdd = PluginList::isPluginActive("LRDD");
@ -74,25 +78,31 @@ class TheFreeNetworkModule extends Module
* @param string $class profile class that triggered this event * @param string $class profile class that triggered this event
* @param int|null &$profile_id Profile:id associated with the remote entity found * @param int|null &$profile_id Profile:id associated with the remote entity found
* @return bool hook flag * @return bool hook flag
* @throws AlreadyHandledException Do not allow to create a profile if a preferred protocol already has one
*/ */
public function onStartTFNLookup(string $uri, string $class, int &$profile_id = null): bool public function onStartTFNLookup(string $uri, string $class, int &$profile_id = null): bool
{ {
$profile_id = $this->lookup($uri, $class); [$profile_id, $cls] = $this->lookup($uri, $class);
if (is_null($profile_id)) { if (is_null($profile_id)) {
$perf = common_config('performance', 'high'); $perf = common_config('performance', 'high');
if (!$perf && $this->lrdd) { if (!$perf && $this->lrdd) {
// Force lookup with online resources // Force lookup with online resources
$profile_id = $this->lookup($uri, $class, true); [$profile_id, $cls] = $this->lookup($uri, $class, true);
} }
} }
// Lower means higher priority
if ($this->priority[$cls] < $this->priority[$class]) {
throw new AlreadyHandledException("TheFreeNetworkModule->AlreadyHandled: $cls is preferred over $class");
}
return false; return false;
} }
/** /**
* A new remote profile was sucessfully added, delete * A new remote profile was successfully added, delete
* other remotes associated with the same Profile entity. * other remotes associated with the same Profile entity.
* *
* @param string $class profile class that triggered this event * @param string $class profile class that triggered this event
@ -146,9 +156,9 @@ class TheFreeNetworkModule extends Module
* @param string $uri * @param string $uri
* @param string $class * @param string $class
* @param bool $online * @param bool $online
* @return int|null Profile:id associated with the remote entity found * @return null|array [Profile:id, class] associated with the remote entity found
*/ */
private function lookup(string $uri, string $class, bool $online = false): ?int private function lookup(string $uri, string $class, bool $online = false): ?array
{ {
if ($online) { if ($online) {
$this->log(LOG_INFO, 'Searching with online resources for a remote profile with URI: ' . $uri); $this->log(LOG_INFO, 'Searching with online resources for a remote profile with URI: ' . $uri);
@ -169,7 +179,7 @@ class TheFreeNetworkModule extends Module
$profile = $cls::getKV('uri', $alias); $profile = $cls::getKV('uri', $alias);
if ($profile instanceof $cls) { if ($profile instanceof $cls) {
$this->log(LOG_INFO, 'Found a remote ' . $cls . ' associated with Profile:' . $profile->getID()); $this->log(LOG_INFO, 'Found a remote ' . $cls . ' associated with Profile:' . $profile->getID());
return $profile->getID(); return [$profile->getID(), $cls];
} }
} }
} }

View File

@ -285,8 +285,6 @@ class Activitypub_profile extends Managed_DataObject
} }
$aprofile->created = $aprofile->modified = common_sql_now(); $aprofile->created = $aprofile->modified = common_sql_now();
$aprofile = new Activitypub_profile;
$aprofile->profile_id = $profile->getID(); $aprofile->profile_id = $profile->getID();
$aprofile->uri = $url; $aprofile->uri = $url;
$aprofile->nickname = $profile->getNickname(); $aprofile->nickname = $profile->getNickname();

View File

@ -49,7 +49,7 @@ class Activitypub_postman
* Create a postman to deliver something to someone * Create a postman to deliver something to someone
* *
* @param Profile $from sender Profile * @param Profile $from sender Profile
* @param array $to receiver Profiles * @param array $to receiver AProfiles
* @throws Exception * @throws Exception
* @author Diogo Cordeiro <diogo@fc.up.pt> * @author Diogo Cordeiro <diogo@fc.up.pt>
*/ */

View File

@ -287,7 +287,13 @@ class SalmonAction extends Action
if (!$this->oprofile instanceof Ostatus_profile) { if (!$this->oprofile instanceof Ostatus_profile) {
common_debug("We do not have a local profile to connect to this activity's author. Let's create one."); common_debug("We do not have a local profile to connect to this activity's author. Let's create one.");
// ensureActivityObjectProfile throws exception on failure // ensureActivityObjectProfile throws exception on failure
$this->oprofile = Ostatus_profile::ensureActivityObjectProfile($this->activity->actor); try {
$this->oprofile = Ostatus_profile::ensureActivityObjectProfile($this->activity->actor);
} catch (AlreadyHandledException $e) {
// Some other federation protocol is handling this profile, let it go.
common_debug('SalmonAction found that this actor already was handled by another federation protocol: ' . $e->getMessage());
return;
}
} }
} }