[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