Skip to content

Commit

Permalink
share with teams
Browse files Browse the repository at this point in the history
Signed-off-by: Hoang Pham <[email protected]>
  • Loading branch information
hweihwang committed Nov 22, 2024
1 parent 1a8a8fd commit 34d53d6
Show file tree
Hide file tree
Showing 15 changed files with 341 additions and 55 deletions.
11 changes: 9 additions & 2 deletions lib/Capabilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

namespace OCA\Tables;

use OCA\Tables\Helper\CircleHelper;
use OCP\App\IAppManager;
use OCP\Capabilities\ICapability;
use OCP\IConfig;
Expand All @@ -18,18 +19,23 @@
*/
class Capabilities implements ICapability {
private IAppManager $appManager;

private LoggerInterface $logger;

private IConfig $config;

public function __construct(IAppManager $appManager, LoggerInterface $logger, IConfig $config) {
private CircleHelper $circleHelper;

public function __construct(IAppManager $appManager, LoggerInterface $logger, IConfig $config, CircleHelper $circleHelper) {
$this->appManager = $appManager;
$this->logger = $logger;
$this->config = $config;
$this->circleHelper = $circleHelper;
}

/**
*
* @return array{tables: array{enabled: bool, version: string, apiVersions: string[], features: string[], column_types: string[]}}
* @return array{tables: array{enabled: bool, version: string, apiVersions: string[], features: string[], isCirclesEnabled: bool, column_types: string[]}}
*
* @inheritDoc
*/
Expand All @@ -52,6 +58,7 @@ public function getCapabilities(): array {
'favorite',
'archive',
],
'isCirclesEnabled' => $this->circleHelper->isCirclesEnabled(),
'column_types' => [
'text-line',
$textColumnVariant,
Expand Down
89 changes: 89 additions & 0 deletions lib/Helper/CircleHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php

/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Tables\Helper;

use OCA\Circles\CirclesManager;
use OCA\Circles\Model\Member;
use OCA\Circles\Model\Probes\CircleProbe;
use OCP\App\IAppManager;
use OCP\Server;
use Psr\Log\LoggerInterface;
use Throwable;

/**
* @psalm-suppress UndefinedClass
*/
class CircleHelper {
private LoggerInterface $logger;
private bool $circlesEnabled;
private ?CirclesManager $circlesManager;

/**
* @psalm-suppress UndefinedClass
*/
public function __construct(
LoggerInterface $logger,
IAppManager $appManager,
?CirclesManager $circlesManager = null
) {
$this->logger = $logger;
$this->circlesEnabled = $appManager->isEnabledForUser('circles');
if ($this->circlesEnabled) {
try {
$this->circlesManager = $circlesManager ?? Server::get(CirclesManager::class);
} catch (Throwable $e) {
$this->logger->warning('Failed to get CirclesManager: ' . $e->getMessage());
$this->circlesManager = null;
$this->circlesEnabled = false;
}
} else {
$this->circlesManager = null;
}
}

public function isCirclesEnabled(): bool {
return $this->circlesEnabled;
}

public function getCircleDisplayName(string $circleId, string $userId): string {
if (!$this->circlesEnabled) {
return $circleId;
}

try {
$federatedUser = $this->circlesManager->getFederatedUser($userId, Member::TYPE_USER);
$this->circlesManager->startSession($federatedUser);

$circle = $this->circlesManager->getCircle($circleId);
return $circle ? ($circle->getDisplayName() ?: $circleId) : $circleId;
} catch (Throwable $e) {
$this->logger->warning('Failed to get circle display name: ' . $e->getMessage(), [
'circleId' => $circleId,
'userId' => $userId
]);
return $circleId;
}
}

public function getUserCircles(string $userId): array {
if (!$this->circlesEnabled) {
return [];
}

try {
$federatedUser = $this->circlesManager->getFederatedUser($userId, Member::TYPE_USER);
$this->circlesManager->startSession($federatedUser);
$probe = new CircleProbe();
$probe->mustBeMember();
return $this->circlesManager->getCircles($probe);
} catch (Throwable $e) {
$this->logger->warning('Failed to get user circles: ' . $e->getMessage());
return [];
}
}
}
9 changes: 9 additions & 0 deletions lib/Helper/UserHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,26 @@
use OCP\IUserManager;
use Psr\Log\LoggerInterface;

/**
* @psalm-suppress UndefinedClass
* @psalm-suppress UndefinedDocblockClass
*/
class UserHelper {
private IUserManager $userManager;

private LoggerInterface $logger;

private IGroupManager $groupManager;

/**
* @psalm-suppress UndefinedClass
*/
public function __construct(IUserManager $userManager, LoggerInterface $logger, IGroupManager $groupManager) {
$this->userManager = $userManager;
$this->logger = $logger;
$this->groupManager = $groupManager;
}

public function getUserDisplayName(string $userId): string {
try {
$user = $this->getUser($userId);
Expand Down
43 changes: 39 additions & 4 deletions lib/Service/PermissionsService.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,19 @@
use OCA\Tables\Db\ViewMapper;
use OCA\Tables\Errors\InternalError;
use OCA\Tables\Errors\NotFoundError;
use OCA\Tables\Helper\CircleHelper;
use OCA\Tables\Helper\ConversionHelper;
use OCA\Tables\Helper\UserHelper;
use OCA\Tables\Model\Permissions;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
use OCP\DB\Exception;
use Psr\Log\LoggerInterface;
use Throwable;

/**
* @psalm-suppress UndefinedDocblockClass
*/
class PermissionsService {
private TableMapper $tableMapper;

Expand All @@ -35,11 +40,14 @@ class PermissionsService {

private UserHelper $userHelper;

private CircleHelper $circleHelper;

protected LoggerInterface $logger;

protected ?string $userId = null;

protected bool $isCli = false;

private ContextMapper $contextMapper;

public function __construct(
Expand All @@ -50,6 +58,7 @@ public function __construct(
ShareMapper $shareMapper,
ContextMapper $contextMapper,
UserHelper $userHelper,
CircleHelper $circleHelper,
bool $isCLI
) {
$this->tableMapper = $tableMapper;
Expand All @@ -60,6 +69,7 @@ public function __construct(
$this->userId = $userId;
$this->isCli = $isCLI;
$this->contextMapper = $contextMapper;
$this->circleHelper = $circleHelper;
}


Expand Down Expand Up @@ -420,6 +430,7 @@ public function canReadShare(Share $share, ?string $userId = null): bool {
* @param int $elementId
* @param 'table'|'view' $elementType
* @param string $userId
* @return Permissions
* @throws NotFoundError
*/
public function getSharedPermissionsIfSharedWithMe(int $elementId, string $elementType, string $userId): Permissions {
Expand All @@ -436,16 +447,40 @@ public function getSharedPermissionsIfSharedWithMe(int $elementId, string $eleme
$this->logger->warning('Exception occurred: '.$e->getMessage().' Permission denied.');
return new Permissions();
}
$additionalShares = [];
$groupShares = [];
foreach ($userGroups as $userGroup) {
try {
$additionalShares[] = $this->shareMapper->findAllSharesForNodeFor($elementType, $elementId, $userGroup->getGid(), 'group');
$groupShares[] = $this->shareMapper->findAllSharesForNodeFor($elementType, $elementId, $userGroup->getGid(), 'group');
} catch (Exception $e) {
$this->logger->warning('Exception occurred: '.$e->getMessage().' Permission denied.');
return new Permissions();
}
}
$shares = array_merge($shares, ...$additionalShares);

$shares = array_merge($shares, ...$groupShares);

if ($this->circleHelper->isCirclesEnabled()) {
$circleShares = [];

try {
$userCircles = $this->circleHelper->getUserCircles($userId);
} catch (Throwable $e) {
$this->logger->warning('Exception occurred: ' . $e->getMessage() . ' Permission denied.');
return new Permissions();
}

foreach ($userCircles as $userCircle) {
try {
$circleShares[] = $this->shareMapper->findAllSharesForNodeFor($elementType, $elementId, $userCircle->getSingleId(), 'circle');
} catch (Exception $e) {
$this->logger->warning('Exception occurred: ' . $e->getMessage() . ' Permission denied.');
return new Permissions();
}
}

$shares = array_merge($shares, ...$circleShares);
}

if (count($shares) > 0) {
$read = array_reduce($shares, function ($carry, $share) {
return $carry || ($share->getPermissionRead());
Expand Down Expand Up @@ -520,7 +555,7 @@ private function hasPermission(int $existingPermissions, string $permissionName)
$constantName = 'PERMISSION_' . strtoupper($permissionName);
try {
$permissionBit = constant(Application::class . "::$constantName");
} catch (\Throwable $t) {
} catch (Throwable $t) {
$this->logger->error('Unexpected permission string {permission}', [
'app' => Application::APP_ID,
'permission' => $permissionName,
Expand Down
32 changes: 30 additions & 2 deletions lib/Service/ShareService.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@
use OCA\Tables\Errors\InternalError;
use OCA\Tables\Errors\NotFoundError;
use OCA\Tables\Errors\PermissionError;
use OCA\Tables\Helper\CircleHelper;
use OCA\Tables\Helper\GroupHelper;
use OCA\Tables\Helper\UserHelper;

use OCA\Tables\Model\Permissions;
use OCA\Tables\ResponseDefinitions;
use OCP\AppFramework\Db\DoesNotExistException;
Expand All @@ -35,9 +35,11 @@
use OCP\DB\Exception;
use OCP\IDBConnection;
use Psr\Log\LoggerInterface;
use Throwable;

/**
* @psalm-import-type TablesShare from ResponseDefinitions
* @psalm-suppress UndefinedDocblockClass
*/
class ShareService extends SuperService {
use TTransactional;
Expand All @@ -51,7 +53,11 @@ class ShareService extends SuperService {
protected UserHelper $userHelper;

protected GroupHelper $groupHelper;

protected CircleHelper $circleHelper;

private ContextNavigationMapper $contextNavigationMapper;

private IDBConnection $dbc;

public function __construct(
Expand All @@ -63,6 +69,7 @@ public function __construct(
ViewMapper $viewMapper,
UserHelper $userHelper,
GroupHelper $groupHelper,
CircleHelper $circleHelper,
ContextNavigationMapper $contextNavigationMapper,
IDBConnection $dbc,
) {
Expand All @@ -72,6 +79,7 @@ public function __construct(
$this->viewMapper = $viewMapper;
$this->userHelper = $userHelper;
$this->groupHelper = $groupHelper;
$this->circleHelper = $circleHelper;
$this->contextNavigationMapper = $contextNavigationMapper;
$this->dbc = $dbc;
}
Expand Down Expand Up @@ -163,7 +171,17 @@ private function findElementsSharedWithMe(string $elementType = 'table', ?string
$shares = $this->mapper->findAllSharesFor($elementType, $userGroup->getGid(), $userId, 'group');
$elementsSharedWithMe = array_merge($elementsSharedWithMe, $shares);
}
} catch (Exception $e) {

// get all views or tables that are shared with me by circle
if ($this->circleHelper->isCirclesEnabled()) {
$userCircles = $this->circleHelper->getUserCircles($userId);

foreach ($userCircles as $userCircle) {
$shares = $this->mapper->findAllSharesFor($elementType, $userCircle->getSingleId(), $userId, 'circle');
$elementsSharedWithMe = array_merge($elementsSharedWithMe, $shares);
}
}
} catch (Throwable $e) {
throw new InternalError($e->getMessage());
}
foreach ($elementsSharedWithMe as $share) {
Expand Down Expand Up @@ -398,6 +416,16 @@ private function addReceiverDisplayName(Share $share):Share {
$share->setReceiverDisplayName($this->userHelper->getUserDisplayName($share->getReceiver()));
} elseif ($share->getReceiverType() === 'group') {
$share->setReceiverDisplayName($this->groupHelper->getGroupDisplayName($share->getReceiver()));
} elseif ($share->getReceiverType() === 'circle') {
if ($this->circleHelper->isCirclesEnabled()) {
$share->setReceiverDisplayName($this->circleHelper->getCircleDisplayName($share->getReceiver(), $this->userId));
} else {
$this->logger->info(
'Could not get display name for receiver type {type}',
['type' => $share->getReceiverType()]
);
$share->setReceiverDisplayName($share->getReceiver());
}
} else {
$this->logger->info('can not use receiver type to get display name');
$share->setReceiverDisplayName($share->getReceiver());
Expand Down
4 changes: 4 additions & 0 deletions openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"version",
"apiVersions",
"features",
"isCirclesEnabled",
"column_types"
],
"properties": {
Expand All @@ -54,6 +55,9 @@
"type": "string"
}
},
"isCirclesEnabled": {
"type": "boolean"
},
"column_types": {
"type": "array",
"items": {
Expand Down
2 changes: 2 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@mdi/svg": "^7.4.47",
"@nextcloud/auth": "^2.4.0",
"@nextcloud/axios": "^2.5.1",
"@nextcloud/capabilities": "^1.2.0",
"@nextcloud/dialogs": "^6.0.1",
"@nextcloud/event-bus": "^3.3.1",
"@nextcloud/files": "^3.10.0",
Expand Down
Loading

0 comments on commit 34d53d6

Please sign in to comment.