[CalendarServer-changes] [5810] CalendarServer/trunk/twistedcaldav/directory

source_changes at macosforge.org source_changes at macosforge.org
Mon Jun 28 14:00:59 PDT 2010


Revision: 5810
          http://trac.macosforge.org/projects/calendarserver/changeset/5810
Author:   sagen at apple.com
Date:     2010-06-28 14:00:58 -0700 (Mon, 28 Jun 2010)
Log Message:
-----------
Adds support for /Local users, including principal property search report.

Modified Paths:
--------------
    CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py
    CalendarServer/trunk/twistedcaldav/directory/test/test_buildquery.py
    CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectory.py

Modified: CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py	2010-06-24 21:26:41 UTC (rev 5809)
+++ CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py	2010-06-28 21:00:58 UTC (rev 5810)
@@ -392,7 +392,7 @@
             return ()
 
     def recordsMatchingFields(self, fields, operand="or", recordType=None,
-        lookupMethod=opendirectory.queryRecordsWithAttributes_list):
+        lookupMethod=opendirectory.queryRecordsWithAttribute_list):
 
         # Note that OD applies case-sensitivity globally across the entire
         # query, not per expression, so the current code uses whatever is
@@ -407,9 +407,6 @@
                         dsattributes.kDSNAttrMetaNodeLocation)
                     recordShortNames = self._uniqueTupleFromAttribute(
                         value.get(dsattributes.kDSNAttrRecordName))
-                    if (recordNodeName == "/Local/Default" and not
-                        (config.Scheduling.iMIP.Username in recordShortNames)):
-                        continue
 
                     recordGUID = value.get(dsattributes.kDS1AttrGeneratedUID)
 
@@ -469,40 +466,58 @@
                     pass
 
         def multiQuery(directory, queries, attrs, operand):
-            results = []
+            byGUID = { }
+            sets = []
 
             for query, recordTypes in queries.iteritems():
-                if not query:
-                    continue
+                ODField, value, caseless, matchType = query
+                if matchType == "starts-with":
+                    comparison = dsattributes.eDSStartsWith
+                elif matchType == "contains":
+                    comparison = dsattributes.eDSContains
+                else:
+                    comparison = dsattributes.eDSExact
 
-                expressions = []
-                for ODField, value, caseless, matchType in query:
-                    if matchType == "starts-with":
-                        comparison = dsattributes.eDSStartsWith
-                    elif matchType == "contains":
-                        comparison = dsattributes.eDSContains
-                    else:
-                        comparison = dsattributes.eDSExact
-                    expressions.append(dsquery.match(ODField, value, comparison))
+                self.log_debug("Calling OD: Types %s, Field %s, Value %s, Match %s, Caseless %s" %
+                    (recordTypes, ODField, value, matchType, caseless))
 
-                complexExpression = dsquery.expression(operand, expressions).generate()
+                queryResults = lookupMethod(
+                    directory,
+                    ODField,
+                    value,
+                    comparison,
+                    caseless,
+                    recordTypes,
+                    attrs,
+                )
 
-                self.log_debug("Calling OD: Types %s, Operand %s, Caseless %s, %s" %
-                    (recordTypes, operand, caseless, complexExpression))
+                if operand == dsquery.expression.OR:
+                    for recordName, data in queryResults:
+                        guid = data.get(dsattributes.kDS1AttrGeneratedUID, None)
+                        if guid:
+                            byGUID[guid] = (recordName, data)
+                else: # AND
+                    newSet = set()
+                    for recordName, data in queryResults:
+                        guid = data.get(dsattributes.kDS1AttrGeneratedUID, None)
+                        if guid:
+                            byGUID[guid] = (recordName, data)
+                            newSet.add(guid)
 
-                results.extend(
-                    lookupMethod(
-                        directory,
-                        complexExpression,
-                        caseless,
-                        recordTypes,
-                        attrs,
-                    )
-                )
+                    sets.append(newSet)
 
-            return results
+            if operand == dsquery.expression.OR:
+                return byGUID.values()
 
