Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[stable27] detailed access list #1660

Merged
merged 2 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion lib/Db/CoreQueryBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ public function limitToFileSource(int $nodeId): void {
* @param array $files
*/
public function limitToFileSourceArray(array $files): void {
$this->limitArray('file_source', $files);
$this->limitInArray('file_source', $files, type: IQueryBuilder::PARAM_INT_ARRAY);
}


Expand Down
13 changes: 13 additions & 0 deletions lib/Db/ShareTokenRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

use OCA\Circles\Exceptions\ShareTokenNotFoundException;
use OCA\Circles\Model\ShareToken;
use OCP\DB\QueryBuilder\IQueryBuilder;

/**
* Class ShareTokenRequest
Expand Down Expand Up @@ -116,4 +117,16 @@ public function removeTokens(string $singleId, string $circleId) {

$qb->execute();
}

/**
* @param array $shareIds
*
* @return ShareToken[]
*/
public function getTokensFromShares(array $shareIds): array {
$qb = $this->getTokenSelectSql();
$qb->limitInArray('share_id', $shareIds, type: IQueryBuilder::PARAM_INT_ARRAY);

return $this->getItemsFromRequest($qb);
}
}
10 changes: 7 additions & 3 deletions lib/Db/ShareWrapperRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -285,19 +285,23 @@ public function getSharesByFileId(int $fileId, bool $getData = false): array {
* @return ShareWrapper[]
* @throws RequestBuilderException
*/
public function getSharesByFileIds(array $fileIds, bool $getData = false): array {
public function getSharesByFileIds(array $fileIds, bool $getData = false, bool $getChild = false): array {
$qb = $this->getShareSelectSql();
$qb->limitToFileSourceArray($fileIds);

if ($getData) {
$qb->setOptions([CoreQueryBuilder::SHARE], ['getData' => $getData]);
$qb->leftJoinCircle(CoreQueryBuilder::SHARE, null, 'share_with');
$qb->limitNull('parent', false);
}

if ($getChild) {
$qb->orderBy('parent', 'asc');
} else {
$qb->limitNull('parent', false);
}
return $this->getItemsFromRequest($qb);
}


/**
* @param FederatedUser $federatedUser
Expand Down
11 changes: 11 additions & 0 deletions lib/Model/ShareWrapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class ShareWrapper extends ManagedModel implements IDeserializable, IQueryRow, J
private ?DateTime $expirationDate = null;
private string $shareOwner = '';
private int $shareType = 0;
private int $parent = 0;
private ?Circle $circle = null;
private int $childId = 0;
private string $childFileTarget = '';
Expand Down Expand Up @@ -252,6 +253,15 @@ public function getShareType(): int {
return $this->shareType;
}

public function setParent(int $parent): self {
$this->parent = $parent;
return $this;
}

public function getParent(): int {
return $this->parent;
}

public function setCircle(Circle $circle): self {
$this->circle = $circle;

Expand Down Expand Up @@ -492,6 +502,7 @@ public function import(array $data): IDeserializable {

$this->setId($this->get('id', $data))
->setShareType($this->getInt('shareType', $data))
->setParent($data['parent'] ?? 0)
->setPermissions($this->getInt('permissions', $data))
->setHideDownload($this->getBool('hideDownload', $data))
->setItemType($this->get('itemType', $data))
Expand Down
9 changes: 9 additions & 0 deletions lib/Service/ShareTokenService.php
Original file line number Diff line number Diff line change
Expand Up @@ -169,4 +169,13 @@ public function removeSharePassword(string $circleId): void {
public function removeTokens(string $singleId, string $circleId) {
$this->shareTokenRequest->removeTokens($singleId, $circleId);
}

/**
* @param array $shareIds
*
* @return ShareToken[]
*/
public function getTokensFromShares(array $shareIds): array {
return ($shareIds === []) ? [] : $this->shareTokenRequest->getTokensFromShares($shareIds);
}
}
4 changes: 2 additions & 2 deletions lib/Service/ShareWrapperService.php
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,8 @@ public function getSharesByFileId(int $fileId, bool $getData = false): array {
* @return ShareWrapper[]
* @throws RequestBuilderException
*/
public function getSharesByFileIds(array $fileIds, bool $getData = false): array {
return ($fileIds === []) ? [] : $this->shareWrapperRequest->getSharesByFileIds($fileIds, $getData);
public function getSharesByFileIds(array $fileIds, bool $getData = false, bool $getChild = false): array {
return ($fileIds === []) ? [] : $this->shareWrapperRequest->getSharesByFileIds($fileIds, $getData, $getChild);
}

/**
Expand Down
124 changes: 118 additions & 6 deletions lib/ShareByCircleProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
use OCA\Circles\Service\EventService;
use OCA\Circles\Service\FederatedEventService;
use OCA\Circles\Service\FederatedUserService;
use OCA\Circles\Service\ShareTokenService;
use OCA\Circles\Service\ShareWrapperService;
use OCA\Circles\Tools\Traits\TArrayTools;
use OCA\Circles\Tools\Traits\TNCLogger;
Expand Down Expand Up @@ -108,6 +109,7 @@ class ShareByCircleProvider implements IShareProvider {
private LoggerInterface $logger;
private IURLGenerator $urlGenerator;
private ShareWrapperService $shareWrapperService;
private ShareTokenService $shareTokenService;
private FederatedUserService $federatedUserService;
private FederatedEventService $federatedEventService;
private CircleService $circleService;
Expand All @@ -131,6 +133,7 @@ public function __construct(
$this->federatedUserService = OC::$server->get(FederatedUserService::class);
$this->federatedEventService = OC::$server->get(FederatedEventService::class);
$this->shareWrapperService = OC::$server->get(ShareWrapperService::class);
$this->shareTokenService = OC::$server->get(ShareTokenService::class);
$this->circleService = OC::$server->get(CircleService::class);
$this->eventService = OC::$server->get(EventService::class);
}
Expand Down Expand Up @@ -649,6 +652,27 @@ public function userDeletedFromGroup($uid, $gid): void {


/**
* if $currentAccess, returns long version of the access list:
* [
* 'users' => [
* 'user1' => ['node_id' => 42, 'node_path' => '/fileA'],
* 'user4' => ['node_id' => 32, 'node_path' => '/folder2'],
* 'user2' => ['node_id' => 23, 'node_path' => '/folder (1)'],
* ],
* 'remote' => [
* 'user1@server1' => ['node_id' => 42, 'token' => 'SeCr3t'],
* 'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'],
* ],
* 'public' => bool,
* 'mail' => [
* 'email1@maildomain1' => ['node_id' => 42, 'token' => 'aBcDeFg'],
* 'email2@maildomain2' => ['node_id' => 23, 'token' => 'hIjKlMn'],
* ]
* ]
*
*
*
*
* @param Node[] $nodes
* @param bool $currentAccess
*
Expand All @@ -660,10 +684,83 @@ public function getAccessList($nodes, $currentAccess): array {
$ids[] = $node->getId();
}

$users = $remote = $mails = [];
$shares = $this->shareWrapperService->getSharesByFileIds($ids, true);
if (!$currentAccess) {
return $this->getAccessListShort($ids);
}

foreach ($shares as $share) {
$shareIds = $knownIds = $users = $remote = $mails = [];
foreach ($this->shareWrapperService->getSharesByFileIds($ids, true, true) as $share) {
$shareIds[] = $share->getId();
$circle = $share->getCircle();
foreach ($circle->getInheritedMembers() as $member) {
if ($share->getParent() > 0 && in_array($member->getSingleId(), $knownIds[$share->getFileSource()] ?? [])) {
continue;
}
$knownIds[$share->getFileSource()][] = $member->getSingleId();

switch ($member->getUserType()) {
case Member::TYPE_USER:
if ($member->isLocal()) {
$users[$member->getUserId()] = [
'node_id' => $share->getFileSource(),
'node_path' => $share->getFileTarget()
];
} else {
// we only store temp value, as token is unknown at this point
$remote[$member->getUserid() . '@' . $member->getInstance()] = [
'node_id' => $share->getFileSource(),
'shareId' => $share->getId(),
'memberId' => $member->getId(),
];
}
break;
case Member::TYPE_MAIL:
// we only store temp value, as token is unknown at this point
$mails[$member->getUserId()] = [
'node_id' => $share->getFileSource(),
'shareId' => $share->getId(),
'memberId' => $member->getId(),
];
break;
}
}
}

// list share tokens in an indexed array and update details for remote/mail entries with the correct token
$shareTokens = [];
foreach ($this->shareTokenService->getTokensFromShares(array_values(array_unique($shareIds))) as $shareToken) {
$shareTokens[$shareToken->getShareId()][$shareToken->getMemberId()] = $shareToken->getToken();
}

return [
'users' => $users,
'remote' => $this->updateAccessListTokens($remote, $shareTokens),
'email' => $this->updateAccessListTokens($mails, $shareTokens)
];
}

/**
* returns short version of the access list:
* [
* 'users' => ['user1', 'user2', 'user4'],
* 'remote' => bool,
* 'mail' => ['email1@maildomain1', 'email2@maildomain2']
* ]
*
* @param array $ids
*
* @return array
* @throws FederatedItemException
* @throws RemoteInstanceException
* @throws RemoteNotFoundException
* @throws RemoteResourceNotFoundException
* @throws RequestBuilderException
* @throws UnknownRemoteException
*/
private function getAccessListShort(array $ids): array {
$users = $mails = [];
$remote = false;
foreach ($this->shareWrapperService->getSharesByFileIds($ids, true) as $share) {
$circle = $share->getCircle();
foreach ($circle->getInheritedMembers() as $member) {
switch ($member->getUserType()) {
Expand All @@ -673,9 +770,7 @@ public function getAccessList($nodes, $currentAccess): array {
$users[] = $member->getUserId();
}
} else {
if (!in_array($member->getUserId(), $remote)) {
$remote[] = $member->getUserid() . '@' . $member->getInstance();
}
$remote = true;
}
break;
case Member::TYPE_MAIL:
Expand All @@ -694,6 +789,23 @@ public function getAccessList($nodes, $currentAccess): array {
];
}

/**
* @param array $list
* @param array<int, array<string, string>> $shareTokens
*
* @return array
*/
private function updateAccessListTokens(array $list, array $shareTokens): array {
$result = [];
foreach ($list as $id => $data) {
$result[$id] = [
'node_id' => $data['node_id'],
'token' => $shareTokens[$data['shareId']][$data['memberId']]
];
}

return $result;
}

/**
* We don't return a thing about children.
Expand Down
17 changes: 11 additions & 6 deletions lib/Tools/Db/ExtendedQueryBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -359,9 +359,10 @@ public function limitArray(string $field, array $value, string $alias = '', bool
* @param string $field
* @param array $value
* @param string $alias
* @param int $type
*/
public function limitInArray(string $field, array $value, string $alias = ''): void {
$this->andWhere($this->exprLimitInArray($field, $value, $alias));
public function limitInArray(string $field, array $value, string $alias = '', int $type = IQueryBuilder::PARAM_STR_ARRAY): void {
$this->andWhere($this->exprLimitInArray($field, $value, $alias, $type));
}

/**
Expand Down Expand Up @@ -540,22 +541,26 @@ public function exprLimitArray(
return $andX;
}


/**
* @param string $field
* @param array $values
* @param string $alias
* @param int $type
*
* @return string
*/
public function exprLimitInArray(string $field, array $values, string $alias = ''): string {
public function exprLimitInArray(
string $field,
array $values,
string $alias = '',
int $type = IQueryBuilder::PARAM_STR_ARRAY
): string {
if ($this->getType() === DBALQueryBuilder::SELECT) {
$field = (($alias === '') ? $this->getDefaultSelectAlias() : $alias) . '.' . $field;
}

$expr = $this->expr();

return $expr->in($field, $this->createNamedParameter($values, IQueryBuilder::PARAM_STR_ARRAY));
return $expr->in($field, $this->createNamedParameter($values, $type));
}


Expand Down
Loading