From defd0f8e2e196ca324854f23d2692a556381cef0 Mon Sep 17 00:00:00 2001
From: Sebastien Marinier
diff --git a/lib/private/Collaboration/Collaborators/GroupPlugin.php b/lib/private/Collaboration/Collaborators/GroupPlugin.php index 75e52c19e0b27..8b18bf248e2a0 100644 --- a/lib/private/Collaboration/Collaborators/GroupPlugin.php +++ b/lib/private/Collaboration/Collaborators/GroupPlugin.php @@ -45,6 +45,8 @@ class GroupPlugin implements ISearchPlugin { protected $shareeEnumerationInGroupOnly; /** @var bool */ protected $groupSharingDisabled; + /** @var array */ + protected $shareWithGroupOnlyExcludeGroupsList; /** @var IGroupManager */ private $groupManager; @@ -62,6 +64,14 @@ public function __construct(IConfig $config, IGroupManager $groupManager, IUserS $this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes'; $this->shareeEnumerationInGroupOnly = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes'; $this->groupSharingDisabled = $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes') === 'no'; + + if ($this->shareWithGroupOnly) { + $shareWithGroupOnlyExcludeGroups = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members_exclude_group_list', ''); + $decodedExcludeGroups = json_decode($shareWithGroupOnlyExcludeGroups, true); + $this->shareWithGroupOnlyExcludeGroupsList = $decodedExcludeGroups ?? []; + } else { + $this->shareWithGroupOnlyExcludeGroupsList = []; + } } public function search($search, $limit, $offset, ISearchResult $searchResult) { @@ -89,6 +99,9 @@ public function search($search, $limit, $offset, ISearchResult $searchResult) { return $group->getGID(); }, $userGroups); $groupIds = array_intersect($groupIds, $userGroups); + + // ShareWithGroupOnly filtering + $groupIds = array_diff( $groupIds, $this->shareWithGroupOnlyExcludeGroupsList); } $lowerSearch = strtolower($search); diff --git a/lib/private/Collaboration/Collaborators/MailPlugin.php b/lib/private/Collaboration/Collaborators/MailPlugin.php index aa317ec17204f..f20b5cebba0ed 100644 --- a/lib/private/Collaboration/Collaborators/MailPlugin.php +++ b/lib/private/Collaboration/Collaborators/MailPlugin.php @@ -53,6 +53,8 @@ class MailPlugin implements ISearchPlugin { protected $shareeEnumerationFullMatch; /* @var bool */ protected $shareeEnumerationFullMatchEmail; + /** @var array */ + protected $shareWithGroupOnlyExcludeGroupsList; /** @var IManager */ private $contactsManager; @@ -91,6 +93,14 @@ public function __construct(IManager $contactsManager, $this->shareeEnumerationPhone = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes'; $this->shareeEnumerationFullMatch = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes') === 'yes'; $this->shareeEnumerationFullMatchEmail = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_email', 'yes') === 'yes'; + + if ($this->shareWithGroupOnly) { + $shareWithGroupOnlyExcludeGroups = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members_exclude_group_list', ''); + $decodedExcludeGroups = json_decode($shareWithGroupOnlyExcludeGroups, true); + $this->shareWithGroupOnlyExcludeGroupsList = $decodedExcludeGroups ?? []; + } else { + $this->shareWithGroupOnlyExcludeGroupsList = []; + } } /** @@ -150,6 +160,10 @@ public function search($search, $limit, $offset, ISearchResult $searchResult) { * Check if the user may share with the user associated with the e-mail of the just found contact */ $userGroups = $this->groupManager->getUserGroupIds($this->userSession->getUser()); + + // ShareWithGroupOnly filtering + $userGroups = array_diff( $userGroups, $this->shareWithGroupOnlyExcludeGroupsList); + $found = false; foreach ($userGroups as $userGroup) { if ($this->groupManager->isInGroup($contact['UID'], $userGroup)) { diff --git a/lib/private/Collaboration/Collaborators/UserPlugin.php b/lib/private/Collaboration/Collaborators/UserPlugin.php index 9beecdaa6cbbf..726a7099e8a3f 100644 --- a/lib/private/Collaboration/Collaborators/UserPlugin.php +++ b/lib/private/Collaboration/Collaborators/UserPlugin.php @@ -60,6 +60,8 @@ class UserPlugin implements ISearchPlugin { protected $shareeEnumerationFullMatchEmail; /* @var bool */ protected $shareeEnumerationFullMatchIgnoreSecondDisplayName; + /** @var array */ + protected $shareWithGroupOnlyExcludeGroupsList; /** @var IConfig */ private $config; @@ -96,6 +98,14 @@ public function __construct(IConfig $config, $this->shareeEnumerationFullMatchUserId = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_userid', 'yes') === 'yes'; $this->shareeEnumerationFullMatchEmail = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_email', 'yes') === 'yes'; $this->shareeEnumerationFullMatchIgnoreSecondDisplayName = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_dn', 'no') === 'yes'; + + if ($this->shareWithGroupOnly) { + $shareWithGroupOnlyExcludeGroups = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members_exclude_group_list', ''); + $decodedExcludeGroups = json_decode($shareWithGroupOnlyExcludeGroups, true); + $this->shareWithGroupOnlyExcludeGroupsList = $decodedExcludeGroups ?? []; + } else { + $this->shareWithGroupOnlyExcludeGroupsList = []; + } } public function search($search, $limit, $offset, ISearchResult $searchResult) { @@ -105,6 +115,10 @@ public function search($search, $limit, $offset, ISearchResult $searchResult) { $currentUserId = $this->userSession->getUser()->getUID(); $currentUserGroups = $this->groupManager->getUserGroupIds($this->userSession->getUser()); + + // ShareWithGroupOnly filtering + $currentUserGroups = array_diff( $currentUserGroups, $this->shareWithGroupOnlyExcludeGroupsList); + if ($this->shareWithGroupOnly || $this->shareeEnumerationInGroupOnly) { // Search in all the groups this user is part of foreach ($currentUserGroups as $userGroupId) { diff --git a/lib/private/Contacts/ContactsMenu/ContactsStore.php b/lib/private/Contacts/ContactsMenu/ContactsStore.php index 3a75e924d24f0..367c9cf2a04cc 100644 --- a/lib/private/Contacts/ContactsMenu/ContactsStore.php +++ b/lib/private/Contacts/ContactsMenu/ContactsStore.php @@ -125,6 +125,9 @@ public function getContacts(IUser $user, ?string $filter, ?int $limit = null, ?i * 3. if the `shareapi_only_share_with_group_members` config option is * enabled it will filter all users which doesn't have a common group * with the current user. + * If enabled, the 'shareapi_only_share_with_group_members_exclude_group_list' + * config option may specify some groups excluded from the principle of + * belonging to the same group. * * @param IUser $self * @param Entry[] $entries @@ -160,6 +163,14 @@ private function filterContacts( } } + // ownGroupsOnly : some groups may be excluded + if ($ownGroupsOnly) { + $excludeGroupsFromOwnGroups = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members_exclude_group_list', ''); + $decodedExcludeGroupsFromOwnGroups = json_decode($excludeGroupsFromOwnGroups, true); + $excludeGroupsFromOwnGroupsList = $decodedExcludeGroupsFromOwnGroups ?? []; + $selfGroups = array_diff( $selfGroups, $excludeGroupsFromOwnGroupsList); + } + $selfUID = $self->getUID(); return array_values(array_filter($entries, function (IEntry $entry) use ($skipLocal, $ownGroupsOnly, $selfGroups, $selfUID, $disallowEnumeration, $restrictEnumerationGroup, $restrictEnumerationPhone, $allowEnumerationFullMatch, $filter) { diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php index 732bd5bb97d39..8a61c2467fb17 100644 --- a/lib/private/Share20/Manager.php +++ b/lib/private/Share20/Manager.php @@ -550,6 +550,11 @@ protected function userCreateChecks(IShare $share) { $this->groupManager->getUserGroupIds($sharedBy), $this->groupManager->getUserGroupIds($sharedWith) ); + + // optional excluded groups + $excludedGroups = $this->shareWithGroupMembersOnlyExcludedGroupsList(); + $groups = array_diff($groups, $excludedGroups); + if (empty($groups)) { $message_t = $this->l->t('Sharing is only allowed with group members'); throw new \Exception($message_t); @@ -610,7 +615,10 @@ protected function groupCreateChecks(IShare $share) { if ($this->shareWithGroupMembersOnly()) { $sharedBy = $this->userManager->get($share->getSharedBy()); $sharedWith = $this->groupManager->get($share->getSharedWith()); - if (is_null($sharedWith) || !$sharedWith->inGroup($sharedBy)) { + + // optional excluded groups + $excludedGroups = $this->shareWithGroupMembersOnlyExcludedGroupsList(); + if (is_null($sharedWith) || in_array($share->getSharedWith(), $excludedGroups) || !$sharedWith->inGroup($sharedBy)) { throw new \Exception('Sharing is only allowed within your own groups'); } } @@ -1940,6 +1948,22 @@ public function shareWithGroupMembersOnly() { return $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes'; } + /** + * If shareWithGroupMembersOnly is enabled, return an optional + * list of groups that must be excluded from the principle of + * belonging to the same group. + * + * @return array + */ + public function shareWithGroupMembersOnlyExcludedGroupsList() { + if (!$this->shareWithGroupMembersOnly()) { + return []; + } + $excludeGroups = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members_exclude_group_list', ''); + $decodedExcludeGroups = json_decode($excludeGroups, true); + return $decodedExcludeGroups ?? []; + } + /** * Check if users can share with groups * diff --git a/lib/public/Share/IManager.php b/lib/public/Share/IManager.php index 9ac224ed7ef30..f951cf8106ae3 100644 --- a/lib/public/Share/IManager.php +++ b/lib/public/Share/IManager.php @@ -415,6 +415,15 @@ public function shareApiLinkAllowPublicUpload(); */ public function shareWithGroupMembersOnly(); + /** + * If shareWithGroupMembersOnly is enabled, return an optional + * list of groups that must be excluded from the principle of + * belonging to the same group. + * @return array + * @since 27.0.0 + */ + public function shareWithGroupMembersOnlyExcludedGroupsList(); + /** * Check if users can share with groups * @return bool