+            else:
+                results = []
+                for guid in set.intersection(*sets):
+                    recordName, data = byGUID.get(guid, None)
+                    if data is not None:
+                        results.append((data[dsattributes.kDSNAttrRecordName], data))
+                return results
 
+
         operand = (dsquery.expression.OR if operand == "or"
             else dsquery.expression.AND)
 
@@ -636,11 +651,6 @@
             recordEmailAddresses = self._setFromAttribute(value.get(dsattributes.kDSNAttrEMailAddress), lower=True)
             recordNodeName       = value.get(dsattributes.kDSNAttrMetaNodeLocation)
 
-            if recordNodeName == "/Local/Default" and not (config.Scheduling.iMIP.Username in recordShortNames):
-                self.log_info("Local record (%s)%s is not eligible for calendaring."
-                              % (recordType, recordShortName))
-                continue
-
             if not recordType:
                 self.log_debug("Record (unknown)%s in node %s has no recordType; ignoring."
                                % (recordShortName, recordNodeName))
@@ -766,22 +776,19 @@
     results (either none, or all records).
     """
 
-    fieldLists = {}
+    queries = {}
     for recordType in recordTypes:
-        fieldLists[recordType] = []
         for field, value, caseless, matchType in fields:
             if field in mapping:
                 if recordType in mapping[field]['appliesTo']:
                     ODField = mapping[field]['odField']
-                    fieldLists[recordType].append((ODField, value, caseless, matchType))
+                    key = (ODField, value, caseless, matchType)
+                    queries.setdefault(key, []).append(recordType)
 
-    queries = {}
-    for recordType, fieldList in fieldLists.iteritems():
-        key = tuple(fieldList)
-        queries.setdefault(key, []).append(recordType)
     return queries
 
 
+
 class OpenDirectoryRecord(CachingDirectoryRecord):
     """
     OpenDirectory implementation of L{IDirectoryRecord}.

Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_buildquery.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_buildquery.py	2010-06-24 21:26:41 UTC (rev 5809)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_buildquery.py	2010-06-28 21:00:58 UTC (rev 5810)
@@ -31,10 +31,8 @@
                 OpenDirectoryService._ODFields
             ),
             {
-                (
-                    ('dsAttrTypeStandard:FirstName', 'morgen', True, 'starts-with'),
-                    ('dsAttrTypeStandard:LastName', 'sagen', True, 'starts-with')
-                ): ['dsRecTypeStandard:Users']
+                ('dsAttrTypeStandard:FirstName', 'morgen', True, 'starts-with') : [dsattributes.kDSStdRecordTypeUsers],
+                ('dsAttrTypeStandard:LastName', 'sagen', True, 'starts-with') : [dsattributes.kDSStdRecordTypeUsers],
             }
         )
         self.assertEquals(
@@ -49,10 +47,8 @@
                 OpenDirectoryService._ODFields
             ),
             {
-                (
-                    ('dsAttrTypeStandard:FirstName', 'morgen', True, 'starts-with'),
-                    ('dsAttrTypeStandard:EMailAddress', 'morgen', True, 'contains'),
-                ): ['dsRecTypeStandard:Users'],
+                ('dsAttrTypeStandard:FirstName', 'morgen', True, 'starts-with') : [dsattributes.kDSStdRecordTypeUsers],
+                ('dsAttrTypeStandard:EMailAddress', 'morgen', True, 'contains') : [dsattributes.kDSStdRecordTypeUsers],
             }
         )
         self.assertEquals(
@@ -69,10 +65,8 @@
                 OpenDirectoryService._ODFields
             ),
             {
-                (
-                    ('dsAttrTypeStandard:RealName', 'morgen', True, 'starts-with'),
-                    ('dsAttrTypeStandard:EMailAddress', 'morgen', True, 'contains'),
-                ): ['dsRecTypeStandard:Groups'],
+                ('dsAttrTypeStandard:RealName', 'morgen', True, 'starts-with') : [dsattributes.kDSStdRecordTypeGroups],
+                ('dsAttrTypeStandard:EMailAddress', 'morgen', True, 'contains') : [dsattributes.kDSStdRecordTypeGroups],
             }
         )
         self.assertEquals(
@@ -90,15 +84,22 @@
                 OpenDirectoryService._ODFields
             ),
             {
+                ('dsAttrTypeStandard:RealName', 'morgen', True, 'starts-with') : [dsattributes.kDSStdRecordTypeUsers, dsattributes.kDSStdRecordTypeGroups],
+                ('dsAttrTypeStandard:EMailAddress', 'morgen', True, 'contains') : [dsattributes.kDSStdRecordTypeUsers, dsattributes.kDSStdRecordTypeGroups],
+                ('dsAttrTypeStandard:FirstName', 'morgen', True, 'starts-with') : [dsattributes.kDSStdRecordTypeUsers],
+                ('dsAttrTypeStandard:LastName', 'morgen', True, 'starts-with') : [dsattributes.kDSStdRecordTypeUsers],
+            }
+        )
+        self.assertEquals(
+            buildQueries(
+                [
+                    dsattributes.kDSStdRecordTypeGroups,
+                ],
                 (
-                    ('dsAttrTypeStandard:RealName', 'morgen', True, 'starts-with'),
-                    ('dsAttrTypeStandard:EMailAddress', 'morgen', True, 'contains')
-                ): ['dsRecTypeStandard:Groups'],
-                (
-                    ('dsAttrTypeStandard:FirstName', 'morgen', True, 'starts-with'),
-                    ('dsAttrTypeStandard:LastName', 'morgen', True, 'starts-with'),
-                    ('dsAttrTypeStandard:RealName', 'morgen', True, 'starts-with'),
-                    ('dsAttrTypeStandard:EMailAddress', 'morgen', True, 'contains')
-                ): ['dsRecTypeStandard:Users']
+                    ("firstName", "morgen", True, "starts-with"),
+                ),
+                OpenDirectoryService._ODFields
+            ),
+            {
             }
         )

Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectory.py	2010-06-24 21:26:41 UTC (rev 5809)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectory.py	2010-06-28 21:00:58 UTC (rev 5810)
@@ -243,35 +243,110 @@
         @inlineCallbacks
         def test_recordsMatchingFields(self):
 
