diff options
Diffstat (limited to 'lib/private/Accounts/AccountManager.php')
-rw-r--r-- | lib/private/Accounts/AccountManager.php | 58 |
1 files changed, 58 insertions, 0 deletions
diff --git a/lib/private/Accounts/AccountManager.php b/lib/private/Accounts/AccountManager.php index 9c7c35d4a6b..d00b1d2e9a3 100644 --- a/lib/private/Accounts/AccountManager.php +++ b/lib/private/Accounts/AccountManager.php @@ -78,6 +78,7 @@ class AccountManager implements IAccountManager { self::PROPERTY_PRONOUNS => self::SCOPE_FEDERATED, self::PROPERTY_ROLE => self::SCOPE_LOCAL, self::PROPERTY_TWITTER => self::SCOPE_LOCAL, + self::PROPERTY_BLUESKY => self::SCOPE_LOCAL, self::PROPERTY_WEBSITE => self::SCOPE_LOCAL, ]; @@ -564,6 +565,13 @@ class AccountManager implements IAccountManager { ], [ + 'name' => self::PROPERTY_BLUESKY, + 'value' => '', + 'scope' => $scopes[self::PROPERTY_BLUESKY], + 'verified' => self::NOT_VERIFIED, + ], + + [ 'name' => self::PROPERTY_FEDIVERSE, 'value' => '', 'scope' => $scopes[self::PROPERTY_FEDIVERSE], @@ -713,6 +721,47 @@ class AccountManager implements IAccountManager { } } + private function validateBlueSkyHandle(string $text): bool { + if ($text === '') { + return true; + } + + $lowerText = strtolower($text); + + if ($lowerText === 'bsky.social') { + // "bsky.social" itself is not a valid handle + return false; + } + + if (str_ends_with($lowerText, '.bsky.social')) { + $parts = explode('.', $lowerText); + + // Must be exactly: username.bsky.social → 3 parts + if (count($parts) !== 3 || $parts[1] !== 'bsky' || $parts[2] !== 'social') { + return false; + } + + $username = $parts[0]; + + // Must be 3–18 chars, alphanumeric/hyphen, no start/end hyphen + return preg_match('/^[a-z0-9][a-z0-9-]{2,17}$/', $username) === 1; + } + + // Allow custom domains (Bluesky handle via personal domain) + return filter_var($text, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) !== false; + } + + + private function sanitizePropertyBluesky(IAccountProperty $property): void { + if ($property->getName() === self::PROPERTY_BLUESKY) { + if (!$this->validateBlueSkyHandle($property->getValue())) { + throw new InvalidArgumentException(self::PROPERTY_BLUESKY); + } + + $property->setValue($property->getValue()); + } + } + /** * @throws InvalidArgumentException If the property value is not a valid fediverse handle (username@instance where instance is a valid domain) */ @@ -805,6 +854,15 @@ class AccountManager implements IAccountManager { } try { + $property = $account->getProperty(self::PROPERTY_BLUESKY); + if ($property->getValue() !== '') { + $this->sanitizePropertyBluesky($property); + } + } catch (PropertyDoesNotExistException $e) { + // valid case, nothing to do + } + + try { $property = $account->getProperty(self::PROPERTY_FEDIVERSE); if ($property->getValue() !== '') { $this->sanitizePropertyFediverse($property); |