Merge pull request #57416 from nextcloud/backport/56938/stable32

This commit is contained in:
Benjamin Gaussorgues 2026-01-08 08:55:16 +01:00 committed by GitHub
commit d9c8ea6dd6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 304 additions and 4 deletions

View file

@ -25,5 +25,7 @@ class FeatureContext implements Context, SnippetAcceptingContext {
$this->deleteServerConfig('bruteForce', 'whitelist_0');
$this->deleteServerConfig('bruteForce', 'whitelist_1');
$this->deleteServerConfig('bruteforcesettings', 'apply_allowlist_to_ratelimit');
$this->deleteServerConfig('core', 'shareapi_exclude_groups');
$this->deleteServerConfig('core', 'shareapi_exclude_groups_list');
}
}

View file

@ -22,5 +22,7 @@ class ShareesContext implements Context, SnippetAcceptingContext {
$this->deleteServerConfig('core', 'shareapi_only_share_with_group_members');
$this->deleteServerConfig('core', 'shareapi_allow_share_dialog_user_enumeration');
$this->deleteServerConfig('core', 'shareapi_allow_group_sharing');
$this->deleteServerConfig('core', 'shareapi_exclude_groups');
$this->deleteServerConfig('core', 'shareapi_exclude_groups_list');
}
}

View file

@ -71,6 +71,140 @@ Feature: contacts-menu
And searched contact "1" is named "Test name"
And searched contact "2" is named "user2"
Scenario: users can not be searched by display name when searcher belongs to a group excluded from sharing
Given user "user0" exists
And group "ExcludedGroup" exists
And user "user0" belongs to group "ExcludedGroup"
And parameter "shareapi_exclude_groups" of app "core" is set to "yes"
And parameter "shareapi_exclude_groups_list" of app "core" is set to "ExcludedGroup"
And user "user1" exists
And As an "admin"
And sending "PUT" to "/cloud/users/user1" with
| key | displayname |
| value | Test name |
When Logging in using web as "user0"
And searching for contacts matching with "test"
Then the list of searched contacts has "0" contacts
Scenario: users can not be searched by email when searcher belongs to a group excluded from sharing
Given user "user0" exists
And group "ExcludedGroup" exists
And user "user0" belongs to group "ExcludedGroup"
And parameter "shareapi_exclude_groups" of app "core" is set to "yes"
And parameter "shareapi_exclude_groups_list" of app "core" is set to "ExcludedGroup"
And user "user1" exists
And As an "admin"
And sending "PUT" to "/cloud/users/user1" with
| key | email |
| value | test@example.com |
When Logging in using web as "user0"
And searching for contacts matching with "test"
Then the list of searched contacts has "0" contacts
Scenario: users can be searched by display name when searcher belongs to both a group excluded from sharing and another group
Given user "user0" exists
And group "ExcludedGroup" exists
And user "user0" belongs to group "ExcludedGroup"
And group "AnotherGroup" exists
And user "user0" belongs to group "AnotherGroup"
And parameter "shareapi_exclude_groups" of app "core" is set to "yes"
And parameter "shareapi_exclude_groups_list" of app "core" is set to "ExcludedGroup"
And user "user1" exists
And As an "admin"
And sending "PUT" to "/cloud/users/user1" with
| key | displayname |
| value | Test name |
When Logging in using web as "user0"
And searching for contacts matching with "test"
Then the list of searched contacts has "1" contacts
And searched contact "0" is named "Test name"
Scenario: users can be searched by email when searcher belongs to both a group excluded from sharing and another group
Given user "user0" exists
And group "ExcludedGroup" exists
And user "user0" belongs to group "ExcludedGroup"
And group "AnotherGroup" exists
And user "user0" belongs to group "AnotherGroup"
And parameter "shareapi_exclude_groups" of app "core" is set to "yes"
And parameter "shareapi_exclude_groups_list" of app "core" is set to "ExcludedGroup"
And user "user1" exists
And As an "admin"
And sending "PUT" to "/cloud/users/user1" with
| key | email |
| value | test@example.com |
When Logging in using web as "user0"
And searching for contacts matching with "test"
Then the list of searched contacts has "1" contacts
And searched contact "0" is named "user1"
Scenario: users can not be searched by display name when searcher does not belong to a group allowed to share
Given user "user0" exists
And group "AllowedGroup" exists
And parameter "shareapi_exclude_groups" of app "core" is set to "allow"
And parameter "shareapi_exclude_groups_list" of app "core" is set to "AllowedGroup"
And user "user1" exists
And As an "admin"
And sending "PUT" to "/cloud/users/user1" with
| key | displayname |
| value | Test name |
When Logging in using web as "user0"
And searching for contacts matching with "test"
Then the list of searched contacts has "0" contacts
Scenario: users can not be searched by email when searcher does not belong to a group allowed to share
Given user "user0" exists
And group "AllowedGroup" exists
And parameter "shareapi_exclude_groups" of app "core" is set to "allow"
And parameter "shareapi_exclude_groups_list" of app "core" is set to "AllowedGroup"
And user "user1" exists
And As an "admin"
And sending "PUT" to "/cloud/users/user1" with
| key | email |
| value | test@example.com |
When Logging in using web as "user0"
And searching for contacts matching with "test"
Then the list of searched contacts has "0" contacts
Scenario: users can be searched by display name when searcher belongs to both a group allowed to share and another group
Given user "user0" exists
And group "AllowedGroup" exists
And user "user0" belongs to group "AllowedGroup"
And group "AnotherGroup" exists
And user "user0" belongs to group "AnotherGroup"
And parameter "shareapi_exclude_groups" of app "core" is set to "allow"
And parameter "shareapi_exclude_groups_list" of app "core" is set to "AllowedGroup"
And user "user1" exists
And As an "admin"
And sending "PUT" to "/cloud/users/user1" with
| key | displayname |
| value | Test name |
When Logging in using web as "user0"
And searching for contacts matching with "test"
Then the list of searched contacts has "1" contacts
And searched contact "0" is named "Test name"
Scenario: users can be searched by email when searcher belongs to both a group allowed to share and another group
Given user "user0" exists
And group "AllowedGroup" exists
And user "user0" belongs to group "AllowedGroup"
And group "AnotherGroup" exists
And user "user0" belongs to group "AnotherGroup"
And parameter "shareapi_exclude_groups" of app "core" is set to "allow"
And parameter "shareapi_exclude_groups_list" of app "core" is set to "AllowedGroup"
And user "user1" exists
And As an "admin"
And sending "PUT" to "/cloud/users/user1" with
| key | email |
| value | test@example.com |
When Logging in using web as "user0"
And searching for contacts matching with "test"
Then the list of searched contacts has "1" contacts
And searched contact "0" is named "user1"
Scenario: users can not be found by display name if visibility is private
Given user "user0" exists
And user "user1" exists