-            def lookupMethod(obj, compound, casei, recordType, attributes, count=0):
-                if dsattributes.kDSStdRecordTypeUsers in recordType:
-                    return [
-                        ('morgen',
+
+            def lookupMethod(obj, attribute, value, matchType, caseless,
+                recordTypes, attributes):
+
+                data = {
+                    dsattributes.kDSStdRecordTypeUsers : (
                         {
-                            'dsAttrTypeStandard:RecordType': 'dsRecTypeStandard:Users',
-                            'dsAttrTypeStandard:AppleMetaNodeLocation': '/LDAPv3/127.0.0.1',
-                            'dsAttrTypeStandard:RecordName': ['morgen', 'Morgen Sagen'],
-                            'dsAttrTypeStandard:FirstName': 'Morgen',
-                            'dsAttrTypeStandard:GeneratedUID': '83479230-821E-11DE-B6B0-DBB02C6D659D',
-                            'dsAttrTypeStandard:LastName': 'Sagen',
-                            'dsAttrTypeStandard:EMailAddress': 'morgen at example.com',
-                            'dsAttrTypeStandard:RealName': 'Morgen Sagen'
-                        }),
-                        ('morehouse',
+                            dsattributes.kDS1AttrDistinguishedName : "Morgen Sagen",
+                            dsattributes.kDSNAttrRecordName : "morgen",
+                            dsattributes.kDS1AttrFirstName : "Morgen",
+                            dsattributes.kDS1AttrLastName : "Sagen",
+                            dsattributes.kDSNAttrEMailAddress : "morgen at example.com",
+                            dsattributes.kDSNAttrMetaNodeLocation : "/LDAPv3/127.0.0.1",
+                            dsattributes.kDS1AttrGeneratedUID : "83479230-821E-11DE-B6B0-DBB02C6D659D",
+                            dsattributes.kDSNAttrRecordType : dsattributes.kDSStdRecordTypeUsers,
+                        },
                         {
-                            'dsAttrTypeStandard:RecordType': 'dsRecTypeStandard:Users',
-                            'dsAttrTypeStandard:AppleMetaNodeLocation': '/LDAPv3/127.0.0.1',
-                            'dsAttrTypeStandard:RecordName': ['morehouse', 'Joe Morehouse'],
-                            'dsAttrTypeStandard:FirstName': 'Joe',
-                            'dsAttrTypeStandard:GeneratedUID': '98342930-90DC-11DE-A842-A29601FB13E8',
-                            'dsAttrTypeStandard:LastName': 'Morehouse',
-                            'dsAttrTypeStandard:EMailAddress': 'morehouse at example.com',
-                            'dsAttrTypeStandard:RealName': 'Joe Morehouse'
-                        }),
-                    ]
-                else:
-                    return []
+                            dsattributes.kDS1AttrDistinguishedName : "Morgan Sagan",
+                            dsattributes.kDSNAttrRecordName : "morgan",
+                            dsattributes.kDS1AttrFirstName : "Morgan",
+                            dsattributes.kDS1AttrLastName : "Sagan",
+                            dsattributes.kDSNAttrEMailAddress : "morgan at example.com",
+                            dsattributes.kDSNAttrMetaNodeLocation : "/LDAPv3/127.0.0.1",
+                            dsattributes.kDS1AttrGeneratedUID : "93479230-821E-11DE-B6B0-DBB02C6D659D",
+                            dsattributes.kDSNAttrRecordType : dsattributes.kDSStdRecordTypeUsers,
+                        },
+                        {
+                            dsattributes.kDS1AttrDistinguishedName : "Shari Sagen",
+                            dsattributes.kDSNAttrRecordName : "shari",
+                            dsattributes.kDS1AttrFirstName : "Shari",
+                            dsattributes.kDS1AttrLastName : "Sagen",
+                            dsattributes.kDSNAttrEMailAddress : "shari at example.com",
+                            dsattributes.kDSNAttrMetaNodeLocation : "/LDAPv3/127.0.0.1",
+                            dsattributes.kDS1AttrGeneratedUID : "A3479230-821E-11DE-B6B0-DBB02C6D659D",
+                            dsattributes.kDSNAttrRecordType : dsattributes.kDSStdRecordTypeUsers,
+                        },
+                        {
+                            dsattributes.kDS1AttrDistinguishedName : "Local Morgen",
+                            dsattributes.kDSNAttrRecordName : "localmorgen",
+                            dsattributes.kDS1AttrFirstName : "Local",
+                            dsattributes.kDS1AttrLastName : "Morgen",
+                            dsattributes.kDSNAttrEMailAddress : "localmorgen at example.com",
+                            dsattributes.kDSNAttrMetaNodeLocation : "/Local/Default",
+                            dsattributes.kDS1AttrGeneratedUID : "B3479230-821E-11DE-B6B0-DBB02C6D659D",
+                            dsattributes.kDSNAttrRecordType : dsattributes.kDSStdRecordTypeUsers,
+                        },
+                    ),
+                    dsattributes.kDSStdRecordTypeGroups : (
+                        {
+                            dsattributes.kDS1AttrDistinguishedName : "Test Group",
+                            dsattributes.kDSNAttrRecordName : "testgroup",
+                            dsattributes.kDS1AttrFirstName : None,
+                            dsattributes.kDS1AttrLastName : None,
+                            dsattributes.kDSNAttrEMailAddress : None,
+                            dsattributes.kDSNAttrMetaNodeLocation : "/LDAPv3/127.0.0.1",
+                            dsattributes.kDS1AttrGeneratedUID : "C3479230-821E-11DE-B6B0-DBB02C6D659D",
+                            dsattributes.kDSNAttrRecordType : dsattributes.kDSStdRecordTypeGroups,
+                        },
+                        {
+                            dsattributes.kDS1AttrDistinguishedName : "Morgen's Group",
+                            dsattributes.kDSNAttrRecordName : "morgensgroup",
+                            dsattributes.kDS1AttrFirstName : None,
+                            dsattributes.kDS1AttrLastName : None,
+                            dsattributes.kDSNAttrEMailAddress : None,
+                            dsattributes.kDSNAttrMetaNodeLocation : "/LDAPv3/127.0.0.1",
+                            dsattributes.kDS1AttrGeneratedUID : "D3479230-821E-11DE-B6B0-DBB02C6D659D",
+                            dsattributes.kDSNAttrRecordType : dsattributes.kDSStdRecordTypeGroups,
+                        },
+                    ),
+                }
 
