diff --git a/config/services.yaml b/config/services.yaml index 951daafacb..4786f9aa99 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -53,4 +53,5 @@ services: Component\: resource: '../components/*/*.php' + exclude: '../components/*/{scripts,classes,lib,actions,locale,doc}' tags: ['controller.service_arguments'] diff --git a/plugins/ActivityPub/ActivityPub.php b/plugins/ActivityPub/ActivityPub.php index a962e88969..94a35fc869 100644 --- a/plugins/ActivityPub/ActivityPub.php +++ b/plugins/ActivityPub/ActivityPub.php @@ -58,7 +58,8 @@ class ActivityPub extends Module { const PLUGIN_VERSION = '0.4.0alpha0'; // So that this isn't hardcoded everywhere - const ACTIVITYPUB_PUBLIC_TO = ['https://www.w3.org/ns/activitystreams#Public', + const ACTIVITYPUB_PUBLIC_TO = [ + 'https://www.w3.org/ns/activitystreams#Public', 'Public', 'as:Public', ]; diff --git a/plugins/ActivityPub/classes/Activitypub_profile.php b/plugins/ActivityPub/Entity/ActivityPubActor.php similarity index 78% rename from plugins/ActivityPub/classes/Activitypub_profile.php rename to plugins/ActivityPub/Entity/ActivityPubActor.php index ebaee0acf8..a2a72e0b72 100644 --- a/plugins/ActivityPub/classes/Activitypub_profile.php +++ b/plugins/ActivityPub/Entity/ActivityPubActor.php @@ -1,4 +1,7 @@ . -/** - * ActivityPub implementation for GNU social - * - * @package GNUsocial - * @author Diogo Cordeiro - * @copyright 2018-2019 Free Software Foundation, Inc http://www.fsf.org - * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later - * @link http://www.gnu.org/software/social/ - */ - -defined('GNUSOCIAL') || die(); +// }}} /** - * ActivityPub Profile + * ActivityPub's Remote Actor * * @category Plugin * @package GNUsocial + * * @author Diogo Cordeiro + * @author Hugo Sales * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later */ -class Activitypub_profile extends Managed_DataObject -{ - public $__table = 'activitypub_profile'; - public $uri; // text() not_null - public $profile_id; // int(4) primary_key not_null - public $inboxuri; // text() not_null - public $sharedInboxuri; // text() - public $nickname; // varchar(64) multiple_key not_null - public $fullname; // text() - public $profileurl; // text() - public $homepage; // text() - public $bio; // text() multiple_key - public $location; // text() - public $created; // datetime() - public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP - /** - * Return table definition for Schema setup and DB_DataObject usage. - * - * @return array array of column definitions - * @author Diogo Cordeiro - */ - public static function schemaDef() - { - return [ - 'fields' => [ - 'uri' => ['type' => 'text', 'not null' => true], - 'profile_id' => ['type' => 'int', 'not null' => true], - 'inboxuri' => ['type' => 'text', 'not null' => true], - 'sharedInboxuri' => ['type' => 'text'], - 'created' => ['type' => 'datetime', 'description' => 'date this record was created'], - 'modified' => ['type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'], - ], - 'primary key' => ['profile_id'], - 'foreign keys' => [ - 'activitypub_profile_profile_id_fkey' => ['profile', ['profile_id' => 'id']], - ], - ]; - } +namespace Plugin\ActivityPub\Entity; + +class ActivityPubActor +{ + // {{{ Autocode + // }}} Autocode /** * Generates a pretty profile from a Profile object * * @param Profile $profile - * @return array array to be used in a response + * * @throws InvalidUrlException * @throws ServerException * @throws Exception + * + * @return array array to be used in a response + * * @author Diogo Cordeiro */ public static function profile_to_array(Profile $profile): array { - $uri = $profile->getUri(); - $id = $profile->getID(); - $rsa = new Activitypub_rsa(); + $uri = $profile->getUri(); + $id = $profile->getID(); + $rsa = new Activitypub_rsa(); $public_key = $rsa->ensure_public_key($profile); unset($rsa); $res = [ @@ -96,42 +62,42 @@ class Activitypub_profile extends Managed_DataObject 'https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1', [ - 'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers' - ] + 'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers', + ], ], - 'id' => $uri, - 'type' => 'Person', - 'following' => common_local_url('apActorFollowing', ['id' => $id]), - 'followers' => common_local_url('apActorFollowers', ['id' => $id]), - 'liked' => common_local_url('apActorLiked', ['id' => $id]), - 'inbox' => common_local_url('apInbox', ['id' => $id]), - 'outbox' => common_local_url('apActorOutbox', ['id' => $id]), - 'preferredUsername' => $profile->getNickname(), - 'name' => $profile->getBestName(), - 'summary' => ($desc = $profile->getDescription()) == null ? "" : $desc, - 'url' => $profile->getUrl(), + 'id' => $uri, + 'type' => 'Person', + 'following' => common_local_url('apActorFollowing', ['id' => $id]), + 'followers' => common_local_url('apActorFollowers', ['id' => $id]), + 'liked' => common_local_url('apActorLiked', ['id' => $id]), + 'inbox' => common_local_url('apInbox', ['id' => $id]), + 'outbox' => common_local_url('apActorOutbox', ['id' => $id]), + 'preferredUsername' => $profile->getNickname(), + 'name' => $profile->getBestName(), + 'summary' => ($desc = $profile->getDescription()) == null ? '' : $desc, + 'url' => $profile->getUrl(), 'manuallyApprovesFollowers' => false, - 'publicKey' => [ - 'id' => $uri . "#public-key", - 'owner' => $uri, - 'publicKeyPem' => $public_key + 'publicKey' => [ + 'id' => $uri . '#public-key', + 'owner' => $uri, + 'publicKeyPem' => $public_key, ], - 'tag' => [], + 'tag' => [], 'attachment' => [], - 'icon' => [ - 'type' => 'Image', + 'icon' => [ + 'type' => 'Image', 'mediaType' => 'image/png', - 'height' => AVATAR_PROFILE_SIZE, - 'width' => AVATAR_PROFILE_SIZE, - 'url' => $profile->avatarUrl(AVATAR_PROFILE_SIZE) - ] + 'height' => AVATAR_PROFILE_SIZE, + 'width' => AVATAR_PROFILE_SIZE, + 'url' => $profile->avatarUrl(AVATAR_PROFILE_SIZE), + ], ]; if ($profile->isLocal()) { $res['endpoints']['sharedInbox'] = common_local_url('apInbox'); } else { - $aprofile = new Activitypub_profile(); - $aprofile = $aprofile->from_profile($profile); + $aprofile = new Activitypub_profile(); + $aprofile = $aprofile->from_profile($profile); $res['endpoints']['sharedInbox'] = $aprofile->sharedInboxuri; } @@ -142,8 +108,8 @@ class Activitypub_profile extends Managed_DataObject * Insert the current object variables into the database * * @throws ServerException + * * @author Diogo Cordeiro - * @access public */ public function do_insert(): void { @@ -152,7 +118,7 @@ class Activitypub_profile extends Managed_DataObject if (!is_null($profile_id)) { // Yes! Avoid creating a new profile $this->profile_id = $profile_id; - $this->created = $this->modified = common_sql_now(); + $this->created = $this->modified = common_sql_now(); if ($this->insert() === false) { $this->query('ROLLBACK'); @@ -167,7 +133,7 @@ class Activitypub_profile extends Managed_DataObject Event::handle('EndTFNLookup', [get_class($this), $profile_id]); } else { // No, create both a new profile and remote profile - $profile = new Profile(); + $profile = new Profile(); $profile->created = $this->created = $this->modified = common_sql_now(); self::update_local_profile($profile, $this); @@ -190,8 +156,10 @@ class Activitypub_profile extends Managed_DataObject /** * Fetch the locally stored profile for this Activitypub_profile * - * @return Profile * @throws NoProfileException if it was not found + * + * @return Profile + * * @author Diogo Cordeiro */ public function local_profile(): Profile @@ -207,8 +175,11 @@ class Activitypub_profile extends Managed_DataObject * Generates an Activitypub_profile from a Profile * * @param Profile $profile - * @return Activitypub_profile + * * @throws Exception if no Activitypub_profile exists for given Profile + * + * @return Activitypub_profile + * * @author Diogo Cordeiro */ public static function from_profile(Profile $profile): Activitypub_profile @@ -231,11 +202,11 @@ class Activitypub_profile extends Managed_DataObject $fields = [ 'nickname' => 'nickname', 'fullname' => 'fullname', - 'bio' => 'bio' + 'bio' => 'bio', ]; foreach ($fields as $af => $pf) { - $aprofile->$af = $profile->$pf; + $aprofile->{$af} = $profile->{$pf}; } return $aprofile; @@ -245,6 +216,7 @@ class Activitypub_profile extends Managed_DataObject * Travels an array of Profile and returns an array of Activitypub_profile * * @param array of Profile $profiles + * * @return array of Activitypub_profile */ public static function from_profile_collection(array $profiles): array @@ -268,30 +240,35 @@ class Activitypub_profile extends Managed_DataObject * as only remote users have ActivityPub_profiles on local instance * * @param Profile $profile - * @return Activitypub_profile + * * @throws HTTP_Request2_Exception * @throws Exception * @throws Exception + * + * @return Activitypub_profile + * * @author Diogo Cordeiro */ private static function create_from_local_profile(Profile $profile): Activitypub_profile { $aprofile = new Activitypub_profile(); - $url = $profile->getUri(); + $url = $profile->getUri(); $inboxes = Activitypub_explorer::get_actor_inboxes_uri($url); if ($inboxes === false) { throw new Exception('This is not an ActivityPub user thus AProfile is politely refusing to proceed.'); } $aprofile->created = $aprofile->modified = common_sql_now(); - $aprofile->profile_id = $profile->getID(); - $aprofile->uri = $url; - $aprofile->nickname = $profile->getNickname(); - $aprofile->fullname = $profile->getFullname(); - $aprofile->bio = substr($profile->getDescription(), 0, 1000); - $aprofile->inboxuri = $inboxes["inbox"]; - $aprofile->sharedInboxuri = $inboxes["sharedInbox"]; + + $aprofile = new Activitypub_profile; + $aprofile->profile_id = $profile->getID(); + $aprofile->uri = $url; + $aprofile->nickname = $profile->getNickname(); + $aprofile->fullname = $profile->getFullname(); + $aprofile->bio = substr($profile->getDescription(), 0, 1000); + $aprofile->inboxuri = $inboxes['inbox']; + $aprofile->sharedInboxuri = $inboxes['sharedInbox']; $aprofile->insert(); @@ -302,6 +279,7 @@ class Activitypub_profile extends Managed_DataObject * Returns sharedInbox if possible, inbox otherwise * * @return string Inbox URL + * * @author Diogo Cordeiro */ public function get_inbox(): string @@ -317,6 +295,7 @@ class Activitypub_profile extends Managed_DataObject * Getter for uri property * * @return string URI + * * @author Diogo Cordeiro */ public function getUri(): string @@ -328,6 +307,7 @@ class Activitypub_profile extends Managed_DataObject * Getter for url property * * @return string URL + * * @author Diogo Cordeiro */ public function getUrl(): string @@ -339,6 +319,7 @@ class Activitypub_profile extends Managed_DataObject * Getter for id property * * @return int + * * @author Diogo Cordeiro */ public function getID(): int @@ -350,9 +331,12 @@ class Activitypub_profile extends Managed_DataObject * Ensures a valid Activitypub_profile when provided with a valid URI. * * @param string $url - * @param bool $grab_online whether to try online grabbing, defaults to true - * @return Activitypub_profile + * @param bool $grab_online whether to try online grabbing, defaults to true + * * @throws Exception if it isn't possible to return an Activitypub_profile + * + * @return Activitypub_profile + * * @author Diogo Cordeiro */ public static function fromUri(string $url, bool $grab_online = true): Activitypub_profile @@ -371,8 +355,11 @@ class Activitypub_profile extends Managed_DataObject * an exception will be thrown. * * @param string $addr WebFinger address - * @return Activitypub_profile + * * @throws Exception on error conditions + * + * @return Activitypub_profile + * * @author Diogo Cordeiro * @author GNU social */ @@ -419,10 +406,9 @@ class Activitypub_profile extends Managed_DataObject // If there's an Hcard, let's grab its info if (array_key_exists('hcard', $hints)) { - if (!array_key_exists('profileurl', $hints) || - $hints['hcard'] != $hints['profileurl']) { + if (!array_key_exists('profileurl', $hints) || $hints['hcard'] != $hints['profileurl']) { $hcardHints = DiscoveryHints::fromHcardUrl($hints['hcard']); - $hints = array_merge($hcardHints, $hints); + $hints = array_merge($hcardHints, $hints); } } @@ -431,12 +417,12 @@ class Activitypub_profile extends Managed_DataObject if (array_key_exists('profileurl', $hints)) { $profileUrl = $hints['profileurl']; try { - common_log(LOG_INFO, "Discovery on acct:$addr with profile URL $profileUrl"); + common_log(LOG_INFO, "Discovery on acct:{$addr} with profile URL {$profileUrl}"); $aprofile = self::fromUri($hints['profileurl']); self::cacheSet(sprintf('activitypub_profile:webfinger:%s', $addr), $aprofile->getUri()); return $aprofile; } catch (Exception $e) { - common_log(LOG_WARNING, "Failed creating profile from profile URL '$profileUrl': " . $e->getMessage()); + common_log(LOG_WARNING, "Failed creating profile from profile URL '{$profileUrl}': " . $e->getMessage()); // keep looking // // @todo FIXME: This means an error discovering from profile page @@ -455,9 +441,11 @@ class Activitypub_profile extends Managed_DataObject /** * Update local profile with info from some AP profile * - * @param Profile $profile + * @param Profile $profile * @param Activitypub_profile $aprofile + * * @return void + * * @author Bruno Casteleiro * @author Diogo Cordeiro */ @@ -465,15 +453,15 @@ class Activitypub_profile extends Managed_DataObject { $fields = [ 'profileurl' => 'profileurl', - 'nickname' => 'nickname', - 'fullname' => 'fullname', - 'bio' => 'bio' + 'nickname' => 'nickname', + 'fullname' => 'fullname', + 'bio' => 'bio', ]; - $orig = clone($profile); + $orig = clone $profile; foreach ($fields as $af => $pf) { - $profile->$pf = $aprofile->$af; + $profile->{$pf} = $aprofile->{$af}; } if ($profile->id) { @@ -487,18 +475,21 @@ class Activitypub_profile extends Managed_DataObject * Update remote user profile in local instance * * @param Activitypub_profile $aprofile - * @param array|false $res remote response, if array it updates, if false it deletes - * @return Profile remote Profile object + * @param array|false $res remote response, if array it updates, if false it deletes + * * @throws NoProfileException + * + * @return Profile remote Profile object + * * @author Diogo Cordeiro */ public static function update_profile(Activitypub_profile $aprofile, $res): Profile { if ($res === false) { $profile = $aprofile->local_profile(); - $id = $profile->getID(); + $id = $profile->getID(); $profile->delete(); - throw new NoProfileException($id, "410 Gone"); + throw new NoProfileException($id, '410 Gone'); } if (!is_array($res)) { @@ -506,14 +497,14 @@ class Activitypub_profile extends Managed_DataObject } // ActivityPub Profile - $aprofile->uri = $res['id']; - $aprofile->nickname = $res['preferredUsername']; - $aprofile->fullname = $res['name'] ?? null; - $aprofile->bio = isset($res['summary']) ? substr(strip_tags($res['summary']), 0, 1000) : null; - $aprofile->inboxuri = $res['inbox']; + $aprofile->uri = $res['id']; + $aprofile->nickname = $res['preferredUsername']; + $aprofile->fullname = $res['name'] ?? null; + $aprofile->bio = isset($res['summary']) ? substr(strip_tags($res['summary']), 0, 1000) : null; + $aprofile->inboxuri = $res['inbox']; $aprofile->sharedInboxuri = $res['endpoints']['sharedInbox'] ?? $res['inbox']; - $aprofile->profileurl = $res['url'] ?? $aprofile->uri; - $aprofile->modified = common_sql_now(); + $aprofile->profileurl = $res['url'] ?? $aprofile->uri; + $aprofile->modified = common_sql_now(); $profile = $aprofile->local_profile(); @@ -541,13 +532,16 @@ class Activitypub_profile extends Managed_DataObject * Update remote user profile URI in local instance * * @param string $uri - * @return void + * * @throws Exception (if the update fails) + * + * @return void + * * @author Bruno Casteleiro */ public function updateUri(string $uri): void { - $orig = clone($this); + $orig = clone $this; $this->uri = $uri; $this->updateWithKeys($orig); } @@ -557,7 +551,9 @@ class Activitypub_profile extends Managed_DataObject * given local profile * * @param Profile $profile profile object + * * @return int number of subscribers + * * @author Bruno Casteleiro */ public static function subscriberCount(Profile $profile): int @@ -568,8 +564,8 @@ class Activitypub_profile extends Managed_DataObject return $cnt; } - $user_table = common_database_tablename('user'); - $sub = new Subscription(); + $user_table = common_database_tablename('user'); + $sub = new Subscription(); $sub->subscribed = $profile->id; $sub->_join .= "\n" . << */ public static function subscriptionCount(Profile $profile): int @@ -602,8 +600,8 @@ class Activitypub_profile extends Managed_DataObject return $cnt; } - $user_table = common_database_tablename('user'); - $sub = new Subscription(); + $user_table = common_database_tablename('user'); + $sub = new Subscription(); $sub->subscriber = $profile->id; $sub->_join .= "\n" . << */ public static function updateSubscriberCount(Profile $profile, $adder): void @@ -641,6 +640,7 @@ class Activitypub_profile extends Managed_DataObject * * @param Profile $profile * @param $adder + * * @author Bruno Casteleiro */ public static function updateSubscriptionCount(Profile $profile, $adder): void @@ -656,12 +656,14 @@ class Activitypub_profile extends Managed_DataObject * Getter for the subscriber profiles of a * given local profile * - * @param Profile $profile profile object - * @param int $offset [optional] index of the starting row to fetch from - * @param int|null $limit [optional] maximum number of rows allowed for fetching. If it is omitted, - * then the sequence will have everything - * from offset up until the end. + * @param Profile $profile profile object + * @param int $offset [optional] index of the starting row to fetch from + * @param null|int $limit [optional] maximum number of rows allowed for fetching. If it is omitted, + * then the sequence will have everything + * from offset up until the end. + * * @return array subscriber profile objects + * * @author Bruno Casteleiro */ public static function getSubscribers(Profile $profile, int $offset = 0, ?int $limit = null): array @@ -676,7 +678,7 @@ class Activitypub_profile extends Managed_DataObject $cache = true; } - $subs = Subscription::getSubscriberIDs($profile->id, $offset, $limit); + $subs = Subscription::getSubscriberIDs($profile->id, $offset, $limit); $profiles = []; $users = User::multiGet('id', $subs); @@ -700,10 +702,12 @@ class Activitypub_profile extends Managed_DataObject * Getter for the subscribed profiles of a * given local profile * - * @param Profile $profile profile object - * @param int $offset index of the starting row to fetch from - * @param int|null $limit maximum number of rows allowed for fetching + * @param Profile $profile profile object + * @param int $offset index of the starting row to fetch from + * @param null|int $limit maximum number of rows allowed for fetching + * * @return array subscribed profile objects + * * @author Bruno Casteleiro */ public static function getSubscribed(Profile $profile, int $offset = 0, ?int $limit = null): array @@ -745,8 +749,11 @@ class Activitypub_profile extends Managed_DataObject * * @param Profile $actor subscriber profile object * @param Profile $other subscribed profile object - * @return void + * * @throws Exception + * + * @return void + * * @author Bruno Casteleiro */ public static function subscribeCacheUpdate(Profile $actor, Profile $other): void @@ -763,8 +770,11 @@ class Activitypub_profile extends Managed_DataObject * * @param Profile $actor subscriber profile object * @param Profile $other subscribed profile object - * @return void + * * @throws Exception + * + * @return void + * * @author Bruno Casteleiro */ public static function unsubscribeCacheUpdate(Profile $actor, Profile $other): void @@ -774,4 +784,24 @@ class Activitypub_profile extends Managed_DataObject self::updateSubscriptionCount($actor, -1); self::updateSubscriberCount($other, -1); } + + public static function schemaDef() + { + return [ + 'name' => 'activitypub_actor', + 'description' => 'remote actor profiles', + 'fields' => [ + 'uri' => ['type' => 'text', 'not null' => true], + 'profile_id' => ['type' => 'int', 'not null' => true], + 'inboxuri' => ['type' => 'text', 'not null' => true], + 'sharedInboxuri' => ['type' => 'text'], + 'created' => ['type' => 'datetime', 'description' => 'date this record was created'], + 'modified' => ['type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'], + ], + 'primary key' => ['profile_id'], + 'foreign keys' => [ + 'activitypub_profile_profile_id_fkey' => ['profile', ['profile_id' => 'id']], + ], + ]; + } } diff --git a/plugins/ActivityPub/classes/Activitypub_rsa.php b/plugins/ActivityPub/Entity/ActivityPubCryptKey.php similarity index 69% rename from plugins/ActivityPub/classes/Activitypub_rsa.php rename to plugins/ActivityPub/Entity/ActivityPubCryptKey.php index b25b9409f8..7501baac44 100644 --- a/plugins/ActivityPub/classes/Activitypub_rsa.php +++ b/plugins/ActivityPub/Entity/ActivityPubCryptKey.php @@ -1,4 +1,7 @@ . +// }}} + /** - * ActivityPub implementation for GNU social + * ActivityPub Assymetric Key Storage System * * @package GNUsocial + * * @author Diogo Cordeiro + * @author Hugo Sales * @copyright 2018-2019 Free Software Foundation, Inc http://www.fsf.org * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later - * @link http://www.gnu.org/software/social/ */ -defined('GNUSOCIAL') || die(); +namespace Plugin\ActivityPub\Entity; -/** - * ActivityPub Keys System - * - * @category Plugin - * @package GNUsocial - * @author Diogo Cordeiro - * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later - */ -class Activitypub_rsa extends Managed_DataObject +class ActivityPubCryptKey { - public $__table = 'activitypub_rsa'; - public $profile_id; // int(4) primary_key not_null - public $private_key; // text() not_null - public $public_key; // text() not_null - public $created; // datetime() - public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP - - /** - * Return table definition for Schema setup and DB_DataObject usage. - * - * @return array array of column definitions - * @author Diogo Cordeiro - */ - public static function schemaDef() - { - return [ - 'fields' => [ - 'profile_id' => ['type' => 'int', 'not null' => true], - 'private_key' => ['type' => 'text'], - 'public_key' => ['type' => 'text', 'not null' => true], - 'created' => ['type' => 'datetime', 'description' => 'date this record was created'], - 'modified' => ['type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'], - ], - 'primary key' => ['profile_id'], - 'foreign keys' => [ - 'activitypub_rsa_profile_id_fkey' => ['profile', ['profile_id' => 'id']], - ], - ]; - } + // {{{ Autocode + // }}} Autocode /** * Private key getter * * @param Profile $profile - * @return string The private key + * * @throws Exception Throws exception if tries to fetch a private key of an actor we don't own + * + * @return string The private key */ public function get_private_key(Profile $profile): string { $this->profile_id = $profile->getID(); - $apRSA = self::getKV('profile_id', $this->profile_id); + $apRSA = self::getKV('profile_id', $this->profile_id); if (!$apRSA instanceof Activitypub_rsa) { // Nonexistent key pair for this profile if ($profile->isLocal()) { @@ -94,16 +67,19 @@ class Activitypub_rsa extends Managed_DataObject * Guarantees a Public Key for a given profile. * * @param Profile $profile - * @param bool $fetch=true Should attempt to fetch keys from a remote profile? - * @return string The public key + * @param bool $fetch=true Should attempt to fetch keys from a remote profile? + * * @throws ServerException It should never occur, but if so, we break everything! * @throws Exception + * + * @return string The public key + * * @author Diogo Cordeiro */ public function ensure_public_key(Profile $profile, bool $fetch = true): string { $this->profile_id = $profile->getID(); - $apRSA = self::getKV('profile_id', $this->profile_id); + $apRSA = self::getKV('profile_id', $this->profile_id); if (!$apRSA instanceof Activitypub_rsa) { // No existing key pair for this profile if ($profile->isLocal()) { @@ -112,7 +88,7 @@ class Activitypub_rsa extends Managed_DataObject $apRSA->public_key = $this->public_key; } else { // ASSERT: This should never happen, but try to recover! - common_log(LOG_ERR, "Activitypub_rsa: An impossible thing has happened... Please let the devs know that it entered in line 116 at Activitypub_rsa.php"); + common_log(LOG_ERR, 'Activitypub_rsa: An impossible thing has happened... Please let the devs know that it entered in line 116 at Activitypub_rsa.php'); if ($fetch) { $res = Activitypub_explorer::get_remote_user_activity($profile->getUri()); Activitypub_rsa::update_public_key($profile, $res['publicKey']['publicKeyPem']); @@ -129,13 +105,13 @@ class Activitypub_rsa extends Managed_DataObject * Insert the current object variables into the database. * * @throws ServerException + * * @author Diogo Cordeiro - * @access public */ public function store_keys(): void { $this->created = $this->modified = common_sql_now(); - $ok = $this->insert(); + $ok = $this->insert(); if ($ok === false) { throw new ServerException('Cannot save ActivityPub RSA.'); } @@ -145,13 +121,14 @@ class Activitypub_rsa extends Managed_DataObject * Generates a pair of RSA keys. * * @param string $private_key out - * @param string $public_key out + * @param string $public_key out + * * @author PHP Manual Contributed Notes */ public static function generate_keys(?string &$private_key, ?string &$public_key): void { $config = [ - 'digest_alg' => 'sha512', + 'digest_alg' => 'sha512', 'private_key_bits' => 2048, 'private_key_type' => OPENSSL_KEYTYPE_RSA, ]; @@ -163,28 +140,49 @@ class Activitypub_rsa extends Managed_DataObject openssl_pkey_export($res, $private_key); // Extract the public key from $res to $pubKey - $pubKey = openssl_pkey_get_details($res); - $public_key = $pubKey["key"]; + $pubKey = openssl_pkey_get_details($res); + $public_key = $pubKey['key']; unset($pubKey); } /** * Update public key. * - * @param Profile|Activitypub_profile $profile - * @param string $public_key + * @param Activitypub_profile|Profile $profile + * @param string $public_key + * * @throws Exception + * * @author Diogo Cordeiro */ public static function update_public_key($profile, string $public_key): void { // Public Key - $apRSA = new Activitypub_rsa(); + $apRSA = new Activitypub_rsa(); $apRSA->profile_id = $profile->getID(); $apRSA->public_key = $public_key; - $apRSA->created = common_sql_now(); + $apRSA->created = common_sql_now(); if (!$apRSA->update()) { $apRSA->insert(); } } + + public static function schemaDef() + { + return [ + 'name' => 'activitypub_crypt_key', + 'description' => 'assymetric key storage for activitypub', + 'fields' => [ + 'gsactor_id' => ['type' => 'int', 'not null' => true], + 'private_key' => ['type' => 'text'], + 'public_key' => ['type' => 'text', 'not null' => true], + 'created' => ['type' => 'datetime', 'description' => 'date this record was created'], + 'modified' => ['type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'], + ], + 'primary key' => ['gsactor_id'], + 'foreign keys' => [ + 'activitypub_rsa_gsactor_id_fkey' => ['gsactor', ['gsactor_id' => 'id']], + ], + ]; + } } diff --git a/plugins/ActivityPub/Entity/ActivityPubFollowRequests.php b/plugins/ActivityPub/Entity/ActivityPubFollowRequests.php new file mode 100644 index 0000000000..66dac19605 --- /dev/null +++ b/plugins/ActivityPub/Entity/ActivityPubFollowRequests.php @@ -0,0 +1,60 @@ +. + +// }}} + +/** + * ActivityPub's Pending follow requests + * + * @category Plugin + * @package GNUsocial + * + * @author Diogo Cordeiro + * @author Hugo Sales + * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later + */ + +namespace Plugin\ActivityPub\Entity; + +class ActivityPubFollowRequests +{ + // {{{ Autocode + // }}} Autocode + + public static function schemaDef() + { + return [ + 'name' => 'activitypub_pending_follow_requests', + 'fields' => [ + 'local_gsactor_id' => ['type' => 'int', 'not null' => true], + 'remote_gsactor_id' => ['type' => 'int', 'not null' => true], + 'relation_id' => ['type' => 'serial', 'not null' => true], + ], + 'primary key' => ['relation_id'], + 'foreign keys' => [ + 'activitypub_pending_follow_requests_local_gsactor_id_fkey' => ['gsactor', ['local_gsactor_id' => 'id']], + 'activitypub_pending_follow_requests_remote_gsactor_id_fkey' => ['gsactor', ['remote_gsactor_id' => 'id']], + ], + 'indexes' => [ + 'activitypub_pending_follow_requests_local_gsactor_id_idx' => ['local_gsactor_id'], + 'activitypub_pending_follow_requests_remote_gsactor_id_idx' => ['remote_gsactor_id'], + ], + ]; + } +} diff --git a/plugins/ActivityPub/classes/Activitypub_pending_follow_requests.php b/plugins/ActivityPub/classes/Activitypub_pending_follow_requests.php deleted file mode 100644 index e168149534..0000000000 --- a/plugins/ActivityPub/classes/Activitypub_pending_follow_requests.php +++ /dev/null @@ -1,113 +0,0 @@ -. - -/** - * ActivityPub implementation for GNU social - * - * @package GNUsocial - * @author Diogo Cordeiro - * @copyright 2018-2019 Free Software Foundation, Inc http://www.fsf.org - * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later - * @link http://www.gnu.org/software/social/ - */ - -defined('GNUSOCIAL') || die(); - -/** - * ActivityPub's Pending follow requests - * - * @category Plugin - * @package GNUsocial - * @author Diogo Cordeiro - * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later - */ -class Activitypub_pending_follow_requests extends Managed_DataObject -{ - public $__table = 'activitypub_pending_follow_requests'; - public $local_profile_id; - public $remote_profile_id; - private $_reldb = null; - - /** - * Return table definition for Schema setup and DB_DataObject usage. - * - * @author Diogo Cordeiro - * @return array array of column definitions - */ - public static function schemaDef() - { - return [ - 'fields' => [ - 'local_profile_id' => ['type' => 'int', 'not null' => true], - 'remote_profile_id' => ['type' => 'int', 'not null' => true], - 'relation_id' => ['type' => 'serial', 'not null' => true], - ], - 'primary key' => ['relation_id'], - 'foreign keys' => [ - 'activitypub_pending_follow_requests_local_profile_id_fkey' => ['profile', ['local_profile_id' => 'id']], - 'activitypub_pending_follow_requests_remote_profile_id_fkey' => ['profile', ['remote_profile_id' => 'id']], - ], - 'indexes' => [ - 'activitypub_pending_follow_requests_local_profile_id_idx' => ['local_profile_id'], - 'activitypub_pending_follow_requests_remote_profile_id_idx' => ['remote_profile_id'], - ], - ]; - } - - public function __construct($actor, $remote_actor) - { - $this->local_profile_id = $actor; - $this->remote_profile_id = $remote_actor; - } - - /** - * Add Follow request to table. - * - * @return boolean true if added, false otherwise - * @author Diogo Cordeiro - */ - public function add() - { - return !$this->exists() && $this->insert(); - } - - /** - * Check if a Follow request is pending. - * - * @author Diogo Cordeiro - * @return boolean true if is pending, false otherwise - */ - public function exists() - { - $this->_reldb = clone($this); - if ($this->_reldb->find() > 0) { - $this->_reldb->fetch(); - return true; - } - return false; - } - - /** - * Remove a request from the pending table. - * - * @author Diogo Cordeiro - * @return boolean true if removed, false otherwise - */ - public function remove() - { - return $this->exists() && $this->_reldb->delete(); - } -} diff --git a/src/DependencyInjection/Compiler/ModuleManagerPass.php b/src/DependencyInjection/Compiler/ModuleManagerPass.php index c27998276e..5ca12799d9 100644 --- a/src/DependencyInjection/Compiler/ModuleManagerPass.php +++ b/src/DependencyInjection/Compiler/ModuleManagerPass.php @@ -36,11 +36,17 @@ namespace App\DependencyInjection\Compiler; use App\Core\ModuleManager; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; class ModuleManagerPass implements CompilerPassInterface { public function process(ContainerBuilder $container) { ModuleManager::process(); + $container->findDefinition('doctrine.orm.default_metadata_driver') + ->addMethodCall('addDriver', + [new Reference('app.core.schemadef_driver'), 'Plugin\\Entity']) + ->addMethodCall('addDriver', + [new Reference('app.core.schemadef_driver'), 'Component\\Entity']); } }