View file

@ -117,6 +117,81 @@ Feature: sharees
And "exact remotes" sharees returned is empty
And "remotes" sharees returned is empty
Scenario: Search when belonging to a group excluded from sharing
Given As an "test"
And parameter "shareapi_exclude_groups" of app "core" is set to "yes"
And parameter "shareapi_exclude_groups_list" of app "core" is set to "ShareeGroup"
When getting sharees for
| search | sharee |
| itemType | file |
Then the OCS status code should be "100"
And the HTTP status code should be "200"
And "exact users" sharees returned is empty
And "users" sharees returned is empty
And "exact groups" sharees returned is empty
And "groups" sharees returned is empty
And "exact remotes" sharees returned is empty
And "remotes" sharees returned is empty
Scenario: Search when belonging to both a group excluded from sharing and another group
Given As an "test"
And group "AnotherGroup" exists
And user "test" belongs to group "AnotherGroup"
And parameter "shareapi_exclude_groups" of app "core" is set to "yes"
And parameter "shareapi_exclude_groups_list" of app "core" is set to "ShareeGroup"
When getting sharees for
| search | sharee |
| itemType | file |
Then the OCS status code should be "100"
And the HTTP status code should be "200"
And "exact users" sharees returned is empty
And "users" sharees returned are
| Sharee1 | 0 | Sharee1 | Sharee1 |
| Sharee2 | 0 | Sharee2 | sharee2@system.com |
And "exact groups" sharees returned is empty
And "groups" sharees returned are
| ShareeGroup | 1 | ShareeGroup |
And "exact remotes" sharees returned is empty
And "remotes" sharees returned is empty
Scenario: Search when not belonging to a group allowed to share
Given As an "test"
And group "AnotherGroup" exists
And parameter "shareapi_exclude_groups" of app "core" is set to "allow"
And parameter "shareapi_exclude_groups_list" of app "core" is set to "AnotherGroup"
When getting sharees for
| search | sharee |
| itemType | file |
Then the OCS status code should be "100"
And the HTTP status code should be "200"
And "exact users" sharees returned is empty
And "users" sharees returned is empty
And "exact groups" sharees returned is empty
And "groups" sharees returned is empty
And "exact remotes" sharees returned is empty
And "remotes" sharees returned is empty
Scenario: Search when belonging to both a group allowed to share and another group
Given As an "test"
And group "AnotherGroup" exists
And user "test" belongs to group "AnotherGroup"
And parameter "shareapi_exclude_groups" of app "core" is set to "allow"
And parameter "shareapi_exclude_groups_list" of app "core" is set to "AnotherGroup"
When getting sharees for
| search | sharee |
| itemType | file |
Then the OCS status code should be "100"
And the HTTP status code should be "200"
And "exact users" sharees returned is empty
And "users" sharees returned are
| Sharee1 | 0 | Sharee1 | Sharee1 |
| Sharee2 | 0 | Sharee2 | sharee2@system.com |
And "exact groups" sharees returned is empty
And "groups" sharees returned are
| ShareeGroup | 1 | ShareeGroup |
And "exact remotes" sharees returned is empty
And "remotes" sharees returned is empty
Scenario: Search without exact match no iteration allowed
Given As an "test"
And parameter "shareapi_allow_share_dialog_user_enumeration" of app "core" is set to "no"