+                def attributeMatches(fieldValue, value, caseless, matchType):
+                    if fieldValue is None:
+                        return False
+                    if caseless:
+                        fieldValue = fieldValue.lower()
+                        value = value.lower()
+                    if matchType == "starts-with":
+                        if fieldValue.startswith(value):
+                            return True
+                    elif matchType == "contains":
+                        try:
+                            fieldValue.index(value)
+                            return True
+                        except ValueError:
+                            pass
+                    else: # exact
+                        if fieldValue == value:
+                            return True
+                    return False
+
+                results = []
+                for recordType in recordTypes:
+                    for row in data[recordType]:
+                        if attributeMatches(row[attribute], value, caseless,
+                            matchType):
+                            results.append((row[dsattributes.kDSNAttrRecordName], row))
+
+                return results
+
+
+            #
+            # OR
+            #
             fields = [
                 ('fullName', 'mor', True, u'starts-with'),
                 ('emailAddresses', 'mor', True, u'starts-with'),
@@ -279,14 +354,73 @@
                 ('lastName', 'mor', True, u'starts-with')
             ]
 
+            # any record type
             results = (yield self.service().recordsMatchingFields(fields,
                 lookupMethod=lookupMethod))
             results = list(results)
-            self.assertEquals(len(results), 2)
+            self.assertEquals(len(results), 4)
             for record in results:
                 self.assertTrue(isinstance(record, OpenDirectoryRecord))
 
