aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private
diff options
context:
space:
mode:
Diffstat (limited to 'lib/private')
-rw-r--r--lib/private/Accounts/AccountManager.php56
-rw-r--r--lib/private/Accounts/AccountProperty.php22
-rw-r--r--lib/private/Avatar/AvatarManager.php44
-rw-r--r--lib/private/Avatar/PlaceholderAvatar.php183
-rw-r--r--lib/private/Avatar/UserAvatar.php1
-rw-r--r--lib/private/KnownUser/KnownUserService.php4
-rw-r--r--lib/private/Server.php6
7 files changed, 303 insertions, 13 deletions
diff --git a/lib/private/Accounts/AccountManager.php b/lib/private/Accounts/AccountManager.php
index c5a0f21319e..6198f8dbddd 100644
--- a/lib/private/Accounts/AccountManager.php
+++ b/lib/private/Accounts/AccountManager.php
@@ -144,6 +144,42 @@ class AccountManager implements IAccountManager {
}
}
+ $allowedScopes = [
+ self::SCOPE_PRIVATE,
+ self::SCOPE_LOCAL,
+ self::SCOPE_FEDERATED,
+ self::SCOPE_PUBLISHED,
+ self::VISIBILITY_PRIVATE,
+ self::VISIBILITY_CONTACTS_ONLY,
+ self::VISIBILITY_PUBLIC,
+ ];
+
+ // validate and convert scope values
+ foreach ($data as $propertyName => $propertyData) {
+ if (isset($propertyData['scope'])) {
+ if ($throwOnData && !in_array($propertyData['scope'], $allowedScopes, true)) {
+ throw new \InvalidArgumentException('scope');
+ }
+
+ if (
+ $propertyData['scope'] === self::SCOPE_PRIVATE
+ && ($propertyName === self::PROPERTY_DISPLAYNAME || $propertyName === self::PROPERTY_EMAIL)
+ ) {
+ if ($throwOnData) {
+ // v2-private is not available for these fields
+ throw new \InvalidArgumentException('scope');
+ } else {
+ // default to local
+ $data[$propertyName]['scope'] = self::SCOPE_LOCAL;
+ }
+ } else {
+ // migrate scope values to the new format
+ // invalid scopes are mapped to a default value
+ $data[$propertyName]['scope'] = AccountProperty::mapScopeToV2($propertyData['scope']);
+ }
+ }
+ }
+
if (empty($userData)) {
$this->insertNewUser($user, $data);
} elseif ($userData !== $data) {
@@ -198,6 +234,8 @@ class AccountManager implements IAccountManager {
*
* @param IUser $user
* @return array
+ *
+ * @deprecated use getAccount instead to make sure migrated properties work correctly
*/
public function getUser(IUser $user) {
$uid = $user->getUID();
@@ -405,7 +443,7 @@ class AccountManager implements IAccountManager {
}
$query->setParameter('name', $propertyName)
- ->setParameter('value', $property['value']);
+ ->setParameter('value', $property['value'] ?? '');
$query->execute();
}
}
@@ -421,41 +459,41 @@ class AccountManager implements IAccountManager {
self::PROPERTY_DISPLAYNAME =>
[
'value' => $user->getDisplayName(),
- 'scope' => self::VISIBILITY_CONTACTS_ONLY,
+ 'scope' => self::SCOPE_FEDERATED,
'verified' => self::NOT_VERIFIED,
],
self::PROPERTY_ADDRESS =>
[
'value' => '',
- 'scope' => self::VISIBILITY_PRIVATE,
+ 'scope' => self::SCOPE_LOCAL,
'verified' => self::NOT_VERIFIED,
],
self::PROPERTY_WEBSITE =>
[
'value' => '',
- 'scope' => self::VISIBILITY_PRIVATE,
+ 'scope' => self::SCOPE_LOCAL,
'verified' => self::NOT_VERIFIED,
],
self::PROPERTY_EMAIL =>
[
'value' => $user->getEMailAddress(),
- 'scope' => self::VISIBILITY_CONTACTS_ONLY,
+ 'scope' => self::SCOPE_FEDERATED,
'verified' => self::NOT_VERIFIED,
],
self::PROPERTY_AVATAR =>
[
- 'scope' => self::VISIBILITY_CONTACTS_ONLY
+ 'scope' => self::SCOPE_FEDERATED
],
self::PROPERTY_PHONE =>
[
'value' => '',
- 'scope' => self::VISIBILITY_PRIVATE,
+ 'scope' => self::SCOPE_LOCAL,
'verified' => self::NOT_VERIFIED,
],
self::PROPERTY_TWITTER =>
[
'value' => '',
- 'scope' => self::VISIBILITY_PRIVATE,
+ 'scope' => self::SCOPE_LOCAL,
'verified' => self::NOT_VERIFIED,
],
];
@@ -464,7 +502,7 @@ class AccountManager implements IAccountManager {
private function parseAccountData(IUser $user, $data): Account {
$account = new Account($user);
foreach ($data as $property => $accountData) {
- $account->setProperty($property, $accountData['value'] ?? '', $accountData['scope'] ?? self::VISIBILITY_PRIVATE, $accountData['verified'] ?? self::NOT_VERIFIED);
+ $account->setProperty($property, $accountData['value'] ?? '', $accountData['scope'] ?? self::SCOPE_LOCAL, $accountData['verified'] ?? self::NOT_VERIFIED);
}
return $account;
}
diff --git a/lib/private/Accounts/AccountProperty.php b/lib/private/Accounts/AccountProperty.php
index 97f9b1c356f..850f39df9e3 100644
--- a/lib/private/Accounts/AccountProperty.php
+++ b/lib/private/Accounts/AccountProperty.php
@@ -26,6 +26,7 @@ declare(strict_types=1);
namespace OC\Accounts;
+use OCP\Accounts\IAccountManager;
use OCP\Accounts\IAccountProperty;
class AccountProperty implements IAccountProperty {
@@ -42,7 +43,7 @@ class AccountProperty implements IAccountProperty {
public function __construct(string $name, string $value, string $scope, string $verified) {
$this->name = $name;
$this->value = $value;
- $this->scope = $scope;
+ $this->scope = $this->mapScopeToV2($scope);
$this->verified = $verified;
}
@@ -77,7 +78,7 @@ class AccountProperty implements IAccountProperty {
* @return IAccountProperty
*/
public function setScope(string $scope): IAccountProperty {
- $this->scope = $scope;
+ $this->scope = $this->mapScopeToV2($scope);
return $this;
}
@@ -127,6 +128,23 @@ class AccountProperty implements IAccountProperty {
return $this->scope;
}
+ public static function mapScopeToV2($scope) {
+ if (strpos($scope, 'v2-') === 0) {
+ return $scope;
+ }
+
+ switch ($scope) {
+ case IAccountManager::VISIBILITY_PRIVATE:
+ return IAccountManager::SCOPE_LOCAL;
+ case IAccountManager::VISIBILITY_CONTACTS_ONLY:
+ return IAccountManager::SCOPE_FEDERATED;
+ case IAccountManager::VISIBILITY_PUBLIC:
+ return IAccountManager::SCOPE_PUBLISHED;
+ }
+
+ return IAccountManager::SCOPE_LOCAL;
+ }
+
/**
* Get the verification status of a property
*
diff --git a/lib/private/Avatar/AvatarManager.php b/lib/private/Avatar/AvatarManager.php
index 5102396224d..04d3a721022 100644
--- a/lib/private/Avatar/AvatarManager.php
+++ b/lib/private/Avatar/AvatarManager.php
@@ -34,8 +34,10 @@ declare(strict_types=1);
namespace OC\Avatar;
+use OC\KnownUser\KnownUserService;
use OC\User\Manager;
use OC\User\NoUserException;
+use OCP\Accounts\IAccountManager;
use OCP\Files\IAppData;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
@@ -44,12 +46,16 @@ use OCP\IAvatarManager;
use OCP\IConfig;
use OCP\IL10N;
use OCP\ILogger;
+use OCP\IUserSession;
/**
* This class implements methods to access Avatar functionality
*/
class AvatarManager implements IAvatarManager {
+ /** @var IUserSession */
+ private $userSession;
+
/** @var Manager */
private $userManager;
@@ -65,6 +71,12 @@ class AvatarManager implements IAvatarManager {
/** @var IConfig */
private $config;
+ /** @var IAccountManager */
+ private $accountManager;
+
+ /** @var KnownUserService */
+ private $knownUserService;
+
/**
* AvatarManager constructor.
*
@@ -73,18 +85,26 @@ class AvatarManager implements IAvatarManager {
* @param IL10N $l
* @param ILogger $logger
* @param IConfig $config
+ * @param IUserSession $userSession
*/
public function __construct(
+ IUserSession $userSession,
Manager $userManager,
IAppData $appData,
IL10N $l,
ILogger $logger,
- IConfig $config) {
+ IConfig $config,
+ IAccountManager $accountManager,
+ KnownUserService $knownUserService
+ ) {
+ $this->userSession = $userSession;
$this->userManager = $userManager;
$this->appData = $appData;
$this->l = $l;
$this->logger = $logger;
$this->config = $config;
+ $this->accountManager = $accountManager;
+ $this->knownUserService = $knownUserService;
}
/**
@@ -104,12 +124,34 @@ class AvatarManager implements IAvatarManager {
// sanitize userID - fixes casing issue (needed for the filesystem stuff that is done below)
$userId = $user->getUID();
+ $requestingUser = null;
+ if ($this->userSession !== null) {
+ $requestingUser = $this->userSession->getUser();
+ }
+
try {
$folder = $this->appData->getFolder($userId);
} catch (NotFoundException $e) {
$folder = $this->appData->newFolder($userId);
}
+ $account = $this->accountManager->getAccount($user);
+ $avatarProperties = $account->getProperty(IAccountManager::PROPERTY_AVATAR);
+ $avatarScope = $avatarProperties->getScope();
+
+ if (
+ // v2-private scope hides the avatar from public access and from unknown users
+ $avatarScope === IAccountManager::SCOPE_PRIVATE
+ && (
+ // accessing from public link
+ $requestingUser === null
+ // logged in, but unknown to user
+ || !$this->knownUserService->isKnownToUser($requestingUser->getUID(), $userId)
+ )) {
+ // use a placeholder avatar which caches the generated images
+ return new PlaceholderAvatar($folder, $user, $this->logger);
+ }
+
return new UserAvatar($folder, $this->l, $user, $this->logger, $this->config);
}
diff --git a/lib/private/Avatar/PlaceholderAvatar.php b/lib/private/Avatar/PlaceholderAvatar.php
new file mode 100644
index 00000000000..5883fe531a3
--- /dev/null
+++ b/lib/private/Avatar/PlaceholderAvatar.php
@@ -0,0 +1,183 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright Copyright (c) 2018, Michael Weimann <mail@michael-weimann.eu>
+ *
+ * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
+ * @author Christoph Wurst <christoph@winzerhof-wurst.at>
+ * @author Joas Schilling <coding@schilljs.com>
+ * @author Michael Weimann <mail@michael-weimann.eu>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\Avatar;
+
+use OC\NotSquareException;
+use OC\User\User;
+use OCP\Files\NotFoundException;
+use OCP\Files\NotPermittedException;
+use OCP\Files\SimpleFS\ISimpleFile;
+use OCP\Files\SimpleFS\ISimpleFolder;
+use OCP\IConfig;
+use OCP\IImage;
+use OCP\IL10N;
+use OCP\ILogger;
+
+/**
+ * This class represents a registered user's placeholder avatar.
+ *
+ * It generates an image based on the user's initials and caches it on storage
+ * for faster retrieval, unlike the GuestAvatar.
+ */
+class PlaceholderAvatar extends Avatar {
+ /** @var ISimpleFolder */
+ private $folder;
+
+ /** @var User */
+ private $user;
+
+ /**
+ * UserAvatar constructor.
+ *
+ * @param IConfig $config The configuration
+ * @param ISimpleFolder $folder The avatar files folder
+ * @param IL10N $l The localization helper
+ * @param User $user The user this class manages the avatar for
+ * @param ILogger $logger The logger
+ */
+ public function __construct(
+ ISimpleFolder $folder,
+ $user,
+ ILogger $logger) {
+ parent::__construct($logger);
+
+ $this->folder = $folder;
+ $this->user = $user;
+ }
+
+ /**
+ * Check if an avatar exists for the user
+ *
+ * @return bool
+ */
+ public function exists() {
+ return true;
+ }
+
+ /**
+ * Sets the users avatar.
+ *
+ * @param IImage|resource|string $data An image object, imagedata or path to set a new avatar
+ * @throws \Exception if the provided file is not a jpg or png image
+ * @throws \Exception if the provided image is not valid
+ * @throws NotSquareException if the image is not square
+ * @return void
+ */
+ public function set($data) {
+ // unimplemented for placeholder avatars
+ }
+
+ /**
+ * Removes the users avatar.
+ */
+ public function remove(bool $silent = false) {
+ $avatars = $this->folder->getDirectoryListing();
+
+ foreach ($avatars as $avatar) {
+ $avatar->delete();
+ }
+ }
+
+ /**
+ * Returns the avatar for an user.
+ *
+ * If there is no avatar file yet, one is generated.
+ *
+ * @param int $size
+ * @return ISimpleFile
+ * @throws NotFoundException
+ * @throws \OCP\Files\NotPermittedException
+ * @throws \OCP\PreConditionNotMetException
+ */
+ public function getFile($size) {
+ $size = (int) $size;
+
+ $ext = 'png';
+
+ if ($size === -1) {
+ $path = 'avatar-placeholder.' . $ext;
+ } else {
+ $path = 'avatar-placeholder.' . $size . '.' . $ext;
+ }
+
+ try {
+ $file = $this->folder->getFile($path);
+ } catch (NotFoundException $e) {
+ if ($size <= 0) {
+ throw new NotFoundException;
+ }
+
+ if (!$data = $this->generateAvatarFromSvg($size)) {
+ $data = $this->generateAvatar($this->getDisplayName(), $size);
+ }
+
+ try {
+ $file = $this->folder->newFile($path);
+ $file->putContent($data);
+ } catch (NotPermittedException $e) {
+ $this->logger->error('Failed to save avatar placeholder for ' . $this->user->getUID());
+ throw new NotFoundException();
+ }
+ }
+
+ return $file;
+ }
+
+ /**
+ * Returns the user display name.
+ *
+ * @return string
+ */
+ public function getDisplayName(): string {
+ return $this->user->getDisplayName();
+ }
+
+ /**
+ * Handles user changes.
+ *
+ * @param string $feature The changed feature
+ * @param mixed $oldValue The previous value
+ * @param mixed $newValue The new value
+ * @throws NotPermittedException
+ * @throws \OCP\PreConditionNotMetException
+ */
+ public function userChanged($feature, $oldValue, $newValue) {
+ $this->remove();
+ }
+
+ /**
+ * Check if the avatar of a user is a custom uploaded one
+ *
+ * @return bool
+ */
+ public function isCustomAvatar(): bool {
+ return false;
+ }
+}
diff --git a/lib/private/Avatar/UserAvatar.php b/lib/private/Avatar/UserAvatar.php
index f7ace429f7d..f47809425ed 100644
--- a/lib/private/Avatar/UserAvatar.php
+++ b/lib/private/Avatar/UserAvatar.php
@@ -270,6 +270,7 @@ class UserAvatar extends Avatar {
throw new NotFoundException;
}
+ // TODO: rework to integrate with the PlaceholderAvatar in a compatible way
if ($this->folder->fileExists('generated')) {
if (!$data = $this->generateAvatarFromSvg($size)) {
$data = $this->generateAvatar($this->getDisplayName(), $size);
diff --git a/lib/private/KnownUser/KnownUserService.php b/lib/private/KnownUser/KnownUserService.php
index 96af21c836f..1f300a9f8e4 100644
--- a/lib/private/KnownUser/KnownUserService.php
+++ b/lib/private/KnownUser/KnownUserService.php
@@ -74,6 +74,10 @@ class KnownUserService {
* @return bool
*/
public function isKnownToUser(string $knownTo, string $contactUserId): bool {
+ if ($knownTo === $contactUserId) {
+ return true;
+ }
+
if (!isset($this->knownUsers[$knownTo])) {
$entities = $this->mapper->getKnownUsers($knownTo);
$this->knownUsers[$knownTo] = [];
diff --git a/lib/private/Server.php b/lib/private/Server.php
index c0d6afbaaf6..26c76125e56 100644
--- a/lib/private/Server.php
+++ b/lib/private/Server.php
@@ -104,6 +104,7 @@ use OC\IntegrityCheck\Checker;
use OC\IntegrityCheck\Helpers\AppLocator;
use OC\IntegrityCheck\Helpers\EnvironmentHelper;
use OC\IntegrityCheck\Helpers\FileAccessHelper;
+use OC\KnownUser\KnownUserService;
use OC\Lock\DBLockingProvider;
use OC\Lock\MemcacheLockingProvider;
use OC\Lock\NoopLockingProvider;
@@ -720,11 +721,14 @@ class Server extends ServerContainer implements IServerContainer {
$this->registerService(AvatarManager::class, function (Server $c) {
return new AvatarManager(
+ $c->get(IUserSession::class),
$c->get(\OC\User\Manager::class),
$c->getAppDataDir('avatar'),
$c->getL10N('lib'),
$c->get(ILogger::class),
- $c->get(\OCP\IConfig::class)
+ $c->get(\OCP\IConfig::class),
+ $c->get(IAccountManager::class),
+ $c->get(KnownUserService::class)
);
});
$this->registerAlias(IAvatarManager::class, AvatarManager::class);