View file

@ -149,7 +149,7 @@ class ContactsStore implements IContactsStore {
* 1. if the `shareapi_allow_share_dialog_user_enumeration` config option is
* enabled it will filter all local users
* 2. if the `shareapi_exclude_groups` config option is enabled and the
* current user is in an excluded group it will filter all local users.
* current user is only in excluded groups it will filter all local users.
* 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.
@ -184,8 +184,8 @@ class ContactsStore implements IContactsStore {
$excludeGroupsList = $decodedExcludeGroups ?? [];
if ($excludeGroups != 'allow') {
if (count(array_intersect($excludeGroupsList, $selfGroups)) !== 0) {
// a group of the current user is excluded -> filter all local users
if (count($selfGroups) > 0 && count(array_diff($selfGroups, $excludeGroupsList)) === 0) {
// all the groups of the current user are excluded -> filter all local users
$skipLocal = true;
}
} else {

View file

@ -188,7 +188,94 @@ class ContactsStoreTest extends TestCase {
$this->assertEquals('https://photo', $entries[1]->getAvatar());
}
public function testGetContactsWhenUserIsInExcludeGroups(): void {
public static function dataGetContactsWhenUserIsInExcludeGroups(): array {
return [
['yes', '[]', [], ['user123', 'user12345']],
['yes', '["excludedGroup1"]', [], ['user123', 'user12345']],
['yes', '["excludedGroup1"]', ['anotherGroup1'], ['user123', 'user12345']],
['yes', '["excludedGroup1"]', ['anotherGroup1', 'anotherGroup2', 'anotherGroup3'], ['user123', 'user12345']],
['yes', '["excludedGroup1"]', ['excludedGroup1'], []],
['yes', '["excludedGroup1"]', ['anotherGroup1', 'excludedGroup1'], ['user123', 'user12345']],
['yes', '["excludedGroup1"]', ['excludedGroup1', 'anotherGroup1', 'anotherGroup2', 'anotherGroup3'], ['user123', 'user12345']],
['yes', '["excludedGroup1", "excludedGroup2", "excludedGroup3"]', [], ['user123', 'user12345']],
['yes', '["excludedGroup1", "excludedGroup2", "excludedGroup3"]', ['anotherGroup1'], ['user123', 'user12345']],
['yes', '["excludedGroup1", "excludedGroup2", "excludedGroup3"]', ['anotherGroup1', 'anotherGroup2', 'anotherGroup3'], ['user123', 'user12345']],
['yes', '["excludedGroup1", "excludedGroup2", "excludedGroup3"]', ['excludedGroup1'], []],
['yes', '["excludedGroup1", "excludedGroup2", "excludedGroup3"]', ['excludedGroup2'], []],
['yes', '["excludedGroup1", "excludedGroup2", "excludedGroup3"]', ['excludedGroup3'], []],
['yes', '["excludedGroup1", "excludedGroup2", "excludedGroup3"]', ['excludedGroup1', 'excludedGroup2', 'excludedGroup3'], []],
['yes', '["excludedGroup1", "excludedGroup2", "excludedGroup3"]', ['anotherGroup1', 'excludedGroup1'], ['user123', 'user12345']],
['yes', '["excludedGroup1", "excludedGroup2", "excludedGroup3"]', ['anotherGroup1', 'excludedGroup2', 'anotherGroup2', 'anotherGroup3'], ['user123', 'user12345']],
['yes', '["excludedGroup1", "excludedGroup2", "excludedGroup3"]', ['excludedGroup3', 'anotherGroup1', 'anotherGroup2', 'anotherGroup3'], ['user123', 'user12345']],
['allow', '[]', [], []],
['allow', '["allowedGroup1"]', [], []],
['allow', '["allowedGroup1"]', ['anotherGroup1'], []],
['allow', '["allowedGroup1"]', ['anotherGroup1', 'anotherGroup2', 'anotherGroup3'], []],
['allow', '["allowedGroup1"]', ['allowedGroup1'], ['user123', 'user12345']],
['allow', '["allowedGroup1"]', ['anotherGroup1', 'allowedGroup1'], ['user123', 'user12345']],
['allow', '["allowedGroup1"]', ['allowedGroup1', 'anotherGroup1', 'anotherGroup2', 'anotherGroup3'], ['user123', 'user12345']],
['allow', '["allowedGroup1", "allowedGroup2", "allowedGroup3"]', [], []],
['allow', '["allowedGroup1", "allowedGroup2", "allowedGroup3"]', ['anotherGroup1'], []],
['allow', '["allowedGroup1", "allowedGroup2", "allowedGroup3"]', ['anotherGroup1', 'anotherGroup2', 'anotherGroup3'], []],
['allow', '["allowedGroup1", "allowedGroup2", "allowedGroup3"]', ['allowedGroup1'], ['user123', 'user12345']],
['allow', '["allowedGroup1", "allowedGroup2", "allowedGroup3"]', ['allowedGroup2'], ['user123', 'user12345']],
['allow', '["allowedGroup1", "allowedGroup2", "allowedGroup3"]', ['allowedGroup3'], ['user123', 'user12345']],
['allow', '["allowedGroup1", "allowedGroup2", "allowedGroup3"]', ['allowedGroup1', 'allowedGroup2', 'allowedGroup3'], ['user123', 'user12345']],
['allow', '["allowedGroup1", "allowedGroup2", "allowedGroup3"]', ['anotherGroup1', 'allowedGroup1'], ['user123', 'user12345']],
['allow', '["allowedGroup1", "allowedGroup2", "allowedGroup3"]', ['anotherGroup1', 'allowedGroup2', 'anotherGroup2', 'anotherGroup3'], ['user123', 'user12345']],
['allow', '["allowedGroup1", "allowedGroup2", "allowedGroup3"]', ['allowedGroup3', 'anotherGroup1', 'anotherGroup2', 'anotherGroup3'], ['user123', 'user12345']],
];
}
#[\PHPUnit\Framework\Attributes\DataProvider('dataGetContactsWhenUserIsInExcludeGroups')]
public function testGetContactsWhenUserIsInExcludeGroups(string $excludeGroups, string $excludeGroupsList, array $currentUserGroupIds, array $expectedUids): void {
$this->config
->method('getAppValue')
->willReturnMap([
['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
['core', 'shareapi_exclude_groups', 'no', $excludeGroups],
['core', 'shareapi_only_share_with_group_members', 'no', 'no'],
['core', 'shareapi_exclude_groups_list', '', $excludeGroupsList],
['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
]);
/** @var IUser|MockObject $currentUser */
$currentUser = $this->createMock(IUser::class);
$currentUser->expects($this->exactly(2))
->method('getUID')
->willReturn('user001');
$this->groupManager->expects($this->once())
->method('getUserGroupIds')
->with($this->equalTo($currentUser))
->willReturn($currentUserGroupIds);
$this->contactsManager->expects($this->once())
->method('search')
->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
->willReturn([
[
'UID' => 'user123',
'isLocalSystemBook' => true
],
[
'UID' => 'user12345',
'isLocalSystemBook' => true
],
]);
$entries = $this->contactsStore->getContacts($currentUser, '');
$this->assertCount(count($expectedUids), $entries);
for ($i = 0; $i < count($expectedUids); $i++) {
$this->assertEquals($expectedUids[$i], $entries[$i]->getProperty('UID'));
}
}
public function testGetContactsOnlyShareIfInTheSameGroupWhenUserIsInExcludeGroups(): void {
$this->config
->method('getAppValue')
->willReturnMap([