diff --git a/apps/dav/lib/CalDAV/ResourceBooking/AbstractPrincipalBackend.php b/apps/dav/lib/CalDAV/ResourceBooking/AbstractPrincipalBackend.php index d658c3433f..90d147f674 100644 --- a/apps/dav/lib/CalDAV/ResourceBooking/AbstractPrincipalBackend.php +++ b/apps/dav/lib/CalDAV/ResourceBooking/AbstractPrincipalBackend.php @@ -244,6 +244,14 @@ abstract class AbstractPrincipalBackend implements BackendInterface { $stmt->closeCursor(); break; + case '{urn:ietf:params:xml:ns:caldav}calendar-user-address-set': + // If you add support for more search properties that qualify as a user-address, + // please also add them to the array below + $results[] = $this->searchPrincipals($this->principalPrefix, [ + '{http://sabredav.org/ns}email-address' => $value, + ], 'anyof'); + break; + default: $results[] = []; break; diff --git a/apps/dav/lib/Connector/Sabre/Principal.php b/apps/dav/lib/Connector/Sabre/Principal.php index e781618539..812f9d5416 100644 --- a/apps/dav/lib/Connector/Sabre/Principal.php +++ b/apps/dav/lib/Connector/Sabre/Principal.php @@ -287,6 +287,16 @@ class Principal implements BackendInterface { }, []); break; + case '{urn:ietf:params:xml:ns:caldav}calendar-user-address-set': + // If you add support for more search properties that qualify as a user-address, + // please also add them to the array below + $results[] = $this->searchUserPrincipals([ + // In theory this should also search for principal:principals/users/... + // but that's used internally only anyway and i don't know of any client querying that + '{http://sabredav.org/ns}email-address' => $value, + ], 'anyof'); + break; + default: $results[] = []; break; diff --git a/apps/dav/lib/DAV/GroupPrincipalBackend.php b/apps/dav/lib/DAV/GroupPrincipalBackend.php index 02f8edf392..a797a25f90 100644 --- a/apps/dav/lib/DAV/GroupPrincipalBackend.php +++ b/apps/dav/lib/DAV/GroupPrincipalBackend.php @@ -222,6 +222,13 @@ class GroupPrincipalBackend implements BackendInterface { }, []); break; + case '{urn:ietf:params:xml:ns:caldav}calendar-user-address-set': + // If you add support for more search properties that qualify as a user-address, + // please also add them to the array below + $results[] = $this->searchPrincipals(self::PRINCIPAL_PREFIX, [ + ], 'anyof'); + break; + default: $results[] = []; break; diff --git a/apps/dav/tests/unit/CalDAV/ResourceBooking/AbstractPrincipalBackendTest.php b/apps/dav/tests/unit/CalDAV/ResourceBooking/AbstractPrincipalBackendTest.php index aca36fb77e..19da1782cc 100644 --- a/apps/dav/tests/unit/CalDAV/ResourceBooking/AbstractPrincipalBackendTest.php +++ b/apps/dav/tests/unit/CalDAV/ResourceBooking/AbstractPrincipalBackendTest.php @@ -519,6 +519,125 @@ abstract class AbstractPrincipalBackendTest extends TestCase { ]; } + public function testSearchPrincipalsByCalendarUserAddressSet() { + $user = $this->createMock(IUser::class); + $this->userSession->expects($this->exactly(2)) + ->method('getUser') + ->with() + ->will($this->returnValue($user)); + $this->groupManager->expects($this->exactly(2)) + ->method('getUserGroupIds') + ->with($user) + ->will($this->returnValue(['group1', 'group2'])); + + $queryBuilder1 = $this->createMock(IQueryBuilder::class); + $stmt1 = $this->createMock(\Doctrine\DBAL\Driver\Statement::class); + $expr1 = $this->createMock(\OCP\DB\QueryBuilder\IExpressionBuilder::class); + + $this->dbConnection->expects($this->at(0)) + ->method('getQueryBuilder') + ->with() + ->will($this->returnValue($queryBuilder1)); + $this->dbConnection->expects($this->at(1)) + ->method('escapeLikeParameter') + ->with('foo') + ->will($this->returnValue('escapedFoo')); + + $queryBuilder1->method('expr') + ->will($this->returnValue($expr1)); + + $expr1->method('iLike') + ->will($this->returnValueMap([ + ['email', 'createNamedParameter-1', null, 'ILIKE_CLAUSE_1'], + ])); + + $queryBuilder1->method('expr') + ->will($this->returnValue($expr1)); + + $queryBuilder1->method('createNamedParameter') + ->will($this->returnValueMap([ + ['%escapedFoo%', \PDO::PARAM_STR, null, 'createNamedParameter-1'], + ])); + + $queryBuilder1->expects($this->at(0)) + ->method('select') + ->with(['id', 'backend_id', 'resource_id', 'email', 'displayname', 'group_restrictions']) + ->will($this->returnValue($queryBuilder1)); + $queryBuilder1->expects($this->at(1)) + ->method('from') + ->with($this->expectedDbTable) + ->will($this->returnValue($queryBuilder1)); + $queryBuilder1->expects($this->at(4)) + ->method('where') + ->with('ILIKE_CLAUSE_1') + ->will($this->returnValue($queryBuilder1)); + $queryBuilder1->expects($this->at(5)) + ->method('execute') + ->with() + ->will($this->returnValue($stmt1)); + + $stmt1->expects($this->at(0)) + ->method('fetch') + ->with(\PDO::FETCH_ASSOC) + ->will($this->returnValue([ + 'id' => 0, + 'backend_id' => 'db', + 'resource_id' => '1', + 'email' => '1', + 'displayname' => 'Resource 1', + 'group_restrictions' => null, + ])); + $stmt1->expects($this->at(1)) + ->method('fetch') + ->with(\PDO::FETCH_ASSOC) + ->will($this->returnValue([ + 'id' => 1, + 'backend_id' => 'db', + 'resource_id' => '2', + 'email' => '2', + 'displayname' => 'Resource 2', + 'group_restrictions' => '', + ])); + $stmt1->expects($this->at(2)) + ->method('fetch') + ->with(\PDO::FETCH_ASSOC) + ->will($this->returnValue([ + 'id' => 2, + 'backend_id' => 'db', + 'resource_id' => '3', + 'email' => '3', + 'displayname' => 'Resource 3', + 'group_restrictions' => '["group3"]', + ])); + $stmt1->expects($this->at(3)) + ->method('fetch') + ->with(\PDO::FETCH_ASSOC) + ->will($this->returnValue([ + 'id' => 99, + 'backend_id' => 'db', + 'resource_id' => '99', + 'email' => '99', + 'displayname' => 'Resource 99', + 'group_restrictions' => '["group1", "group2"]', + ])); + $stmt1->expects($this->at(4)) + ->method('fetch') + ->with(\PDO::FETCH_ASSOC) + ->will($this->returnValue(null)); + + $actual = $this->principalBackend->searchPrincipals($this->principalPrefix, [ + '{urn:ietf:params:xml:ns:caldav}calendar-user-address-set' => 'foo', + ]); + + $this->assertEquals( + str_replace('%prefix%', $this->principalPrefix, [ + '%prefix%/db-1', + '%prefix%/db-2', + '%prefix%/db-99', + ]), + $actual); + } + public function testSearchPrincipalsEmptySearchProperties() { $this->userSession->expects($this->never()) ->method('getUser'); diff --git a/apps/dav/tests/unit/Connector/Sabre/PrincipalTest.php b/apps/dav/tests/unit/Connector/Sabre/PrincipalTest.php index b40cd3105a..225189e7d0 100644 --- a/apps/dav/tests/unit/Connector/Sabre/PrincipalTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/PrincipalTest.php @@ -391,6 +391,32 @@ class PrincipalTest extends TestCase { ]; } + public function testSearchPrincipalByCalendarUserAddressSet() { + $this->shareManager->expects($this->exactly(2)) + ->method('shareAPIEnabled') + ->will($this->returnValue(true)); + + $this->shareManager->expects($this->exactly(2)) + ->method('shareWithGroupMembersOnly') + ->will($this->returnValue(false)); + + $user2 = $this->createMock(IUser::class); + $user2->method('getUID')->will($this->returnValue('user2')); + $user3 = $this->createMock(IUser::class); + $user3->method('getUID')->will($this->returnValue('user3')); + + $this->userManager->expects($this->at(0)) + ->method('getByEmail') + ->with('user@example.com') + ->will($this->returnValue([$user2, $user3])); + + $this->assertEquals([ + 'principals/users/user2', + 'principals/users/user3', + ], $this->connector->searchPrincipals('principals/users', + ['{urn:ietf:params:xml:ns:caldav}calendar-user-address-set' => 'user@example.com'])); + } + public function testFindByUriSharingApiDisabled() { $this->shareManager->expects($this->once()) ->method('shareApiEnabled')