diff --git a/build/integration/collaboration_features/autocomplete.feature b/build/integration/collaboration_features/autocomplete.feature
index 5044758a70f..763b95bf170 100644
--- a/build/integration/collaboration_features/autocomplete.feature
+++ b/build/integration/collaboration_features/autocomplete.feature
@@ -41,28 +41,88 @@ Feature: autocomplete
Then get autocomplete for "autocomplete"
| id | source |
+ Scenario: getting autocomplete from address book with enumeration
+ Given As an "admin"
+ And sending "PUT" to "/cloud/users/autocomplete" with
+ | key | email |
+ | value | autocomplete@example.com |
+ And there is a contact in an addressbook
+ When parameter "shareapi_restrict_user_enumeration_full_match" of app "core" is set to "no"
+ Then get autocomplete for "auto"
+ | id | source |
+ | auto | users |
+ | autocomplete | users |
+ | autocomplete2 | users |
+ Then get autocomplete for "example"
+ | id | source |
+ | autocomplete | users |
+ Then get autocomplete for "autocomplete@example.com"
+ | id | source |
+ | autocomplete | users |
+ When parameter "shareapi_restrict_user_enumeration_full_match" of app "core" is set to "yes"
+ Then get autocomplete for "auto"
+ | id | source |
+ | auto | users |
+ | autocomplete | users |
+ | autocomplete2 | users |
+ Then get autocomplete for "example"
+ | id | source |
+ | autocomplete | users |
+ Then get autocomplete for "autocomplete@example.com"
+ | id | source |
+ | autocomplete | users |
+ | autocomplete | users |
+
+ Scenario: getting autocomplete from address book without enumeration
+ Given As an "admin"
+ And sending "PUT" to "/cloud/users/autocomplete" with
+ | key | email |
+ | value | autocomplete@example.com |
+ And there is a contact in an addressbook
+ And parameter "shareapi_allow_share_dialog_user_enumeration" of app "core" is set to "no"
+ When parameter "shareapi_restrict_user_enumeration_full_match" of app "core" is set to "no"
+ Then get autocomplete for "auto"
+ | id | source |
+ Then get autocomplete for "example"
+ | id | source |
+ Then get autocomplete for "autocomplete@example.com"
+ | id | source |
+ When parameter "shareapi_restrict_user_enumeration_full_match" of app "core" is set to "yes"
+ Then get autocomplete for "auto"
+ | id | source |
+ | auto | users |
+ Then get autocomplete for "example"
+ | id | source |
+ Then get autocomplete for "autocomplete@example.com"
+ | id | source |
+ | autocomplete | users |
+ | autocomplete | users |
+
Scenario: getting autocomplete emails from address book with enumeration
Given As an "admin"
And sending "PUT" to "/cloud/users/autocomplete" with
| key | email |
| value | autocomplete@example.com |
And there is a contact in an addressbook
+ When parameter "shareapi_restrict_user_enumeration_full_match" of app "core" is set to "no"
+ Then get email autocomplete for "auto"
+ | id | source |
Then get email autocomplete for "example"
| id | source |
- | autocomplete | users |
| leon@example.com | emails |
| user@example.com | emails |
+ Then get email autocomplete for "autocomplete@example.com"
+ | id | source |
+ | autocomplete@example.com | emails |
+ When parameter "shareapi_restrict_user_enumeration_full_match" of app "core" is set to "yes"
Then get email autocomplete for "auto"
| id | source |
- | autocomplete | users |
Then get email autocomplete for "example"
| id | source |
- | autocomplete | users |
| leon@example.com | emails |
| user@example.com | emails |
Then get email autocomplete for "autocomplete@example.com"
| id | source |
- | autocomplete | users |
Scenario: getting autocomplete emails from address book without enumeration
Given As an "admin"
@@ -72,10 +132,15 @@ Feature: autocomplete
And there is a contact in an addressbook
And parameter "shareapi_allow_share_dialog_user_enumeration" of app "core" is set to "no"
When parameter "shareapi_restrict_user_enumeration_full_match" of app "core" is set to "no"
+ Then get email autocomplete for "auto"
+ | id | source |
Then get email autocomplete for "example"
| id | source |
| leon@example.com | emails |
| user@example.com | emails |
+ Then get email autocomplete for "autocomplete@example.com"
+ | id | source |
+ | autocomplete@example.com | emails |
When parameter "shareapi_restrict_user_enumeration_full_match" of app "core" is set to "yes"
Then get email autocomplete for "auto"
| id | source |
@@ -85,7 +150,6 @@ Feature: autocomplete
| user@example.com | emails |
Then get email autocomplete for "autocomplete@example.com"
| id | source |
- | autocomplete | users |
Scenario: getting autocomplete with limited enumeration by group
Given As an "admin"
diff --git a/build/integration/features/bootstrap/Provisioning.php b/build/integration/features/bootstrap/Provisioning.php
index 935ad2a4a1d..16fb9e297c7 100644
--- a/build/integration/features/bootstrap/Provisioning.php
+++ b/build/integration/features/bootstrap/Provisioning.php
@@ -16,6 +16,12 @@ require __DIR__ . '/../../vendor/autoload.php';
trait Provisioning {
use BasicStructure;
+ /** @var array */
+ private $appsToEnableAfterScenario = [];
+
+ /** @var array */
+ private $appsToDisableAfterScenario = [];
+
/** @var array */
private $createdUsers = [];
@@ -28,6 +34,19 @@ trait Provisioning {
/** @var array */
private $createdGroups = [];
+ /** @AfterScenario */
+ public function restoreAppsEnabledStateAfterScenario() {
+ $this->asAn('admin');
+
+ foreach ($this->appsToEnableAfterScenario as $app) {
+ $this->sendingTo('POST', '/cloud/apps/' . $app);
+ }
+
+ foreach ($this->appsToDisableAfterScenario as $app) {
+ $this->sendingTo('DELETE', '/cloud/apps/' . $app);
+ }
+ }
+
/**
* @Given /^user "([^"]*)" exists$/
* @param string $user
@@ -803,13 +822,23 @@ trait Provisioning {
return $extractedElementsArray;
}
-
/**
- * @Given /^app "([^"]*)" is disabled$/
+ * @Given /^app "([^"]*)" enabled state will be restored once the scenario finishes$/
* @param string $app
*/
- public function appIsDisabled($app) {
- $fullUrl = $this->baseUrl . 'v2.php/cloud/apps?filter=disabled';
+ public function appEnabledStateWillBeRestoredOnceTheScenarioFinishes($app) {
+ if (in_array($app, $this->getAppsWithFilter('enabled'))) {
+ $this->appsToEnableAfterScenario[] = $app;
+ } elseif (in_array($app, $this->getAppsWithFilter('disabled'))) {
+ $this->appsToDisableAfterScenario[] = $app;
+ }
+
+ // Apps that were not installed before the scenario will not be
+ // disabled nor uninstalled after the scenario.
+ }
+
+ private function getAppsWithFilter($filter) {
+ $fullUrl = $this->baseUrl . 'v2.php/cloud/apps?filter=' . $filter;
$client = new Client();
$options = [];
if ($this->currentUser === 'admin') {
@@ -820,7 +849,15 @@ trait Provisioning {
];
$this->response = $client->get($fullUrl, $options);
- $respondedArray = $this->getArrayOfAppsResponded($this->response);
+ return $this->getArrayOfAppsResponded($this->response);
+ }
+
+ /**
+ * @Given /^app "([^"]*)" is disabled$/
+ * @param string $app
+ */
+ public function appIsDisabled($app) {
+ $respondedArray = $this->getAppsWithFilter('disabled');
Assert::assertContains($app, $respondedArray);
Assert::assertEquals(200, $this->response->getStatusCode());
}
@@ -830,18 +867,7 @@ trait Provisioning {
* @param string $app
*/
public function appIsEnabled($app) {
- $fullUrl = $this->baseUrl . 'v2.php/cloud/apps?filter=enabled';
- $client = new Client();
- $options = [];
- if ($this->currentUser === 'admin') {
- $options['auth'] = $this->adminUser;
- }
- $options['headers'] = [
- 'OCS-APIREQUEST' => 'true',
- ];
-
- $this->response = $client->get($fullUrl, $options);
- $respondedArray = $this->getArrayOfAppsResponded($this->response);
+ $respondedArray = $this->getAppsWithFilter('enabled');
Assert::assertContains($app, $respondedArray);
Assert::assertEquals(200, $this->response->getStatusCode());
}
@@ -854,18 +880,7 @@ trait Provisioning {
* @param string $app
*/
public function appIsNotEnabled($app) {
- $fullUrl = $this->baseUrl . 'v2.php/cloud/apps?filter=enabled';
- $client = new Client();
- $options = [];
- if ($this->currentUser === 'admin') {
- $options['auth'] = $this->adminUser;
- }
- $options['headers'] = [
- 'OCS-APIREQUEST' => 'true',
- ];
-
- $this->response = $client->get($fullUrl, $options);
- $respondedArray = $this->getArrayOfAppsResponded($this->response);
+ $respondedArray = $this->getAppsWithFilter('enabled');
Assert::assertNotContains($app, $respondedArray);
Assert::assertEquals(200, $this->response->getStatusCode());
}
diff --git a/build/integration/features/bootstrap/Sharing.php b/build/integration/features/bootstrap/Sharing.php
index 0cc490ff110..d93f114f27b 100644
--- a/build/integration/features/bootstrap/Sharing.php
+++ b/build/integration/features/bootstrap/Sharing.php
@@ -697,7 +697,13 @@ trait Sharing {
if ($body instanceof TableNode) {
$parameters = [];
foreach ($body->getRowsHash() as $key => $value) {
- $parameters[] = $key . '=' . $value;
+ if ($key === 'shareTypes') {
+ foreach (explode(' ', $value) as $shareType) {
+ $parameters[] = 'shareType[]=' . $shareType;
+ }
+ } else {
+ $parameters[] = $key . '=' . $value;
+ }
}
if (!empty($parameters)) {
$url .= '?' . implode('&', $parameters);
@@ -732,9 +738,46 @@ trait Sharing {
$shareeType = substr($shareeType, 6);
}
+ // "simplexml_load_string" creates a SimpleXMLElement object for each
+ // XML element with child elements. In turn, each child is indexed by
+ // its tag in the SimpleXMLElement object. However, when there are
+ // several child XML elements with the same tag, an array with all the
+ // children with the same tag is indexed instead. Therefore, when the
+ // XML contains
+ //
+ //
+ //
+ // ...
+ //
+ //
+ // the "$elements[$shareeType]" variable contains an "element" key which
+ // in turn contains "label" and "value" keys, but when the XML contains
+ //
+ //
+ //
+ // ...
+ //
+ //
+ //
+ // ...
+ //
+ //
+ // the "$elements[$shareeType]" variable contains an "element" key which
+ // in turn contains "0" and "1" keys, and in turn each one contains
+ // "label" and "value" keys.
+ if (array_key_exists('element', $elements[$shareeType]) && is_int(array_keys($elements[$shareeType]['element'])[0])) {
+ $elements[$shareeType] = $elements[$shareeType]['element'];
+ }
+
$sharees = [];
foreach ($elements[$shareeType] as $element) {
- $sharees[] = [$element['label'], $element['value']['shareType'], $element['value']['shareWith']];
+ $sharee = [$element['label'], $element['value']['shareType'], $element['value']['shareWith']];
+
+ if (array_key_exists('shareWithDisplayNameUnique', $element)) {
+ $sharee[] = $element['shareWithDisplayNameUnique'];
+ }
+
+ $sharees[] = $sharee;
}
return $sharees;
}
diff --git a/build/integration/openldap_features/openldap-uid-username.feature b/build/integration/openldap_features/openldap-uid-username.feature
index bee4098972b..02336b05128 100644
--- a/build/integration/openldap_features/openldap-uid-username.feature
+++ b/build/integration/openldap_features/openldap-uid-username.feature
@@ -161,5 +161,5 @@ Feature: LDAP
And the HTTP status code should be "200"
And "exact users" sharees returned is empty
And "users" sharees returned are
- | Elisa | 0 | elisa |
+ | Elisa | 0 | elisa | elisa@nextcloud.ci |
And "exact groups" sharees returned is empty
diff --git a/build/integration/sharees_features/sharees.feature b/build/integration/sharees_features/sharees.feature
index 9480ef997ae..4ff6d70cc53 100644
--- a/build/integration/sharees_features/sharees.feature
+++ b/build/integration/sharees_features/sharees.feature
@@ -8,6 +8,14 @@ Feature: sharees
And user "Sharee1" exists
And group "ShareeGroup" exists
And user "test" belongs to group "ShareeGroup"
+ And user "Sharee2" exists
+ And As an "admin"
+ And sending "PUT" to "/cloud/users/Sharee2" with
+ | key | email |
+ | value | sharee2@system.com |
+ And sending "PUT" to "/cloud/users/Sharee2" with
+ | key | additional_mail |
+ | value | sharee2@secondary.com |
Scenario: Search without exact match
Given As an "test"
@@ -18,7 +26,8 @@ Feature: sharees
And the HTTP status code should be "200"
And "exact users" sharees returned is empty
And "users" sharees returned are
- | Sharee1 | 0 | Sharee1 |
+ | 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 |
@@ -34,7 +43,8 @@ Feature: sharees
And the HTTP status code should be "200"
And "exact users" sharees returned is empty
And "users" sharees returned are
- | Sharee1 | 0 | Sharee1 |
+ | 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 |
@@ -68,7 +78,7 @@ Feature: sharees
And the HTTP status code should be "200"
And "exact users" sharees returned is empty
And "users" sharees returned are
- | Sharee1 | 0 | Sharee1 |
+ | Sharee1 | 0 | Sharee1 | Sharee1 |
And "exact groups" sharees returned is empty
And "groups" sharees returned are
| ShareeGroup | 1 | ShareeGroup |
@@ -85,7 +95,7 @@ Feature: sharees
Then the OCS status code should be "100"
And the HTTP status code should be "200"
And "exact users" sharees returned are
- | Sharee1 | 0 | Sharee1 |
+ | Sharee1 | 0 | Sharee1 | Sharee1 |
And "users" sharees returned is empty
And "exact groups" sharees returned is empty
And "groups" sharees returned is empty
@@ -131,7 +141,7 @@ Feature: sharees
Then the OCS status code should be "100"
And the HTTP status code should be "200"
And "exact users" sharees returned are
- | Sharee1 | 0 | Sharee1 |
+ | Sharee1 | 0 | Sharee1 | Sharee1 |
And "users" sharees returned is empty
And "exact groups" sharees returned is empty
And "groups" sharees returned is empty
@@ -162,7 +172,7 @@ Feature: sharees
Then the OCS status code should be "100"
And the HTTP status code should be "200"
Then "exact users" sharees returned are
- | Sharee1 | 0 | Sharee1 |
+ | Sharee1 | 0 | Sharee1 | Sharee1 |
Then "users" sharees returned is empty
Then "exact groups" sharees returned is empty
Then "groups" sharees returned is empty
@@ -177,7 +187,7 @@ Feature: sharees
Then the OCS status code should be "100"
And the HTTP status code should be "200"
Then "exact users" sharees returned are
- | Sharee1 | 0 | Sharee1 |
+ | Sharee1 | 0 | Sharee1 | Sharee1 |
Then "users" sharees returned is empty
Then "exact groups" sharees returned is empty
Then "groups" sharees returned is empty
@@ -207,7 +217,7 @@ Feature: sharees
Then the OCS status code should be "100"
And the HTTP status code should be "200"
Then "exact users" sharees returned are
- | Sharee1 | 0 | Sharee1 |
+ | Sharee1 | 0 | Sharee1 | Sharee1 |
Then "users" sharees returned is empty
Then "exact groups" sharees returned is empty
Then "groups" sharees returned is empty
@@ -254,8 +264,285 @@ Feature: sharees
And the HTTP status code should be "200"
And "exact users" sharees returned is empty
And "users" sharees returned are
- | Sharee1 | 0 | Sharee1 |
+ | Sharee1 | 0 | Sharee1 | Sharee1 |
+ | Sharee2 | 0 | Sharee2 | sharee2@system.com |
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 user by system e-mail address
+ Given As an "test"
+ When getting sharees for
+ | search | sharee2@system.com |
+ | itemType | file |
+ | shareType | 0 |
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ # UserPlugin provides two identical results (except for the field order, but
+ # that is hidden by the check).
+ # MailPlugin does not add a result if there is already one for that user.
+ And "exact users" sharees returned are
+ | Sharee2 | 0 | Sharee2 | sharee2@system.com |
+ | Sharee2 | 0 | Sharee2 | sharee2@system.com |
+ 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
+ And "exact emails" sharees returned is empty
+ And "emails" sharees returned is empty
+
+ Scenario: Search user by system e-mail address without exact match
+ Given As an "test"
+ When getting sharees for
+ | search | sharee2@system.c |
+ | itemType | file |
+ | shareType | 0 |
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And "exact users" sharees returned is empty
+ # MailPlugin does not add a result if there is already one for that user.
+ And "users" sharees returned are
+ | Sharee2 | 0 | Sharee2 | sharee2@system.com |
+ 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
+ And "exact emails" sharees returned is empty
+ And "emails" sharees returned is empty
+
+ Scenario: Search user by secondary e-mail address
+ Given As an "test"
+ When getting sharees for
+ | search | sharee2@secondary.com |
+ | itemType | file |
+ | shareType | 0 |
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ # UserPlugin only searches in the system e-mail address, but not in
+ # secondary addresses.
+ And "exact users" sharees returned are
+ | Sharee2 (sharee2@secondary.com) | 0 | Sharee2 | sharee2@secondary.com |
+ 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
+ And "exact emails" sharees returned is empty
+ And "emails" sharees returned is empty
+
+ Scenario: Search user by secondary e-mail address without exact match
+ Given As an "test"
+ When getting sharees for
+ | search | sharee2@secondary.c |
+ | itemType | file |
+ | shareType | 0 |
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And "exact users" sharees returned is empty
+ # UserPlugin only searches in the system e-mail address, but not in
+ # secondary addresses.
+ # MailPlugin adds a result for every e-mail address of the contact unless
+ # there is an exact match.
+ And "users" sharees returned are
+ | Sharee2 (sharee2@system.com) | 0 | Sharee2 | sharee2@system.com |
+ | Sharee2 (sharee2@secondary.com) | 0 | Sharee2 | sharee2@secondary.com |
+ 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
+ And "exact emails" sharees returned is empty
+ And "emails" sharees returned is empty
+
+ Scenario: Search e-mail
+ Given As an "test"
+ When getting sharees for
+ | search | sharee2@unknown.com |
+ | itemType | file |
+ | shareType | 4 |
+ 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
+ And "exact emails" sharees returned are
+ | sharee2@unknown.com | 4 | sharee2@unknown.com |
+ And "emails" sharees returned is empty
+
+ Scenario: Search e-mail when sharebymail app is disabled
+ Given app "sharebymail" enabled state will be restored once the scenario finishes
+ And sending "DELETE" to "/cloud/apps/sharebymail"
+ And app "sharebymail" is disabled
+ And As an "test"
+ When getting sharees for
+ | search | sharee2@unknown.com |
+ | itemType | file |
+ | shareType | 4 |
+ 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
+ And "exact emails" sharees returned is empty
+ And "emails" sharees returned is empty
+
+ Scenario: Search e-mail matching system e-mail address of user
+ Given As an "test"
+ When getting sharees for
+ | search | sharee2@system.com |
+ | itemType | file |
+ | shareType | 4 |
+ 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
+ And "exact emails" sharees returned is empty
+ And "emails" sharees returned is empty
+
+ Scenario: Search e-mail partially matching system e-mail address of user
+ Given As an "test"
+ When getting sharees for
+ | search | sharee2@system.c |
+ | itemType | file |
+ | shareType | 4 |
+ 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
+ And "exact emails" sharees returned are
+ | sharee2@system.c | 4 | sharee2@system.c |
+ And "emails" sharees returned is empty
+
+ Scenario: Search e-mail matching secondary e-mail address of user
+ Given As an "test"
+ When getting sharees for
+ | search | sharee2@secondary.com |
+ | itemType | file |
+ | shareType | 4 |
+ 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
+ And "exact emails" sharees returned is empty
+ And "emails" sharees returned is empty
+
+ Scenario: Search e-mail partially matching secondary e-mail address of user
+ Given As an "test"
+ When getting sharees for
+ | search | sharee2@secondary.c |
+ | itemType | file |
+ | shareType | 4 |
+ 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
+ And "exact emails" sharees returned are
+ | sharee2@secondary.c | 4 | sharee2@secondary.c |
+ And "emails" sharees returned is empty
+
+ Scenario: Search user and e-mail matching system e-mail address of user
+ Given As an "test"
+ When getting sharees for
+ | search | sharee2@system.com |
+ | itemType | file |
+ | shareTypes | 0 4 |
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ # UserPlugin provides two identical results (except for the field order, but
+ # that is hidden by the check)
+ And "exact users" sharees returned are
+ | Sharee2 | 0 | Sharee2 | sharee2@system.com |
+ | Sharee2 | 0 | Sharee2 | sharee2@system.com |
+ 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
+ And "exact emails" sharees returned is empty
+ And "emails" sharees returned is empty
+
+ Scenario: Search user and e-mail matching system e-mail address of user when sharebymail app is disabled
+ Given app "sharebymail" enabled state will be restored once the scenario finishes
+ And sending "DELETE" to "/cloud/apps/sharebymail"
+ And app "sharebymail" is disabled
+ And As an "test"
+ When getting sharees for
+ | search | sharee2@system.com |
+ | itemType | file |
+ | shareTypes | 0 4 |
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ # UserPlugin provides two identical results (except for the field order, but
+ # that is hidden by the check)
+ And "exact users" sharees returned are
+ | Sharee2 | 0 | Sharee2 | sharee2@system.com |
+ | Sharee2 | 0 | Sharee2 | sharee2@system.com |
+ 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
+ And "exact emails" sharees returned is empty
+ And "emails" sharees returned is empty
+
+ Scenario: Search user and e-mail matching secondary e-mail address of user
+ Given As an "test"
+ When getting sharees for
+ | search | sharee2@secondary.com |
+ | itemType | file |
+ | shareTypes | 0 4 |
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And "exact users" sharees returned are
+ | Sharee2 (sharee2@secondary.com) | 0 | Sharee2 | sharee2@secondary.com |
+ 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
+ And "exact emails" sharees returned is empty
+ And "emails" sharees returned is empty
+
+ Scenario: Search user and e-mail matching secondary e-mail address of user when sharebymail app is disabled
+ Given app "sharebymail" enabled state will be restored once the scenario finishes
+ And sending "DELETE" to "/cloud/apps/sharebymail"
+ And app "sharebymail" is disabled
+ And As an "test"
+ When getting sharees for
+ | search | sharee2@secondary.com |
+ | itemType | file |
+ | shareTypes | 0 4 |
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And "exact users" sharees returned are
+ | Sharee2 (sharee2@secondary.com) | 0 | Sharee2 | sharee2@secondary.com |
+ 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
+ And "exact emails" sharees returned is empty
+ And "emails" sharees returned is empty
diff --git a/build/integration/sharees_features/sharees_provisioningapiv2.feature b/build/integration/sharees_features/sharees_provisioningapiv2.feature
index b27bb7a4f21..c5a42f7b6a8 100644
--- a/build/integration/sharees_features/sharees_provisioningapiv2.feature
+++ b/build/integration/sharees_features/sharees_provisioningapiv2.feature
@@ -18,7 +18,7 @@ Feature: sharees_provisioningapiv2
And the HTTP status code should be "200"
And "exact users" sharees returned is empty
And "users" sharees returned are
- | Sharee1 | 0 | Sharee1 |
+ | Sharee1 | 0 | Sharee1 | Sharee1 |
And "exact groups" sharees returned is empty
And "groups" sharees returned are
| ShareeGroup | 1 | ShareeGroup |
@@ -34,7 +34,7 @@ Feature: sharees_provisioningapiv2
And the HTTP status code should be "200"
And "exact users" sharees returned is empty
And "users" sharees returned are
- | Sharee1 | 0 | Sharee1 |
+ | Sharee1 | 0 | Sharee1 | Sharee1 |
And "exact groups" sharees returned is empty
And "groups" sharees returned are
| ShareeGroup | 1 | ShareeGroup |
@@ -68,7 +68,7 @@ Feature: sharees_provisioningapiv2
And the HTTP status code should be "200"
And "exact users" sharees returned is empty
And "users" sharees returned are
- | Sharee1 | 0 | Sharee1 |
+ | Sharee1 | 0 | Sharee1 | Sharee1 |
And "exact groups" sharees returned is empty
And "groups" sharees returned are
| ShareeGroup | 1 | ShareeGroup |
@@ -114,7 +114,7 @@ Feature: sharees_provisioningapiv2
Then the OCS status code should be "200"
And the HTTP status code should be "200"
And "exact users" sharees returned are
- | Sharee1 | 0 | Sharee1 |
+ | Sharee1 | 0 | Sharee1 | Sharee1 |
And "users" sharees returned is empty
And "exact groups" sharees returned is empty
And "groups" sharees returned is empty
@@ -145,7 +145,7 @@ Feature: sharees_provisioningapiv2
Then the OCS status code should be "200"
And the HTTP status code should be "200"
Then "exact users" sharees returned are
- | Sharee1 | 0 | Sharee1 |
+ | Sharee1 | 0 | Sharee1 | Sharee1 |
Then "users" sharees returned is empty
Then "exact groups" sharees returned is empty
Then "groups" sharees returned is empty
@@ -160,7 +160,7 @@ Feature: sharees_provisioningapiv2
Then the OCS status code should be "200"
And the HTTP status code should be "200"
Then "exact users" sharees returned are
- | Sharee1 | 0 | Sharee1 |
+ | Sharee1 | 0 | Sharee1 | Sharee1 |
Then "users" sharees returned is empty
Then "exact groups" sharees returned is empty
Then "groups" sharees returned is empty
@@ -190,7 +190,7 @@ Feature: sharees_provisioningapiv2
Then the OCS status code should be "200"
And the HTTP status code should be "200"
Then "exact users" sharees returned are
- | Sharee1 | 0 | Sharee1 |
+ | Sharee1 | 0 | Sharee1 | Sharee1 |
Then "users" sharees returned is empty
Then "exact groups" sharees returned is empty
Then "groups" sharees returned is empty
@@ -237,7 +237,7 @@ Feature: sharees_provisioningapiv2
And the HTTP status code should be "200"
And "exact users" sharees returned is empty
And "users" sharees returned are
- | Sharee1 | 0 | Sharee1 |
+ | Sharee1 | 0 | Sharee1 | Sharee1 |
And "exact groups" sharees returned is empty
And "groups" sharees returned is empty
And "exact remotes" sharees returned is empty
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index 1810eab032c..16eb03b15cf 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -1202,11 +1202,13 @@ return array(
'OC\\Collaboration\\AutoComplete\\Manager' => $baseDir . '/lib/private/Collaboration/AutoComplete/Manager.php',
'OC\\Collaboration\\Collaborators\\GroupPlugin' => $baseDir . '/lib/private/Collaboration/Collaborators/GroupPlugin.php',
'OC\\Collaboration\\Collaborators\\LookupPlugin' => $baseDir . '/lib/private/Collaboration/Collaborators/LookupPlugin.php',
+ 'OC\\Collaboration\\Collaborators\\MailByMailPlugin' => $baseDir . '/lib/private/Collaboration/Collaborators/MailByMailPlugin.php',
'OC\\Collaboration\\Collaborators\\MailPlugin' => $baseDir . '/lib/private/Collaboration/Collaborators/MailPlugin.php',
'OC\\Collaboration\\Collaborators\\RemoteGroupPlugin' => $baseDir . '/lib/private/Collaboration/Collaborators/RemoteGroupPlugin.php',
'OC\\Collaboration\\Collaborators\\RemotePlugin' => $baseDir . '/lib/private/Collaboration/Collaborators/RemotePlugin.php',
'OC\\Collaboration\\Collaborators\\Search' => $baseDir . '/lib/private/Collaboration/Collaborators/Search.php',
'OC\\Collaboration\\Collaborators\\SearchResult' => $baseDir . '/lib/private/Collaboration/Collaborators/SearchResult.php',
+ 'OC\\Collaboration\\Collaborators\\UserByMailPlugin' => $baseDir . '/lib/private/Collaboration/Collaborators/UserByMailPlugin.php',
'OC\\Collaboration\\Collaborators\\UserPlugin' => $baseDir . '/lib/private/Collaboration/Collaborators/UserPlugin.php',
'OC\\Collaboration\\Reference\\File\\FileReferenceEventListener' => $baseDir . '/lib/private/Collaboration/Reference/File/FileReferenceEventListener.php',
'OC\\Collaboration\\Reference\\File\\FileReferenceProvider' => $baseDir . '/lib/private/Collaboration/Reference/File/FileReferenceProvider.php',
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index 753e56107d0..da38482d5ce 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -1243,11 +1243,13 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Collaboration\\AutoComplete\\Manager' => __DIR__ . '/../../..' . '/lib/private/Collaboration/AutoComplete/Manager.php',
'OC\\Collaboration\\Collaborators\\GroupPlugin' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Collaborators/GroupPlugin.php',
'OC\\Collaboration\\Collaborators\\LookupPlugin' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Collaborators/LookupPlugin.php',
+ 'OC\\Collaboration\\Collaborators\\MailByMailPlugin' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Collaborators/MailByMailPlugin.php',
'OC\\Collaboration\\Collaborators\\MailPlugin' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Collaborators/MailPlugin.php',
'OC\\Collaboration\\Collaborators\\RemoteGroupPlugin' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Collaborators/RemoteGroupPlugin.php',
'OC\\Collaboration\\Collaborators\\RemotePlugin' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Collaborators/RemotePlugin.php',
'OC\\Collaboration\\Collaborators\\Search' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Collaborators/Search.php',
'OC\\Collaboration\\Collaborators\\SearchResult' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Collaborators/SearchResult.php',
+ 'OC\\Collaboration\\Collaborators\\UserByMailPlugin' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Collaborators/UserByMailPlugin.php',
'OC\\Collaboration\\Collaborators\\UserPlugin' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Collaborators/UserPlugin.php',
'OC\\Collaboration\\Reference\\File\\FileReferenceEventListener' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Reference/File/FileReferenceEventListener.php',
'OC\\Collaboration\\Reference\\File\\FileReferenceProvider' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Reference/File/FileReferenceProvider.php',
diff --git a/lib/private/Collaboration/Collaborators/MailByMailPlugin.php b/lib/private/Collaboration/Collaborators/MailByMailPlugin.php
new file mode 100644
index 00000000000..53f90e33c57
--- /dev/null
+++ b/lib/private/Collaboration/Collaborators/MailByMailPlugin.php
@@ -0,0 +1,45 @@
+shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
$this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
@@ -139,7 +140,7 @@ class MailPlugin implements ISearchPlugin {
continue;
}
- if (!$this->isCurrentUser($cloud) && !$searchResult->hasResult($userType, $cloud->getUser())) {
+ if ($this->shareType === IShare::TYPE_USER && !$this->isCurrentUser($cloud) && !$searchResult->hasResult($userType, $cloud->getUser())) {
$singleResult = [[
'label' => $displayName,
'uuid' => $contact['UID'] ?? $emailAddress,
@@ -183,22 +184,28 @@ class MailPlugin implements ISearchPlugin {
}
}
if ($addToWide && !$this->isCurrentUser($cloud) && !$searchResult->hasResult($userType, $cloud->getUser())) {
- $userResults['wide'][] = [
- 'label' => $displayName,
- 'uuid' => $contact['UID'] ?? $emailAddress,
- 'name' => $contact['FN'] ?? $displayName,
- 'value' => [
- 'shareType' => IShare::TYPE_USER,
- 'shareWith' => $cloud->getUser(),
- ],
- 'shareWithDisplayNameUnique' => !empty($emailAddress) ? $emailAddress : $cloud->getUser()
- ];
+ if ($this->shareType === IShare::TYPE_USER) {
+ $userResults['wide'][] = [
+ 'label' => $displayName,
+ 'uuid' => $contact['UID'] ?? $emailAddress,
+ 'name' => $contact['FN'] ?? $displayName,
+ 'value' => [
+ 'shareType' => IShare::TYPE_USER,
+ 'shareWith' => $cloud->getUser(),
+ ],
+ 'shareWithDisplayNameUnique' => !empty($emailAddress) ? $emailAddress : $cloud->getUser()
+ ];
+ }
continue;
}
}
continue;
}
+ if ($this->shareType !== IShare::TYPE_EMAIL) {
+ continue;
+ }
+
if ($exactEmailMatch
|| (isset($contact['FN']) && strtolower($contact['FN']) === $lowerSearch)) {
if ($exactEmailMatch) {
@@ -239,7 +246,8 @@ class MailPlugin implements ISearchPlugin {
$userResults['wide'] = array_slice($userResults['wide'], $offset, $limit);
}
- if (!$searchResult->hasExactIdMatch($emailType) && $this->emailValidator->isValid($search)) {
+ if ($this->shareType === IShare::TYPE_EMAIL
+ && !$searchResult->hasExactIdMatch($emailType) && $this->emailValidator->isValid($search)) {
$result['exact'][] = [
'label' => $search,
'uuid' => $search,
@@ -250,10 +258,12 @@ class MailPlugin implements ISearchPlugin {
];
}
- if (!empty($userResults['wide'])) {
+ if ($this->shareType === IShare::TYPE_USER && !empty($userResults['wide'])) {
$searchResult->addResultSet($userType, $userResults['wide'], []);
}
- $searchResult->addResultSet($emailType, $result['wide'], $result['exact']);
+ if ($this->shareType === IShare::TYPE_EMAIL) {
+ $searchResult->addResultSet($emailType, $result['wide'], $result['exact']);
+ }
return !$reachedEnd;
}
diff --git a/lib/private/Collaboration/Collaborators/UserByMailPlugin.php b/lib/private/Collaboration/Collaborators/UserByMailPlugin.php
new file mode 100644
index 00000000000..44c1a95221c
--- /dev/null
+++ b/lib/private/Collaboration/Collaborators/UserByMailPlugin.php
@@ -0,0 +1,45 @@
+registerPlugin(['shareType' => 'SHARE_TYPE_USER', 'class' => UserPlugin::class]);
+ $instance->registerPlugin(['shareType' => 'SHARE_TYPE_USER', 'class' => UserByMailPlugin::class]);
$instance->registerPlugin(['shareType' => 'SHARE_TYPE_GROUP', 'class' => GroupPlugin::class]);
- $instance->registerPlugin(['shareType' => 'SHARE_TYPE_EMAIL', 'class' => MailPlugin::class]);
+ $instance->registerPlugin(['shareType' => 'SHARE_TYPE_EMAIL', 'class' => MailByMailPlugin::class]);
$instance->registerPlugin(['shareType' => 'SHARE_TYPE_REMOTE', 'class' => RemotePlugin::class]);
$instance->registerPlugin(['shareType' => 'SHARE_TYPE_REMOTE_GROUP', 'class' => RemoteGroupPlugin::class]);
diff --git a/tests/lib/Collaboration/Collaborators/MailPluginTest.php b/tests/lib/Collaboration/Collaborators/MailPluginTest.php
index 6c81a0d68e2..e6580854784 100644
--- a/tests/lib/Collaboration/Collaborators/MailPluginTest.php
+++ b/tests/lib/Collaboration/Collaborators/MailPluginTest.php
@@ -72,7 +72,7 @@ class MailPluginTest extends TestCase {
$this->searchResult = new SearchResult();
}
- public function instantiatePlugin() {
+ public function instantiatePlugin(int $shareType) {
$this->plugin = new MailPlugin(
$this->contactsManager,
$this->cloudIdManager,
@@ -81,6 +81,8 @@ class MailPluginTest extends TestCase {
$this->knownUserService,
$this->userSession,
$this->getEmailValidatorWithStrictEmailCheck(),
+ [],
+ $shareType,
);
}
@@ -89,11 +91,13 @@ class MailPluginTest extends TestCase {
* @param string $searchTerm
* @param array $contacts
* @param bool $shareeEnumeration
- * @param array $expected
- * @param bool $reachedEnd
+ * @param array $expectedResult
+ * @param bool $expectedExactIdMatch
+ * @param bool $expectedMoreResults
+ * @param bool $validEmail
*/
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetEmail')]
- public function testSearch($searchTerm, $contacts, $shareeEnumeration, $expected, $exactIdMatch, $reachedEnd, $validEmail): void {
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataSearchEmail')]
+ public function testSearchEmail($searchTerm, $contacts, $shareeEnumeration, $expectedResult, $expectedExactIdMatch, $expectedMoreResults, $validEmail): void {
$this->config->expects($this->any())
->method('getAppValue')
->willReturnCallback(
@@ -105,7 +109,7 @@ class MailPluginTest extends TestCase {
}
);
- $this->instantiatePlugin();
+ $this->instantiatePlugin(IShare::TYPE_EMAIL);
$currentUser = $this->createMock(IUser::class);
$currentUser->method('getUID')
@@ -125,12 +129,12 @@ class MailPluginTest extends TestCase {
$moreResults = $this->plugin->search($searchTerm, 2, 0, $this->searchResult);
$result = $this->searchResult->asArray();
- $this->assertSame($exactIdMatch, $this->searchResult->hasExactIdMatch(new SearchResultType('emails')));
- $this->assertEquals($expected, $result);
- $this->assertSame($reachedEnd, $moreResults);
+ $this->assertSame($expectedExactIdMatch, $this->searchResult->hasExactIdMatch(new SearchResultType('emails')));
+ $this->assertEquals($expectedResult, $result);
+ $this->assertSame($expectedMoreResults, $moreResults);
}
- public static function dataGetEmail(): array {
+ public static function dataSearchEmail(): array {
return [
// data set 0
['test', [], true, ['emails' => [], 'exact' => ['emails' => []]], false, false, false],
@@ -396,7 +400,7 @@ class MailPluginTest extends TestCase {
false,
],
// data set 13
- // Local user found by email
+ // Local user found by email => no result
[
'test@example.com',
[
@@ -409,8 +413,8 @@ class MailPluginTest extends TestCase {
]
],
false,
- ['users' => [], 'exact' => ['users' => [['uuid' => 'uid1', 'name' => 'User', 'label' => 'User (test@example.com)','value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test'], 'shareWithDisplayNameUnique' => 'test@example.com']]]],
- true,
+ ['exact' => []],
+ false,
false,
true,
],
@@ -434,7 +438,7 @@ class MailPluginTest extends TestCase {
true,
],
// data set 15
- // Pagination and "more results" for user matches byyyyyyy emails
+ // Several local users found by email => no result nor pagination
[
'test@example',
[
@@ -468,12 +472,9 @@ class MailPluginTest extends TestCase {
],
],
true,
- ['users' => [
- ['uuid' => 'uid1', 'name' => 'User1', 'label' => 'User1 (test@example.com)', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1'], 'shareWithDisplayNameUnique' => 'test@example.com'],
- ['uuid' => 'uid2', 'name' => 'User2', 'label' => 'User2 (test@example.de)', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test2'], 'shareWithDisplayNameUnique' => 'test@example.de'],
- ], 'emails' => [], 'exact' => ['users' => [], 'emails' => []]],
+ ['emails' => [], 'exact' => ['emails' => []]],
+ false,
false,
- true,
false,
],
// data set 16
@@ -567,13 +568,310 @@ class MailPluginTest extends TestCase {
*
* @param string $searchTerm
* @param array $contacts
- * @param array $expected
- * @param bool $exactIdMatch
- * @param bool $reachedEnd
- * @param array groups
+ * @param bool $shareeEnumeration
+ * @param array $expectedResult
+ * @param bool $expectedExactIdMatch
+ * @param bool $expectedMoreResults
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataSearchUser')]
+ public function testSearchUser($searchTerm, $contacts, $shareeEnumeration, $expectedResult, $expectedExactIdMatch, $expectedMoreResults): void {
+ $this->config->expects($this->any())
+ ->method('getAppValue')
+ ->willReturnCallback(
+ function ($appName, $key, $default) use ($shareeEnumeration) {
+ if ($appName === 'core' && $key === 'shareapi_allow_share_dialog_user_enumeration') {
+ return $shareeEnumeration ? 'yes' : 'no';
+ }
+ return $default;
+ }
+ );
+
+ $this->instantiatePlugin(IShare::TYPE_USER);
+
+ $currentUser = $this->createMock(IUser::class);
+ $currentUser->method('getUID')
+ ->willReturn('current');
+ $this->userSession->method('getUser')
+ ->willReturn($currentUser);
+
+ $this->contactsManager->expects($this->any())
+ ->method('search')
+ ->willReturnCallback(function ($search, $searchAttributes) use ($searchTerm, $contacts) {
+ if ($search === $searchTerm) {
+ return $contacts;
+ }
+ return [];
+ });
+
+ $moreResults = $this->plugin->search($searchTerm, 2, 0, $this->searchResult);
+ $result = $this->searchResult->asArray();
+
+ $this->assertSame($expectedExactIdMatch, $this->searchResult->hasExactIdMatch(new SearchResultType('emails')));
+ $this->assertEquals($expectedResult, $result);
+ $this->assertSame($expectedMoreResults, $moreResults);
+ }
+
+ public static function dataSearchUser(): array {
+ return [
+ // data set 0
+ ['test', [], true, ['exact' => []], false, false],
+ // data set 1
+ ['test', [], false, ['exact' => []], false, false],
+ // data set 2
+ [
+ 'test@remote.com',
+ [],
+ true,
+ ['exact' => []],
+ false,
+ false,
+ ],
+ // data set 3
+ [
+ 'test@remote.com',
+ [],
+ false,
+ ['exact' => []],
+ false,
+ false,
+ ],
+ // data set 4
+ [
+ 'test',
+ [
+ [
+ 'UID' => 'uid3',
+ 'FN' => 'User3 @ Localhost',
+ ],
+ [
+ 'UID' => 'uid2',
+ 'FN' => 'User2 @ Localhost',
+ 'EMAIL' => [
+ ],
+ ],
+ [
+ 'UID' => 'uid1',
+ 'FN' => 'User @ Localhost',
+ 'EMAIL' => [
+ 'username@localhost',
+ ],
+ ],
+ ],
+ true,
+ ['exact' => []],
+ false,
+ false,
+ ],
+ // data set 5
+ [
+ 'test',
+ [
+ [
+ 'UID' => 'uid3',
+ 'FN' => 'User3 @ Localhost',
+ ],
+ [
+ 'UID' => 'uid2',
+ 'FN' => 'User2 @ Localhost',
+ 'EMAIL' => [
+ ],
+ ],
+ [
+ 'isLocalSystemBook' => true,
+ 'UID' => 'uid1',
+ 'FN' => 'User @ Localhost',
+ 'EMAIL' => [
+ 'username@localhost',
+ ],
+ ],
+ ],
+ false,
+ ['exact' => []],
+ false,
+ false,
+ ],
+ // data set 6
+ [
+ 'test@remote.com',
+ [
+ [
+ 'UID' => 'uid3',
+ 'FN' => 'User3 @ Localhost',
+ ],
+ [
+ 'UID' => 'uid2',
+ 'FN' => 'User2 @ Localhost',
+ 'EMAIL' => [
+ ],
+ ],
+ [
+ 'UID' => 'uid1',
+ 'FN' => 'User @ Localhost',
+ 'EMAIL' => [
+ 'username@localhost',
+ ],
+ ],
+ ],
+ true,
+ ['exact' => []],
+ false,
+ false,
+ ],
+ // data set 7
+ [
+ 'username@localhost',
+ [
+ [
+ 'UID' => 'uid3',
+ 'FN' => 'User3 @ Localhost',
+ ],
+ [
+ 'UID' => 'uid2',
+ 'FN' => 'User2 @ Localhost',
+ 'EMAIL' => [
+ ],
+ ],
+ [
+ 'UID' => 'uid1',
+ 'FN' => 'User @ Localhost',
+ 'EMAIL' => [
+ 'username@localhost',
+ ],
+ ],
+ ],
+ true,
+ ['exact' => []],
+ false,
+ false,
+ ],
+ // data set 8
+ // Local user found by email
+ [
+ 'test@example.com',
+ [
+ [
+ 'UID' => 'uid1',
+ 'FN' => 'User',
+ 'EMAIL' => ['test@example.com'],
+ 'CLOUD' => ['test@localhost'],
+ 'isLocalSystemBook' => true,
+ ]
+ ],
+ false,
+ ['users' => [], 'exact' => ['users' => [['uuid' => 'uid1', 'name' => 'User', 'label' => 'User (test@example.com)','value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test'], 'shareWithDisplayNameUnique' => 'test@example.com']]]],
+ true,
+ false,
+ ],
+ // data set 9
+ // Current local user found by email => no result
+ [
+ 'test@example.com',
+ [
+ [
+ 'UID' => 'uid1',
+ 'FN' => 'User',
+ 'EMAIL' => ['test@example.com'],
+ 'CLOUD' => ['current@localhost'],
+ 'isLocalSystemBook' => true,
+ ]
+ ],
+ true,
+ ['exact' => []],
+ false,
+ false,
+ ],
+ // data set 10
+ // Pagination and "more results" for user matches by emails
+ [
+ 'test@example',
+ [
+ [
+ 'UID' => 'uid1',
+ 'FN' => 'User1',
+ 'EMAIL' => ['test@example.com'],
+ 'CLOUD' => ['test1@localhost'],
+ 'isLocalSystemBook' => true,
+ ],
+ [
+ 'UID' => 'uid2',
+ 'FN' => 'User2',
+ 'EMAIL' => ['test@example.de'],
+ 'CLOUD' => ['test2@localhost'],
+ 'isLocalSystemBook' => true,
+ ],
+ [
+ 'UID' => 'uid3',
+ 'FN' => 'User3',
+ 'EMAIL' => ['test@example.org'],
+ 'CLOUD' => ['test3@localhost'],
+ 'isLocalSystemBook' => true,
+ ],
+ [
+ 'UID' => 'uid4',
+ 'FN' => 'User4',
+ 'EMAIL' => ['test@example.net'],
+ 'CLOUD' => ['test4@localhost'],
+ 'isLocalSystemBook' => true,
+ ],
+ ],
+ true,
+ ['users' => [
+ ['uuid' => 'uid1', 'name' => 'User1', 'label' => 'User1 (test@example.com)', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1'], 'shareWithDisplayNameUnique' => 'test@example.com'],
+ ['uuid' => 'uid2', 'name' => 'User2', 'label' => 'User2 (test@example.de)', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test2'], 'shareWithDisplayNameUnique' => 'test@example.de'],
+ ], 'exact' => ['users' => []]],
+ false,
+ true,
+ ],
+ // data set 11
+ // Pagination and "more results" for normal emails
+ [
+ 'test@example',
+ [
+ [
+ 'UID' => 'uid1',
+ 'FN' => 'User1',
+ 'EMAIL' => ['test@example.com'],
+ 'CLOUD' => ['test1@localhost'],
+ ],
+ [
+ 'UID' => 'uid2',
+ 'FN' => 'User2',
+ 'EMAIL' => ['test@example.de'],
+ 'CLOUD' => ['test2@localhost'],
+ ],
+ [
+ 'UID' => 'uid3',
+ 'FN' => 'User3',
+ 'EMAIL' => ['test@example.org'],
+ 'CLOUD' => ['test3@localhost'],
+ ],
+ [
+ 'UID' => 'uid4',
+ 'FN' => 'User4',
+ 'EMAIL' => ['test@example.net'],
+ 'CLOUD' => ['test4@localhost'],
+ ],
+ ],
+ true,
+ ['exact' => []],
+ false,
+ false,
+ ],
+ ];
+ }
+
+ /**
+ *
+ * @param string $searchTerm
+ * @param array $contacts
+ * @param array $expectedResult
+ * @param bool $expectedExactIdMatch
+ * @param bool $expectedMoreResults
+ * @param array $userToGroupMapping
+ * @param bool $validEmail
*/
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetEmailGroupsOnly')]
- public function testSearchGroupsOnly($searchTerm, $contacts, $expected, $exactIdMatch, $reachedEnd, $userToGroupMapping, $validEmail): void {
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataSearchEmailGroupsOnly')]
+ public function testSearchEmailGroupsOnly($searchTerm, $contacts, $expectedResult, $expectedExactIdMatch, $expectedMoreResults, $userToGroupMapping, $validEmail): void {
$this->config->expects($this->any())
->method('getAppValue')
->willReturnCallback(
@@ -587,7 +885,7 @@ class MailPluginTest extends TestCase {
}
);
- $this->instantiatePlugin();
+ $this->instantiatePlugin(IShare::TYPE_EMAIL);
/** @var IUser|\PHPUnit\Framework\MockObject\MockObject */
$currentUser = $this->createMock('\OCP\IUser');
@@ -624,12 +922,12 @@ class MailPluginTest extends TestCase {
$moreResults = $this->plugin->search($searchTerm, 2, 0, $this->searchResult);
$result = $this->searchResult->asArray();
- $this->assertSame($exactIdMatch, $this->searchResult->hasExactIdMatch(new SearchResultType('emails')));
- $this->assertEquals($expected, $result);
- $this->assertSame($reachedEnd, $moreResults);
+ $this->assertSame($expectedExactIdMatch, $this->searchResult->hasExactIdMatch(new SearchResultType('emails')));
+ $this->assertEquals($expectedResult, $result);
+ $this->assertSame($expectedMoreResults, $moreResults);
}
- public static function dataGetEmailGroupsOnly(): array {
+ public static function dataSearchEmailGroupsOnly(): array {
return [
// The user `User` can share with the current user
[
@@ -640,15 +938,15 @@ class MailPluginTest extends TestCase {
'EMAIL' => ['test@example.com'],
'CLOUD' => ['test@localhost'],
'isLocalSystemBook' => true,
- 'UID' => 'User'
+ 'UID' => 'User',
]
],
- ['users' => [['label' => 'User (test@example.com)', 'uuid' => 'User', 'name' => 'User', 'value' => ['shareType' => 0, 'shareWith' => 'test'],'shareWithDisplayNameUnique' => 'test@example.com',]], 'emails' => [], 'exact' => ['emails' => [], 'users' => []]],
+ ['emails' => [], 'exact' => ['emails' => []]],
false,
false,
[
'currentUser' => ['group1'],
- 'User' => ['group1']
+ 'User' => ['group1'],
],
false,
],
@@ -661,7 +959,7 @@ class MailPluginTest extends TestCase {
'EMAIL' => ['test@example.com'],
'CLOUD' => ['test@localhost'],
'isLocalSystemBook' => true,
- 'UID' => 'User'
+ 'UID' => 'User',
]
],
['emails' => [], 'exact' => ['emails' => []]],
@@ -669,7 +967,7 @@ class MailPluginTest extends TestCase {
false,
[
'currentUser' => ['group1'],
- 'User' => ['group2']
+ 'User' => ['group2'],
],
false,
],
@@ -682,18 +980,149 @@ class MailPluginTest extends TestCase {
'EMAIL' => ['test@example.com'],
'CLOUD' => ['test@localhost'],
'isLocalSystemBook' => true,
- 'UID' => 'User'
+ 'UID' => 'User',
]
],
- ['emails' => [], 'exact' => ['emails' => [['label' => 'test@example.com', 'uuid' => 'test@example.com', 'value' => ['shareType' => 4,'shareWith' => 'test@example.com']]]]],
+ ['emails' => [], 'exact' => ['emails' => [['label' => 'test@example.com', 'uuid' => 'test@example.com', 'value' => ['shareType' => IShare::TYPE_EMAIL,'shareWith' => 'test@example.com']]]]],
false,
false,
[
'currentUser' => ['group1'],
- 'User' => ['group2']
+ 'User' => ['group2'],
],
true,
]
];
}
+
+ /**
+ *
+ * @param string $searchTerm
+ * @param array $contacts
+ * @param array $expectedResult
+ * @param bool $expectedExactIdMatch
+ * @param bool $expectedMoreResults
+ * @param array $userToGroupMapping
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataSearchUserGroupsOnly')]
+ public function testSearchUserGroupsOnly($searchTerm, $contacts, $expectedResult, $expectedExactIdMatch, $expectedMoreResults, $userToGroupMapping): void {
+ $this->config->expects($this->any())
+ ->method('getAppValue')
+ ->willReturnCallback(
+ function ($appName, $key, $default) {
+ if ($appName === 'core' && $key === 'shareapi_allow_share_dialog_user_enumeration') {
+ return 'yes';
+ } elseif ($appName === 'core' && $key === 'shareapi_only_share_with_group_members') {
+ return 'yes';
+ }
+ return $default;
+ }
+ );
+
+ $this->instantiatePlugin(IShare::TYPE_USER);
+
+ /** @var \OCP\IUser | \PHPUnit\Framework\MockObject\MockObject */
+ $currentUser = $this->createMock('\OCP\IUser');
+
+ $currentUser->expects($this->any())
+ ->method('getUID')
+ ->willReturn('currentUser');
+
+ $this->contactsManager->expects($this->any())
+ ->method('search')
+ ->willReturnCallback(function ($search, $searchAttributes) use ($searchTerm, $contacts) {
+ if ($search === $searchTerm) {
+ return $contacts;
+ }
+ return [];
+ });
+
+ $this->userSession->expects($this->any())
+ ->method('getUser')
+ ->willReturn($currentUser);
+
+ $this->groupManager->expects($this->any())
+ ->method('getUserGroupIds')
+ ->willReturnCallback(function (\OCP\IUser $user) use ($userToGroupMapping) {
+ return $userToGroupMapping[$user->getUID()];
+ });
+
+ $this->groupManager->expects($this->any())
+ ->method('isInGroup')
+ ->willReturnCallback(function ($userId, $group) use ($userToGroupMapping) {
+ return in_array($group, $userToGroupMapping[$userId]);
+ });
+
+ $moreResults = $this->plugin->search($searchTerm, 2, 0, $this->searchResult);
+ $result = $this->searchResult->asArray();
+
+ $this->assertSame($expectedExactIdMatch, $this->searchResult->hasExactIdMatch(new SearchResultType('emails')));
+ $this->assertEquals($expectedResult, $result);
+ $this->assertSame($expectedMoreResults, $moreResults);
+ }
+
+ public static function dataSearchUserGroupsOnly(): array {
+ return [
+ // The user `User` can share with the current user
+ [
+ 'test',
+ [
+ [
+ 'FN' => 'User',
+ 'EMAIL' => ['test@example.com'],
+ 'CLOUD' => ['test@localhost'],
+ 'isLocalSystemBook' => true,
+ 'UID' => 'User',
+ ]
+ ],
+ ['users' => [['label' => 'User (test@example.com)', 'uuid' => 'User', 'name' => 'User', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test'],'shareWithDisplayNameUnique' => 'test@example.com',]], 'exact' => ['users' => []]],
+ false,
+ false,
+ [
+ 'currentUser' => ['group1'],
+ 'User' => ['group1'],
+ ],
+ ],
+ // The user `User` cannot share with the current user
+ [
+ 'test',
+ [
+ [
+ 'FN' => 'User',
+ 'EMAIL' => ['test@example.com'],
+ 'CLOUD' => ['test@localhost'],
+ 'isLocalSystemBook' => true,
+ 'UID' => 'User',
+ ]
+ ],
+ ['exact' => []],
+ false,
+ false,
+ [
+ 'currentUser' => ['group1'],
+ 'User' => ['group2'],
+ ],
+ ],
+ // The user `User` cannot share with the current user, but there is an exact match on the e-mail address -> share by e-mail
+ [
+ 'test@example.com',
+ [
+ [
+ 'FN' => 'User',
+ 'EMAIL' => ['test@example.com'],
+ 'CLOUD' => ['test@localhost'],
+ 'isLocalSystemBook' => true,
+ 'UID' => 'User',
+ ]
+ ],
+ ['exact' => []],
+ false,
+ false,
+ [
+ 'currentUser' => ['group1'],
+ 'User' => ['group2'],
+ ],
+ ]
+ ];
+ }
}