+            # just users
+            results = (yield self.service().recordsMatchingFields(fields,
+                recordType="users",
+                lookupMethod=lookupMethod))
+            results = list(results)
+            self.assertEquals(len(results), 3)
 
+            # just groups
+            results = (yield self.service().recordsMatchingFields(fields,
+                recordType="groups",
+                lookupMethod=lookupMethod))
+            results = list(results)
+            self.assertEquals(len(results), 1)
+
+
+            #
+            # AND
+            #
+            fields = [
+                ('firstName', 'morgen', True, u'equals'),
+                ('lastName', 'age', True, u'contains')
+            ]
+            results = (yield self.service().recordsMatchingFields(fields,
+                operand="and", lookupMethod=lookupMethod))
+            results = list(results)
+            self.assertEquals(len(results), 1)
+
+            #
+            # case sensitivity
+            #
+            fields = [
+                ('firstName', 'morgen', False, u'equals'),
+            ]
+            results = (yield self.service().recordsMatchingFields(fields,
+                lookupMethod=lookupMethod))
+            results = list(results)
+            self.assertEquals(len(results), 0)
+
+            fields = [
+                ('firstName', 'morgen', True, u'equals'),
+            ]
+            results = (yield self.service().recordsMatchingFields(fields,
+                lookupMethod=lookupMethod))
+            results = list(results)
+            self.assertEquals(len(results), 1)
+
+            #
+            # no matches
+            #
+            fields = [
+                ('firstName', 'xyzzy', True, u'starts-with'),
+                ('lastName', 'plugh', True, u'contains')
+            ]
+            results = (yield self.service().recordsMatchingFields(fields,
+                operand="and", lookupMethod=lookupMethod))
+            results = list(results)
+            self.assertEquals(len(results), 0)
+
+
     class OpenDirectorySubset (OpenDirectory):
         """
         Test the recordTypes subset feature of Apple OpenDirectoryService.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20100628/2a1037fb/attachment-0001.html>


More information about the calendarserver-changes mailing list