Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add known user check in avatar when v2-private scope
Signed-off-by: Vincent Petry <[email protected]>
  • Loading branch information
PVince81 committed Mar 26, 2021
commit cc54f718f561c73cf5e91a2a42cd8b1d878d02d2
35 changes: 23 additions & 12 deletions lib/private/Avatar/AvatarManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

namespace OC\Avatar;

use OC\KnownUser\KnownUserService;
use OC\User\Manager;
use OC\User\NoUserException;
use OCP\Accounts\IAccountManager;
Expand Down Expand Up @@ -73,6 +74,9 @@ class AvatarManager implements IAvatarManager {
/** @var IAccountManager */
private $accountManager;

/** @var KnownUserService */
private $knownUserService;

/**
* AvatarManager constructor.
*
Expand All @@ -90,14 +94,17 @@ public function __construct(
IL10N $l,
ILogger $logger,
IConfig $config,
IAccountManager $accountManager) {
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;
}

/**
Expand Down Expand Up @@ -128,17 +135,21 @@ public function getAvatar(string $userId) : IAvatar {
$folder = $this->appData->newFolder($userId);
}

// requesting in public page
if ($requestingUser === null) {
$account = $this->accountManager->getAccount($user);
$avatarProperties = $account->getProperty(IAccountManager::PROPERTY_AVATAR);
$avatarScope = $avatarProperties->getScope();

// v2-private scope hides the avatar from public access
if ($avatarScope === IAccountManager::SCOPE_PRIVATE) {
// use a placeholder avatar which caches the generated images
return new PlaceholderAvatar($folder, $user, $this->logger);
}
$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);
Expand Down
4 changes: 4 additions & 0 deletions lib/private/KnownUser/KnownUserService.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ public function storeIsKnownToUser(string $knownTo, string $contactUserId): void
* @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] = [];
Expand Down
4 changes: 3 additions & 1 deletion lib/private/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
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;
Expand Down Expand Up @@ -726,7 +727,8 @@ public function __construct($webRoot, \OC\Config $config) {
$c->getL10N('lib'),
$c->get(ILogger::class),
$c->get(\OCP\IConfig::class),
$c->get(IAccountManager::class)
$c->get(IAccountManager::class),
$c->get(KnownUserService::class)
);
});
$this->registerAlias(IAvatarManager::class, AvatarManager::class);
Expand Down
111 changes: 89 additions & 22 deletions tests/lib/Avatar/AvatarManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
use OC\Avatar\AvatarManager;
use OC\Avatar\PlaceholderAvatar;
use OC\Avatar\UserAvatar;
use OC\KnownUser\KnownUserService;
use OC\User\Manager;
use OCP\Accounts\IAccount;
use OCP\Accounts\IAccountManager;
Expand Down Expand Up @@ -59,6 +60,8 @@ class AvatarManagerTest extends \Test\TestCase {
private $accountManager;
/** @var AvatarManager | \PHPUnit\Framework\MockObject\MockObject */
private $avatarManager;
/** @var KnownUserService | \PHPUnit\Framework\MockObject\MockObject */
private $knownUserService;

protected function setUp(): void {
parent::setUp();
Expand All @@ -70,6 +73,7 @@ protected function setUp(): void {
$this->logger = $this->createMock(ILogger::class);
$this->config = $this->createMock(IConfig::class);
$this->accountManager = $this->createMock(IAccountManager::class);
$this->knownUserService = $this->createMock(KnownUserService::class);

$this->avatarManager = new AvatarManager(
$this->userSession,
Expand All @@ -78,7 +82,8 @@ protected function setUp(): void {
$this->l10n,
$this->logger,
$this->config,
$this->accountManager
$this->accountManager,
$this->knownUserService
);
}

Expand All @@ -95,26 +100,45 @@ public function testGetAvatarInvalidUser() {
$this->avatarManager->getAvatar('invalidUser');
}

public function testGetAvatarValidUser() {
// requesting user
$this->userSession->expects($this->once())
->method('getUser')
->willReturn($this->createMock(IUser::class));

// we skip account scope check for logged in user
$this->accountManager->expects($this->never())
->method('getAccount');

public function testGetAvatarForSelf() {
$user = $this->createMock(IUser::class);
$user
->expects($this->once())
->expects($this->any())
->method('getUID')
->willReturn('valid-user');

// requesting user
$this->userSession->expects($this->once())
->method('getUser')
->willReturn($user);

$this->userManager
->expects($this->once())
->method('get')
->with('valid-user')
->willReturn($user);

$account = $this->createMock(IAccount::class);
$this->accountManager->expects($this->once())
->method('getAccount')
->with($user)
->willReturn($account);

$property = $this->createMock(IAccountProperty::class);
$account->expects($this->once())
->method('getProperty')
->with(IAccountManager::PROPERTY_AVATAR)
->willReturn($property);

$property->expects($this->once())
->method('getScope')
->willReturn(IAccountManager::SCOPE_PRIVATE);

$this->knownUserService->expects($this->any())
->method('isKnownToUser')
->with('valid-user', 'valid-user')
->willReturn(true);

$folder = $this->createMock(ISimpleFolder::class);
$this->appData
->expects($this->once())
Expand Down Expand Up @@ -148,7 +172,36 @@ public function testGetAvatarValidUserDifferentCasing() {
$this->assertEquals($expected, $this->avatarManager->getAvatar('vaLid-USER'));
}

public function testGetAvatarPrivateScope() {
public function knownUnknownProvider() {
return [
[IAccountManager::SCOPE_LOCAL, false, false, false],
[IAccountManager::SCOPE_LOCAL, true, false, false],

// public access cannot see real avatar
[IAccountManager::SCOPE_PRIVATE, true, false, true],
// unknown users cannot see real avatar
[IAccountManager::SCOPE_PRIVATE, false, false, true],
// known users can see real avatar
[IAccountManager::SCOPE_PRIVATE, false, true, false],
];
}

/**
* @dataProvider knownUnknownProvider
*/
public function testGetAvatarScopes($avatarScope, $isPublicCall, $isKnownUser, $expectedPlaceholder) {
if ($isPublicCall) {
$requestingUser = null;
} else {
$requestingUser = $this->createMock(IUser::class);
$requestingUser->method('getUID')->willReturn('requesting-user');
}

// requesting user
$this->userSession->expects($this->once())
->method('getUser')
->willReturn($requestingUser);

$user = $this->createMock(IUser::class);
$user
->expects($this->once())
Expand All @@ -160,13 +213,6 @@ public function testGetAvatarPrivateScope() {
->with('valid-user')
->willReturn($user);

$folder = $this->createMock(ISimpleFolder::class);
$this->appData
->expects($this->once())
->method('getFolder')
->with('valid-user')
->willReturn($folder);

$account = $this->createMock(IAccount::class);
$this->accountManager->expects($this->once())
->method('getAccount')
Expand All @@ -181,9 +227,30 @@ public function testGetAvatarPrivateScope() {

$property->expects($this->once())
->method('getScope')
->willReturn(IAccountManager::SCOPE_PRIVATE);
->willReturn($avatarScope);

$folder = $this->createMock(ISimpleFolder::class);
$this->appData
->expects($this->once())
->method('getFolder')
->with('valid-user')
->willReturn($folder);

if (!$isPublicCall) {
$this->knownUserService->expects($this->any())
->method('isKnownToUser')
->with('requesting-user', 'valid-user')
->willReturn($isKnownUser);
} else {
$this->knownUserService->expects($this->never())
->method('isKnownToUser');
}

$expected = new PlaceholderAvatar($folder, $user, $this->createMock(ILogger::class));
if ($expectedPlaceholder) {
$expected = new PlaceholderAvatar($folder, $user, $this->createMock(ILogger::class));
} else {
$expected = new UserAvatar($folder, $this->l10n, $user, $this->logger, $this->config);
}
$this->assertEquals($expected, $this->avatarManager->getAvatar('valid-user'));
}
}