[CalendarServer-changes] [8906] CalendarServer/branches/users/gaya/ldapdirectorybacker
source_changes at macosforge.org
source_changes at macosforge.org
Fri Mar 16 20:37:16 PDT 2012
Revision: 8906
http://trac.macosforge.org/projects/calendarserver/changeset/8906
Author: gaya at apple.com
Date: 2012-03-16 20:37:14 -0700 (Fri, 16 Mar 2012)
Log Message:
-----------
add xmldirectorybacker.py
Modified Paths:
--------------
CalendarServer/branches/users/gaya/ldapdirectorybacker/conf/caldavd-test.plist
CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/xmlfile.py
CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/stdconfig.py
Added Paths:
-----------
CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/xmldirectorybacker.py
Modified: CalendarServer/branches/users/gaya/ldapdirectorybacker/conf/caldavd-test.plist
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/conf/caldavd-test.plist 2012-03-17 01:43:01 UTC (rev 8905)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/conf/caldavd-test.plist 2012-03-17 03:37:14 UTC (rev 8906)
@@ -547,7 +547,7 @@
<!-- Log levels -->
<key>DefaultLogLevel</key>
- <string>info</string> <!-- debug, info, warn, error -->
+ <string>debug</string> <!-- debug, info, warn, error -->
<!-- Log level overrides for specific functionality -->
<key>LogLevels</key>
@@ -994,6 +994,30 @@
<string>English</string>
</dict>
-
+ <!--
+ Directory Address Book
+ -->
+
+ <!-- Disable Directory Address Book -->
+ <!--
+ <key>DirectoryAddressBook</key>
+ <false/>
+ -->
+
+ <!-- XML Directory-backed Directory Address Book -->
+ <key>EnableSearchAddressBook</key>
+ <true/>
+ <key>DirectoryAddressBook</key>
+ <dict>
+ <key>Enabled</key>
+ <true/>
+ <key>type</key>
+ <string>twistedcaldav.directory.xmldirectorybacker.XMLDirectoryBackingService</string>
+ <key>params</key>
+ <dict>
+ <key>xmlFile</key>
+ <string>./conf/auth/accounts-test.xml</string>
+ </dict>
+ </dict>
</dict>
</plist>
Added: CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/xmldirectorybacker.py
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/xmldirectorybacker.py (rev 0)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/xmldirectorybacker.py 2012-03-17 03:37:14 UTC (rev 8906)
@@ -0,0 +1,290 @@
+##
+# Copyright (c) 2006-2012 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+
+"""
+Apple Open Directory directory service implementation for backing up directory-backed address books
+"""
+
+__all__ = [
+ "XMLDirectoryBackingService",
+]
+
+import traceback
+import hashlib
+
+import os
+import sys
+import time
+
+from socket import getfqdn
+
+from twisted.internet import reactor
+from twisted.internet.defer import inlineCallbacks, returnValue, succeed
+
+from twistedcaldav.directory.directory import DirectoryRecord
+from twistedcaldav.directory.xmlfile import XMLDirectoryService
+
+from twistedcaldav.directory.opendirectorybacker import VCardRecord, dsFilterFromAddressBookFilter
+from calendarserver.platform.darwin.od import dsattributes, dsquery
+
+
+class XMLDirectoryBackingService(XMLDirectoryService):
+ """
+ """
+
+ node="/Search"
+ def __repr__(self):
+ return "<%s %r>" % (self.__class__.__name__, self.realmName, )
+
+ def __init__(self, params):
+ self._actuallyConfigure(**params)
+
+ def _actuallyConfigure(self, **params):
+
+ self.log_debug("_actuallyConfigure: params=%s" % (params,))
+ defaults = {
+ "recordTypes": (self.recordType_users,),
+ "rdnSchema": {
+ self.recordType_users : {
+ "vcardPropToDirRecordAttrMap" : {
+ "FN" : (
+ "fullName",
+ "shortNames",
+ "firstName",
+ "lastName",
+ ),
+ "N" : (
+ "fullName",
+ "shortNames",
+ "firstName",
+ "lastName",
+ ),
+ "EMAIL" : "emailAddresses",
+ "UID" : "guid",
+ },
+ "dirRecordAttrToDSAttrMap" : {
+ "guid" : dsattributes.kDS1AttrGeneratedUID,
+ "fullName" : dsattributes.kDS1AttrDistinguishedName,
+ "shortNames" : dsattributes.kDSNAttrRecordName,
+ "firstName" : dsattributes.kDS1AttrFirstName,
+ "lastName" : dsattributes.kDS1AttrLastName,
+ "emailAddresses" : dsattributes.kDSNAttrEMailAddress,
+ },
+ },
+ },
+ "maxQueryRecords":0, # max records returned
+ }
+
+ #params = self.getParams(params, defaults, ignored)
+ def addDefaults(params, defaults, remove=None):
+ keys = set(params.keys())
+
+ for key in defaults:
+ if not key in params:
+ params[key] = defaults[key]
+ return params
+
+ params = addDefaults(params, defaults)
+ self.log_debug("_actuallyConfigure after addDefaults: params=%s" % (params,))
+
+ # super does not like these extra params
+ directoryBackedAddressBook=params["directoryBackedAddressBook"]
+ #del params["directoryBackedAddressBook"]
+ maxQueryRecords=params["maxQueryRecords"]
+ del params["maxQueryRecords"]
+ rdnSchema=params["rdnSchema"]
+ del params["rdnSchema"]
+
+
+ assert directoryBackedAddressBook is not None
+ self.directoryBackedAddressBook = directoryBackedAddressBook
+
+ self.maxQueryRecords = maxQueryRecords
+ self.rdnSchema = rdnSchema
+
+
+ self.realmName = None # needed for super
+
+ super(XMLDirectoryBackingService, self).__init__(params)
+
+ ### self.defaultNodeName used by VCardRecord.
+ # get this now once
+ hostname = getfqdn()
+ if hostname:
+ self.defaultNodeName = "/LDAPv3/" + hostname
+ else:
+ self.defaultNodeName = None
+
+
+ def __cmp__(self, other):
+ if not isinstance(other, DirectoryRecord):
+ return super(DirectoryRecord, self).__eq__(other)
+
+ for attr in ("directory", "node"):
+ diff = cmp(getattr(self, attr), getattr(other, attr))
+ if diff != 0:
+ return diff
+ return 0
+
+ def __hash__(self):
+ h = hash(self.__class__.__name__)
+ for attr in ("node",):
+ h = (h + hash(getattr(self, attr))) & sys.maxint
+ return h
+
+ def createCache(self):
+ succeed(None)
+
+
+ @inlineCallbacks
+ def vCardRecordsForAddressBookQuery(self, addressBookFilter, addressBookQuery, maxResults ):
+ """
+ Get vCards for a given addressBookFilter and addressBookQuery
+ """
+
+ queryRecords = []
+ limited = False
+
+ #calc maxRecords from passed in maxResults allowing extra for second stage filtering in caller
+ maxRecords = int(maxResults * 1.2)
+ if self.maxQueryRecords and maxRecords > self.maxQueryRecords:
+ maxRecords = self.maxQueryRecords
+
+ for queryType in self.recordTypes():
+
+ queryMap = self.rdnSchema[queryType]
+ vcardPropToDirRecordAttrMap = queryMap["vcardPropToDirRecordAttrMap"]
+ dirRecordAttrToDSAttrMap = queryMap["dirRecordAttrToDSAttrMap"]
+
+ allRecords, filterAttributes, dsFilter = dsFilterFromAddressBookFilter( addressBookFilter, vcardPropToDirRecordAttrMap );
+ self.log_debug("vCardRecordsForAddressBookQuery: queryType=\"%s\" LDAP allRecords=%s, filterAttributes=%s, query=%s" % (queryType, allRecords, filterAttributes, "None" if dsFilter is None else dsFilter.generate(),))
+
+
+ if allRecords:
+ dsFilter = None # None expression == all Records
+
+ # stop query for all
+ clear = not allRecords and not dsFilter
+
+ if not clear:
+
+ @inlineCallbacks
+ def recordsForDSFilter(dsFilter, recordType):
+
+ """
+ Whist this tests the dsFilter expression tree and recordsMatchingFields() it make little difference to the resutl of
+ a addressbook query because of post filtering.
+ """
+ self.log_debug("recordsForDSFilter: dsFilter=%s" % (dsFilter.generate(), ))
+ if dsFilter.operator == dsquery.expression.NOT:
+ self.log_debug("recordsForDSFilter: dsFilter-%s NOT NONE" % (dsFilter.generate(), ))
+ returnValue(None) # dsquery.expression.NOT not supported by recordsMatchingFields()
+ else:
+ self.log_debug("recordsForDSFilter: #subs %s" % (len(dsFilter.subexpressions), ))
+
+ # evaluate matches
+ matches = [match for match in dsFilter.subexpressions if isinstance(match, dsquery.match)]
+ fields = []
+ for match in matches:
+ self.log_debug("recordsForDSFilter: match=%s" % (match.generate(), ))
+ xmlMatchType = {
+ dsattributes.eDSExact : "exact",
+ dsattributes.eDSStartsWith : "start-with",
+ dsattributes.eDSContains : "contains",
+ }.get(match.matchType)
+ if not xmlMatchType:
+ self.log_debug("recordsForDSFilter: match=%s match type not supported" % (match.generate(), ))
+ returnValue(None) # match type not supported by recordsMatchingFields()
+
+ fields += ((match.attribute, match.value, True, xmlMatchType,),)
+ self.log_debug("recordsForDSFilter: fields=%s" % (fields,))
+
+ # if there were matches, call get records that match
+ result = set()
+ if len(fields):
+ operand = "and" if dsFilter.operator == dsquery.expression.AND else "or"
+ self.log_debug("recordsForDSFilter: recordsMatchingFields(fields=%s, operand=%s, recordType=%s)" % (fields, operand, recordType,))
+ result = set((yield self.recordsMatchingFields(fields, operand=operand, recordType=recordType)))
+ self.log_debug("recordsForDSFilter: result=%s" % (result,))
+
+ # evaluate subexpressions
+ subexpressions = [subexpression for subexpression in dsFilter.subexpressions if isinstance(subexpression, dsquery.expression)]
+ for subexpression in subexpressions:
+ self.log_debug("recordsForDSFilter: subexpression=%s" % (subexpression.generate(), ))
+ subresult = (yield recordsForDSFilter(subexpression, recordType))
+ self.log_debug("recordsForDSFilter: subresult=%s" % (subresult,))
+ if subresult is None:
+ returnValue(None)
+
+ if dsFilter.operator == dsquery.expression.OR:
+ result = result.union(subresult)
+ else:
+ result = result.intersection(subresult)
+
+ self.log_debug("recordsForDSFilter: dsFilter=%s returning %s" % (dsFilter.generate(), result, ))
+ returnValue(result)
+
+ # walk the expression tree
+ xmlDirectoryRecords = (yield recordsForDSFilter(dsFilter, queryType))
+ self.log_debug("vCardRecordsForAddressBookQuery: #xmlDirectoryRecords %s" % (len(xmlDirectoryRecords) if xmlDirectoryRecords is not None else xmlDirectoryRecords, ))
+
+ if xmlDirectoryRecords is None:
+ xmlDirectoryRecords = (yield self.listRecords(queryType))
+ self.log_debug("vCardRecordsForAddressBookQuery: all #xmlDirectoryRecords %s" % (len(xmlDirectoryRecords), ))
+
+ # apply limit
+ if len(xmlDirectoryRecords) > maxRecords:
+ xmlDirectoryRecords = set(list(xmlDirectoryRecords)[:maxRecords])
+ self.log_debug("vCardRecordsForAddressBookQuery: #xmlDirectoryRecords after max %s" % (len(xmlDirectoryRecords), ))
+
+ for xmlDirectoryRecord in xmlDirectoryRecords:
+
+ def dsRecordAttributesFromDirectoryRecord( xmlDirectoryRecord ):
+ dsRecordAttributes = {}
+ for attr in dirRecordAttrToDSAttrMap:
+ try:
+ value = getattr(xmlDirectoryRecord, attr)
+ dsRecordAttributes[dirRecordAttrToDSAttrMap[attr]] = value
+ except AttributeError:
+ # No value
+ pass
+ return dsRecordAttributes
+
+ dsRecord = None
+ dsRecordAttributes = dsRecordAttributesFromDirectoryRecord( xmlDirectoryRecord )
+ try:
+ dsRecord = VCardRecord(self, dsRecordAttributes,)
+ vCardText = dsRecord.vCardText()
+
+ except:
+ traceback.print_exc()
+ self.log_info("Could not get vcard for ds record %s" % (dsRecord,))
+ else:
+ self.log_debug("vCardRecordsForAddressBookQuery: VCard text =\n%s" % (vCardText, ))
+ queryRecords.append(dsRecord)
+
+
+ # only get requested number of record results
+ maxRecords -= len(xmlDirectoryRecords)
+ if maxRecords <= 0:
+ limited = True
+ break
+
+
+ self.log_info("limited %s len(queryRecords) %s" % (limited,len(queryRecords),))
+ returnValue((queryRecords, limited,))
+
Modified: CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/xmlfile.py
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/xmlfile.py 2012-03-17 01:43:01 UTC (rev 8905)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/xmlfile.py 2012-03-17 03:37:14 UTC (rev 8906)
@@ -38,6 +38,7 @@
from twistedcaldav.scheduling.cuaddress import normalizeCUAddr
from twistedcaldav.xmlutil import addSubElement, createElement, elementToXML
from uuid import uuid4
+from twisted.internet.defer import succeed
class XMLDirectoryService(DirectoryService):
@@ -337,7 +338,8 @@
recordTypes = list(self.recordTypes())
else:
recordTypes = (recordType,)
-
+
+ records = []
for recordType in recordTypes:
for xmlPrincipal in self._accounts()[recordType].itervalues():
if xmlPrincipalMatches(xmlPrincipal):
@@ -345,9 +347,9 @@
# Load/cache record from its GUID
record = self.recordWithGUID(xmlPrincipal.guid)
if record:
- yield record
+ records.append(record)
+ return succeed(records)
-
def _addElement(self, parent, principal):
"""
Create an XML element from principal and add it as a child of parent
Modified: CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/stdconfig.py 2012-03-17 01:43:01 UTC (rev 8905)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/stdconfig.py 2012-03-17 03:37:14 UTC (rev 8906)
@@ -207,8 +207,10 @@
directoryAddressBookBackingServiceDefaultParams = {
- "twistedcaldav.directory.xmlfile.XMLDirectoryService": {
- "xmlFile": "/etc/carddavd/accounts.xml",
+ "twistedcaldav.directory.xmldirectorybacker.XMLDirectoryBackingService": {
+ "xmlFile": "accounts.xml",
+ "recordTypes": ("users",),
+ "statSeconds" : 15,
},
"twistedcaldav.directory.opendirectorybacker.OpenDirectoryBackingService": {
"queryPeopleRecords": True,
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20120316/104f3cf8/attachment-0001.html>
More information about the calendarserver-changes
mailing list