From 402e0acba726ed60f2113db430be2c557afb5b1a Mon Sep 17 00:00:00 2001 From: Maxence Lange Date: Tue, 12 Apr 2022 20:54:57 -0100 Subject: [PATCH] share with group members only Signed-off-by: Maxence Lange --- lib/Db/CoreQueryBuilder.php | 38 ++++++++++- lib/Db/MembershipRequest.php | 21 +++++- lib/FederatedItems/CircleJoin.php | 4 ++ lib/FederatedItems/SingleMemberAdd.php | 5 ++ lib/IEntity.php | 11 +++ lib/Model/Circle.php | 18 +++-- lib/Model/Federated/FederatedEvent.php | 6 ++ lib/Model/FederatedUser.php | 27 ++++---- lib/Model/Member.php | 16 +++-- lib/Model/Membership.php | 68 +++++++++++++++++++ lib/Model/ModelManager.php | 69 +++++++++++++++++-- lib/Service/MemberService.php | 2 +- lib/Service/MembershipService.php | 92 +++++++++++++++++++++++++- lib/Service/SyncService.php | 51 +++++++++++++- lib/StatusCode.php | 6 +- 15 files changed, 394 insertions(+), 40 deletions(-) diff --git a/lib/Db/CoreQueryBuilder.php b/lib/Db/CoreQueryBuilder.php index 532d6e023..8f4b7cee5 100644 --- a/lib/Db/CoreQueryBuilder.php +++ b/lib/Db/CoreQueryBuilder.php @@ -160,9 +160,19 @@ class CoreQueryBuilder extends ExtendedQueryBuilder { self::BASED_ON ] ], + + self::MEMBERSHIPS => [ - self::CONFIG + self::CONFIG, + self::INHERITED_BY, + self::CIRCLE => [ + self::OWNER => [ + self::BASED_ON + ] + ] ], + + self::SHARE => [ self::SHARE, self::TOKEN, @@ -733,6 +743,32 @@ public function leftJoinCircle( } + /** + * @param string $alias + * @param string $circleId + * @param string $singleId + * + * @throws RequestBuilderException + */ + public function leftJoinInheritedBy( + string $alias, + string $circleId = 'inheritance_last', + string $singleId = 'single_id' + ): void { + $aliasInheritedBy = $this->generateAlias($alias, self::INHERITED_BY); + $expr = $this->expr(); + + $this->generateMemberSelectAlias($aliasInheritedBy) + ->leftJoin( + $alias, CoreRequestBuilder::TABLE_MEMBER, $aliasInheritedBy, + $expr->andX( + $expr->eq($alias . '.' . $circleId, $aliasInheritedBy . '.circle_id'), + $expr->eq($alias . '.' . $singleId, $aliasInheritedBy . '.single_id') + ) + ); + } + + /** * @param string $aliasMember * diff --git a/lib/Db/MembershipRequest.php b/lib/Db/MembershipRequest.php index 0d1f5ccd5..c8435ad14 100644 --- a/lib/Db/MembershipRequest.php +++ b/lib/Db/MembershipRequest.php @@ -107,22 +107,33 @@ public function getMembership(string $circleId, string $singleId): Membership { * * @return Membership[] */ - public function getMemberships(string $singleId): array { + public function getMemberships(string $singleId, bool $detailed = false, int $level = 0): array { $qb = $this->getMembershipSelectSql(); $qb->limitToSingleId($singleId); $qb->leftJoinCircleConfig(CoreQueryBuilder::MEMBERSHIPS); + if ($level > 1) { + $expr = $qb->expr(); + $qb->andWhere($expr->gte('level', $qb->createNamedParameter($level, IQueryBuilder::PARAM_INT))); + } + + if ($detailed) { + $qb->setOptions([CoreQueryBuilder::MEMBERSHIPS], ['getData' => true]); + $qb->leftJoinCircle(CoreQueryBuilder::MEMBERSHIPS); + } + return $this->getItemsFromRequest($qb); } /** * @param string $singleId + * @param bool $detailed * @param int $level * * @return Membership[] */ - public function getInherited(string $singleId, int $level = 0): array { + public function getAccounted(string $singleId, bool $detailed = false, int $level = 0): array { $qb = $this->getMembershipSelectSql(); $qb->limitToCircleId($singleId); $qb->leftJoinCircleConfig(self::TABLE_MEMBERSHIP); @@ -132,6 +143,12 @@ public function getInherited(string $singleId, int $level = 0): array { $qb->andWhere($expr->gte('level', $qb->createNamedParameter($level, IQueryBuilder::PARAM_INT))); } + if ($detailed) { + $qb->setOptions([CoreQueryBuilder::MEMBERSHIPS], ['getData' => true]); + $qb->leftJoinCircle(CoreQueryBuilder::MEMBERSHIPS); + $qb->leftJoinInheritedBy(CoreQueryBuilder::MEMBERSHIPS); + } + return $this->getItemsFromRequest($qb); } diff --git a/lib/FederatedItems/CircleJoin.php b/lib/FederatedItems/CircleJoin.php index 3b7abd46f..75a90d2b8 100644 --- a/lib/FederatedItems/CircleJoin.php +++ b/lib/FederatedItems/CircleJoin.php @@ -307,6 +307,10 @@ private function manageMemberStatus(Circle $circle, Member $member) { throw new FederatedItemBadRequestException(StatusCode::$CIRCLE_JOIN[124], 124); } + if ($this->membershipService->limitToGroupMembersOnly($member, $circle)) { + throw new FederatedItemBadRequestException(StatusCode::$CIRCLE_JOIN[125], 125); + } + $member->setId($this->token(ManagedModel::ID_LENGTH)); if ($circle->isConfig(Circle::CFG_REQUEST)) { diff --git a/lib/FederatedItems/SingleMemberAdd.php b/lib/FederatedItems/SingleMemberAdd.php index 98b441f16..eb6bd6e9f 100644 --- a/lib/FederatedItems/SingleMemberAdd.php +++ b/lib/FederatedItems/SingleMemberAdd.php @@ -346,6 +346,11 @@ protected function generateMember(FederatedEvent $event, Circle $circle, Member throw new FederatedItemBadRequestException(StatusCode::$MEMBER_ADD[128], 128); } + if (!$event->getParams()->gBool('force_group_member') + && $this->membershipService->limitToGroupMembersOnly($federatedUser, $circle)) { + throw new FederatedItemBadRequestException(StatusCode::$MEMBER_ADD[133], 133); + } + if (!$this->configService->isLocalInstance($member->getInstance())) { if ($circle->isConfig(Circle::CFG_LOCAL)) { throw new FederatedItemBadRequestException(StatusCode::$MEMBER_ADD[126], 126); diff --git a/lib/IEntity.php b/lib/IEntity.php index 9826609fb..77f64b1d9 100644 --- a/lib/IEntity.php +++ b/lib/IEntity.php @@ -48,17 +48,28 @@ public function getSingleId(): string; /** * @param Membership[] $memberships + * @deprecated - do not use outside of the app itself, will be removed. * * @return $this */ public function setMemberships(array $memberships): self; /** + * TODO: might be better to have use abstract instead of interface * @return Membership[] */ public function getMemberships(): array; /** + * returns all memberships that can be hidden behind this entity, including this one. + * TODO: might be better to have use abstract instead of interface + * + * @return Membership[] + */ + public function getAccountedMemberships(bool $detailed): array; + + /** + * TODO: might be better to have use abstract instead of interface * @param string $singleId * @param bool $detailed * diff --git a/lib/Model/Circle.php b/lib/Model/Circle.php index 03c54e394..bad8fc5cb 100644 --- a/lib/Model/Circle.php +++ b/lib/Model/Circle.php @@ -461,6 +461,14 @@ public function getMembers(): array { } + /** + * @return Membership[] + */ + public function getAccountedMemberships(bool $detailed = false): array { + return $this->getManager()->getAccountedMemberships($this, $detailed); + } + + /** * @param array $members * @param bool $detailed @@ -515,7 +523,7 @@ function (Member $member): string { * @throws UnknownRemoteException */ public function getInheritedMembers(bool $detailed = false, bool $remote = false): array { - if (is_null($this->inheritedMembers) + if (is_null($this->inheritedMembers) // fix: array cannot be null. || ($detailed && !$this->detailedInheritedMember)) { $this->getManager()->getInheritedMembers($this, $detailed); } @@ -549,12 +557,8 @@ public function setMemberships(array $memberships): IEntity { /** * @return Membership[] */ - public function getMemberships(): array { - if (!$this->hasMemberships()) { - $this->getManager()->getMemberships($this); - } - - return $this->memberships; + public function getMemberships(bool $detailed = false, int $level = 0): array { + return $this->getManager()->getMemberships($this, $detailed, $level); } diff --git a/lib/Model/Federated/FederatedEvent.php b/lib/Model/Federated/FederatedEvent.php index 8443f67c9..8e53200de 100644 --- a/lib/Model/Federated/FederatedEvent.php +++ b/lib/Model/Federated/FederatedEvent.php @@ -388,6 +388,8 @@ public function setParams(SimpleDataStore $params): self { } /** + * params are kept between master and slave in GS setup + * * @return SimpleDataStore */ public function getParams(): SimpleDataStore { @@ -396,6 +398,8 @@ public function getParams(): SimpleDataStore { /** + * internal are only available to master in GS setup + * * @param SimpleDataStore $internal * * @return self @@ -424,6 +428,8 @@ public function resetInternal(): self { /** + * data are generated within the FederatedEvent + * * @param SimpleDataStore $data * * @return self diff --git a/lib/Model/FederatedUser.php b/lib/Model/FederatedUser.php index e9f4e9309..10fe7289c 100644 --- a/lib/Model/FederatedUser.php +++ b/lib/Model/FederatedUser.php @@ -31,19 +31,19 @@ namespace OCA\Circles\Model; -use OCA\Circles\Tools\Db\IQueryRow; -use OCA\Circles\Tools\Exceptions\InvalidItemException; -use OCA\Circles\Tools\IDeserializable; -use OCA\Circles\Tools\Traits\TDeserialize; -use OCA\Circles\Tools\Traits\TArrayTools; use JsonSerializable; use OCA\Circles\Exceptions\FederatedUserNotFoundException; use OCA\Circles\Exceptions\MembershipNotFoundException; use OCA\Circles\Exceptions\OwnerNotFoundException; use OCA\Circles\Exceptions\RequestBuilderException; use OCA\Circles\Exceptions\UnknownInterfaceException; -use OCA\Circles\IFederatedUser; use OCA\Circles\IEntity; +use OCA\Circles\IFederatedUser; +use OCA\Circles\Tools\Db\IQueryRow; +use OCA\Circles\Tools\Exceptions\InvalidItemException; +use OCA\Circles\Tools\IDeserializable; +use OCA\Circles\Tools\Traits\TArrayTools; +use OCA\Circles\Tools\Traits\TDeserialize; /** * Class FederatedUser @@ -309,6 +309,7 @@ public function hasMemberships(): bool { * @param array $memberships * * @return self + * @deprecated - can be removed. */ public function setMemberships(array $memberships): IEntity { $this->memberships = $memberships; @@ -319,15 +320,17 @@ public function setMemberships(array $memberships): IEntity { /** * @return Membership[] */ - public function getMemberships(): array { - if (!$this->hasMemberships()) { - $this->getManager()->getMemberships($this); - } - - return $this->memberships; + public function getMemberships(bool $detailed = false, int $level = 0): array { + return $this->getManager()->getMemberships($this, $detailed, $level); } + /** + * @return Membership[] + */ + public function getAccountedMemberships(bool $detailed = false, int $level = 0): array { + return $this->getManager()->getAccountedMemberships($this, $detailed, $level); + } /** * @param string $singleId diff --git a/lib/Model/Member.php b/lib/Model/Member.php index 4dd420b83..9a849d2ea 100644 --- a/lib/Model/Member.php +++ b/lib/Model/Member.php @@ -720,12 +720,16 @@ public function setMemberships(array $memberships): IEntity { /** * @return Membership[] */ - public function getMemberships(): array { - if (is_null($this->memberships)) { - $this->getManager()->getMemberships($this); - } + public function getMemberships(bool $detailed = false, int $level = 0): array { + return $this->getManager()->getMemberships($this, $detailed, $level); + } - return $this->memberships; + + /** + * @return Membership[] + */ + public function getAccountedMemberships(bool $detailed = false): array { + return $this->getManager()->getAccountedMemberships($this, $detailed); } @@ -741,7 +745,7 @@ public function getLink(string $singleId, bool $detailed = false): Membership { if ($singleId !== '') { $this->getManager()->getLink($this, $singleId, $detailed); } - + throw new MembershipNotFoundException(); } diff --git a/lib/Model/Membership.php b/lib/Model/Membership.php index bba5145ce..ef2a304ca 100644 --- a/lib/Model/Membership.php +++ b/lib/Model/Membership.php @@ -74,6 +74,12 @@ class Membership extends ManagedModel implements IDeserializable, IQueryRow, Jso /** @var array */ private $inheritanceDetails = []; + /** @var Circle */ + private $circle; + + /** @var FederatedUser */ + private $inheritedBy; + /** * Membership constructor. @@ -271,6 +277,58 @@ public function getInheritanceDetails(): array { } + /** + * @return bool + */ + public function hasCircle(): bool { + return !is_null($this->circle); + } + + /** + * @param Circle $circle + * + * @return Membership + */ + public function setCircle(Circle $circle): self { + $this->circle = $circle; + + return $this; + } + + /** + * @return Circle + */ + public function getCircle(): Circle { + return $this->circle; + } + + + /** + * @return bool + */ + public function hasInheritedBy(): bool { + return !is_null($this->inheritedBy); + } + + /** + * @param FederatedUser $inheritedBy + * + * @return Membership + */ + public function setInheritedBy(FederatedUser $inheritedBy): self { + $this->inheritedBy = $inheritedBy; + + return $this; + } + + /** + * @return FederatedUser + */ + public function getInheritedBy(): FederatedUser { + return $this->inheritedBy; + } + + /** * @param array $data * @@ -315,6 +373,8 @@ public function importFromDatabase(array $data, string $prefix = ''): IQueryRow $this->setInheritancePath($this->getArray($prefix . 'inheritance_path', $data)); $this->setInheritanceDepth($this->getInt($prefix . 'inheritance_depth', $data)); + $this->getManager()->manageImportFromDatabase($this, $data, $prefix); + return $this; } @@ -338,6 +398,14 @@ public function jsonSerialize(): array { $result['inheritanceDetails'] = $this->getInheritanceDetails(); } + if ($this->hasCircle()) { + $result['circle'] = $this->getCircle(); + } + + if ($this->hasInheritedBy()) { + $result['inheritedBy'] = $this->getInheritedBy(); + } + return $result; } } diff --git a/lib/Model/ModelManager.php b/lib/Model/ModelManager.php index 175214b90..95a46f39b 100644 --- a/lib/Model/ModelManager.php +++ b/lib/Model/ModelManager.php @@ -31,7 +31,6 @@ namespace OCA\Circles\Model; -use OCA\Circles\Tools\Traits\TNCLogger; use OCA\Circles\AppInfo\Application; use OCA\Circles\Db\CircleRequest; use OCA\Circles\Db\CoreQueryBuilder; @@ -57,6 +56,7 @@ use OCA\Circles\Service\InterfaceService; use OCA\Circles\Service\MembershipService; use OCA\Circles\Service\RemoteService; +use OCA\Circles\Tools\Traits\TNCLogger; use OCP\IURLGenerator; /** @@ -169,11 +169,23 @@ public function getInheritedMembers(Circle $circle, bool $detailed = false): voi $detailed ); } catch (RequestBuilderException $e) { - // TODO: debug log + $this->e($e, ['singleId' => $circle->getSingleId(), 'detailed' => $detailed]); } } + /** + * @param Circle $entity + * @param bool $detailed + * @param int $level + * + * @return array + */ + public function getAccountedMemberships(IEntity $entity, bool $detailed = false, int $level = 0): array { + return $this->membershipRequest->getAccounted($entity->getSingleId(), $detailed, $level); + } + + /** * @param Circle $circle * @param bool $detailed @@ -206,10 +218,13 @@ public function getRemoteInheritedMembers(Circle $circle, bool $detailed = false /** * @param IEntity $member + * @param bool $detailed + * @param int $level + * + * @return Membership[] */ - public function getMemberships(IEntity $member): void { - $memberships = $this->membershipRequest->getMemberships($member->getSingleId()); - $member->setMemberships($memberships); + public function getMemberships(IEntity $member, bool $detailed = false, int $level = 0): array { + return $this->membershipRequest->getMemberships($member->getSingleId(), $detailed, $level); } @@ -245,6 +260,12 @@ public function manageImportFromDatabase(ManagedModel $model, array $data, strin } } + if ($model instanceof Membership) { + if ($base === '') { + $base = CoreQueryBuilder::MEMBERSHIPS; + } + } + if ($model instanceof ShareWrapper) { if ($base === '') { $base = CoreQueryBuilder::SHARE; @@ -272,6 +293,10 @@ private function importBasedOnPath(ManagedModel $model, array $data, string $pat $this->importIntoMember($model, $data, $path, $prefix); } + if ($model instanceof Membership) { + $this->importIntoMembership($model, $data, $path, $prefix); + } + if ($model instanceof FederatedUser) { $this->importIntoFederatedUser($model, $data, $path, $prefix); } @@ -391,6 +416,40 @@ private function importIntoMember(Member $member, array $data, string $path, str } + /** + * @param Membership $membership + * @param array $data + * @param string $path + * @param string $prefix + */ + private function importIntoMembership( + Membership $membership, + array $data, + string $path, + string $prefix + ): void { + switch ($path) { + case CoreQueryBuilder::CIRCLE: + try { + $member = new Circle(); + $member->importFromDatabase($data, $prefix); + $membership->setCircle($member); + } catch (CircleNotFoundException $e) { + } + break; + + case CoreQueryBuilder::INHERITED_BY: + try { + $inheritedBy = new FederatedUser(); + $inheritedBy->importFromDatabase($data, $prefix); + $membership->setInheritedBy($inheritedBy); + } catch (FederatedUserNotFoundException $e) { + } + break; + } + } + + /** * @param FederatedUser $federatedUser * @param array $data diff --git a/lib/Service/MemberService.php b/lib/Service/MemberService.php index 58e1d261c..e04aed8a7 100644 --- a/lib/Service/MemberService.php +++ b/lib/Service/MemberService.php @@ -136,7 +136,7 @@ public function __construct( /** * @param string $memberId * @param string $circleId - * @param bool $canBeVisitor + * @param MemberProbe|null $probe * * @return Member * @throws InitiatorNotFoundException diff --git a/lib/Service/MembershipService.php b/lib/Service/MembershipService.php index 863831b16..6547f7876 100644 --- a/lib/Service/MembershipService.php +++ b/lib/Service/MembershipService.php @@ -38,12 +38,14 @@ use OCA\Circles\Exceptions\MembershipNotFoundException; use OCA\Circles\Exceptions\OwnerNotFoundException; use OCA\Circles\Exceptions\RequestBuilderException; +use OCA\Circles\IFederatedUser; use OCA\Circles\Model\Circle; use OCA\Circles\Model\FederatedUser; use OCA\Circles\Model\Member; use OCA\Circles\Model\Membership; use OCA\Circles\Tools\Exceptions\ItemNotFoundException; use OCA\Circles\Tools\Traits\TNCLogger; +use OCP\Share\IManager; /** * Class MembershipService @@ -54,6 +56,9 @@ class MembershipService { use TNCLogger; + /** @var IManager */ + private $shareManager; + /** @var MembershipRequest */ private $membershipRequest; @@ -75,6 +80,7 @@ class MembershipService { /** * MembershipService constructor. * + * @param IManager $shareManager * @param MembershipRequest $membershipRequest * @param CircleRequest $circleRequest * @param MemberRequest $memberRequest @@ -83,6 +89,7 @@ class MembershipService { * @param OutputService $outputService */ public function __construct( + IManager $shareManager, MembershipRequest $membershipRequest, CircleRequest $circleRequest, MemberRequest $memberRequest, @@ -90,6 +97,7 @@ public function __construct( ShareWrapperService $shareWrapperService, OutputService $outputService ) { + $this->shareManager = $shareManager; $this->membershipRequest = $membershipRequest; $this->circleRequest = $circleRequest; $this->memberRequest = $memberRequest; @@ -288,7 +296,7 @@ private function getChildrenMemberships(string $id, array &$knownIds = []): arra $singleIds = array_map( function (Membership $item): string { return $item->getSingleId(); - }, $this->membershipRequest->getInherited($id) + }, $this->membershipRequest->getAccounted($id) ); foreach ($singleIds as $singleId) { @@ -405,7 +413,11 @@ private function getMembershipsFromList(array $list, string $circleId): Membersh */ public function updatePopulation(Circle $circle): void { $local = $inherited = 0; - $memberships = $this->membershipRequest->getInherited($circle->getSingleId(), Member::LEVEL_MEMBER); + $memberships = $this->membershipRequest->getAccounted( + $circle->getSingleId(), + false, + Member::LEVEL_MEMBER + ); foreach ($memberships as $membership) { $inherited++; if ($membership->getCircleId() === $circle->getSingleId()) { @@ -418,4 +430,80 @@ public function updatePopulation(Circle $circle): void { $settings['populationInherited'] = $inherited; $this->circleRequest->updateSettings($circle->setSettings($settings)); } + + + /** + * based on Core Configuration, limit new members to shareGroupMembersOnly + * returns false if the new member is good to go. + * + * @param FederatedUser $federatedUser + * @param Circle $circle + * + * @return bool + * @throws RequestBuilderException + */ + public function limitToGroupMembersOnly(IFederatedUser $federatedUser, Circle $circle): bool { + if (!$this->shareManager->shareWithGroupMembersOnly()) { + return false; + } + + // bypass limit if Circle is not managed by a real user + if ($circle->getOwner()->getUserType() !== Member::TYPE_USER) { + return false; + } + + // we get all valid owner from the chain of circles upstream of the current circle. + // valid owner are the one based on an actual local user account. + // We do not need to check Circles that cannot be created from the OCS API. + $owners = array_filter( + array_map(function (Membership $membership): ?Member { + if (!$membership->hasCircle()) { + return null; + } + $owner = $membership->getCircle()->getOwner(); + if ($owner->getUserType() !== Member::TYPE_USER) { + return null; + } + + return $owner; + }, $circle->getMemberships(true)) + ); + + $owners[] = $circle->getOwner(); + + // foreach owners, we need to confirm at least one identical Nextcloud Group + // with each accounted memberships + foreach ($federatedUser->getAccountedMemberships(true) as $accounted) { + $accountedMemberships = array_filter( + array_map(function (Membership $membership): ?string { + if ($membership->getCircle()->getSource() !== Member::TYPE_GROUP) { + return null; + } + + return $membership->getCircleId(); + }, $accounted->getInheritedBy()->getMemberships(true)) + ); + + foreach ($owners as $owner) { + $ownerMemberships = array_filter( + array_map(function (Membership $membership): ?string { + if ($membership->getCircle()->getSource() !== Member::TYPE_GROUP) { + return null; + } + + return $membership->getCircleId(); + }, $owner->getMemberships(true)) + ); + + + // We compare list of circles based on Nextcloud Group to which both (owner and accounted) belongs to. + // If not we stop + if (empty(array_intersect($accountedMemberships, $ownerMemberships))) { + return true; + } + } + } + + return false; + } } diff --git a/lib/Service/SyncService.php b/lib/Service/SyncService.php index 0fb12f435..acee0afa5 100644 --- a/lib/Service/SyncService.php +++ b/lib/Service/SyncService.php @@ -55,6 +55,7 @@ use OCA\Circles\Exceptions\RequestBuilderException; use OCA\Circles\Exceptions\SingleCircleNotFoundException; use OCA\Circles\Exceptions\UnknownRemoteException; +use OCA\Circles\FederatedItems\MemberRemove; use OCA\Circles\FederatedItems\SingleMemberAdd; use OCA\Circles\Model\Circle; use OCA\Circles\Model\Federated\FederatedEvent; @@ -63,6 +64,7 @@ use OCA\Circles\Model\Member; use OCP\IGroupManager; use OCP\IUserManager; +use OCP\Share\IManager; /** * Class SyncService @@ -89,6 +91,8 @@ class SyncService { /** @var IGroupManager */ private $groupManager; + /** @var IManager */ + private $shareManager; /** @var CircleRequest */ private $circleRequest; @@ -99,6 +103,9 @@ class SyncService { /** @var FederatedUserService */ private $federatedUserService; + /** @var MemberService */ + private $memberService; + /** @var federatedEventService */ private $federatedEventService; @@ -120,6 +127,7 @@ class SyncService { * * @param IUserManager $userManager * @param IGroupManager $groupManager + * @param IManager $shareManager * @param CircleRequest $circleRequest * @param MemberRequest $memberRequest * @param FederatedUserService $federatedUserService @@ -132,8 +140,10 @@ class SyncService { public function __construct( IUserManager $userManager, IGroupManager $groupManager, + IManager $shareManager, CircleRequest $circleRequest, MemberRequest $memberRequest, + MemberService $memberService, FederatedUserService $federatedUserService, federatedEventService $federatedEventService, CircleService $circleService, @@ -143,8 +153,10 @@ public function __construct( ) { $this->userManager = $userManager; $this->groupManager = $groupManager; + $this->shareManager = $shareManager; $this->circleRequest = $circleRequest; $this->memberRequest = $memberRequest; + $this->memberService = $memberService; $this->federatedUserService = $federatedUserService; $this->federatedEventService = $federatedEventService; $this->circleService = $circleService; @@ -299,6 +311,7 @@ public function syncNextcloudGroup(string $groupId): Circle { $event = new FederatedEvent(SingleMemberAdd::class); $event->setCircle($circle); $event->setMember($member); + $event->getParams()->sBool('force_group_member', true); try { $this->federatedEventService->newEvent($event); @@ -451,6 +464,7 @@ public function groupMemberAdded(string $groupId, string $userId): void { $event = new FederatedEvent(SingleMemberAdd::class); $event->setCircle($circle); $event->setMember($member); + $event->getParams()->sBool('force_group_member', true); $this->federatedEventService->newEvent($event); // $this->memberRequest->insertOrUpdate($member); @@ -482,8 +496,41 @@ public function groupMemberRemoved(string $groupId, string $userId): void { $circle = $this->federatedUserService->getGroupCircle($groupId); $federatedUser = $this->federatedUserService->getLocalFederatedUser($userId); - $this->memberRequest->deleteFederatedUserFromCircle($federatedUser, $circle); - $this->membershipService->onUpdate($federatedUser->getSingleId()); + $member = $this->memberRequest->getMember($circle->getSingleId(), $federatedUser->getSingleId()); + $event = new FederatedEvent(MemberRemove::class); + $event->setCircle($member->getCircle()); + $event->setMember($member); + + $this->maintainGroupMemberOnly($federatedUser); + } + + + /** + * in case of limitation shareGroupMembersOnly, clean membership on existing Circles related to + * the removed group member. + * + * @param FederatedUser $federatedUser + * + * @throws RequestBuilderException + */ + private function maintainGroupMemberOnly(FederatedUser $federatedUser): void { + if (!$this->shareManager->shareWithGroupMembersOnly()) { + return; + } + + foreach ($federatedUser->getMemberships() as $membership) { + try { + $circle = $this->circleRequest->getCircle($membership->getCircleId()); + } catch (CircleNotFoundException $e) { + continue; + } + + if (!$this->membershipService->limitToGroupMembersOnly($federatedUser, $circle)) { + continue; + } + + echo 'NO NON ' . json_encode($circle) . "\n"; + } } diff --git a/lib/StatusCode.php b/lib/StatusCode.php index 948661d6d..c525ac3fe 100644 --- a/lib/StatusCode.php +++ b/lib/StatusCode.php @@ -49,7 +49,8 @@ class StatusCode { 121 => 'Circle is full', 122 => 'You are already a member', 123 => 'Already requesting to join the circle', - 124 => 'Circle is closed' + 124 => 'Circle is closed', + 125 => 'You must belongs to one of the Nextcloud Groups from Circle\'s owner' ]; public static $CIRCLE_LEAVE = [ @@ -69,7 +70,8 @@ class StatusCode { 129 => 'Member does not contains a patron', 130 => 'Member is invited by an entity that does not belongs to the instance at the origin of the request', 131 => 'Member is a non-local Circle', - 132 => 'Member type not allowed' + 132 => 'Member type not allowed', + 133 => 'Member must belongs to one of the Nextcloud Groups from Circle\'s owner' ]; public static $CIRCLE_DESTROY = [