[CalendarServer-changes] [10613] CalendarServer/trunk/twext/who
source_changes at macosforge.org
source_changes at macosforge.org
Thu Jan 31 14:17:25 PST 2013
Revision: 10613
http://trac.calendarserver.org//changeset/10613
Author: wsanchez at apple.com
Date: 2013-01-31 14:17:24 -0800 (Thu, 31 Jan 2013)
Log Message:
-----------
Add XML implementation.
Added Paths:
-----------
CalendarServer/trunk/twext/who/test/test_xml.py
CalendarServer/trunk/twext/who/xml.py
Added: CalendarServer/trunk/twext/who/test/test_xml.py
===================================================================
--- CalendarServer/trunk/twext/who/test/test_xml.py (rev 0)
+++ CalendarServer/trunk/twext/who/test/test_xml.py 2013-01-31 22:17:24 UTC (rev 10613)
@@ -0,0 +1,73 @@
+##
+# Copyright (c) 2013 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.
+##
+
+"""
+XML directory service tests
+"""
+
+from twisted.python.filepath import FilePath
+from twext.who.xml import DirectoryService
+
+from twext.who.test import test_directory
+
+
+
+xmlRealmName = "Test Realm"
+
+
+
+class BaseTest(object):
+ def _testService(self):
+ if not hasattr(self, "_service"):
+ filePath = FilePath(self.mktemp())
+
+ filePath.setContent(
+ """<?xml version="1.0" encoding="utf-8"?>
+
+<directory realm="xyzzy">
+
+ <record type="user">
+ <uid>wsanchez</uid>
+ <short-name>wsanchez</short-name>
+ <short-name>wilfredo_sanchez</short-name>
+ <full-name>Wilfredo Sanchez</full-name>
+ <password>zehcnasw</password>
+ <email>wsanchez at calendarserver.org</email>
+ <email>wsanchez at example.com</email>
+ </record>
+
+ <record type="user">
+ <uid>glyph</uid>
+ <short-name>glyph</short-name>
+ <full-name>Glyph Lefkowitz</full-name>
+ <password>hpylg</password>
+ <email>glyph at calendarserver.org</email>
+ </record>
+
+</directory>
+"""
+ )
+
+ self._service = DirectoryService(filePath)
+ return self._service
+
+
+
+class DirectoryServiceTest(BaseTest, test_directory.DirectoryServiceTest):
+ def test_XYZZY(self):
+ service = self._testService()
+
+ service.loadRecords()
Added: CalendarServer/trunk/twext/who/xml.py
===================================================================
--- CalendarServer/trunk/twext/who/xml.py (rev 0)
+++ CalendarServer/trunk/twext/who/xml.py 2013-01-31 22:17:24 UTC (rev 10613)
@@ -0,0 +1,239 @@
+##
+# Copyright (c) 2006-2013 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.
+##
+
+from __future__ import absolute_import
+
+"""
+XML directory service implementation.
+"""
+
+__all__ = [
+ "DirectoryService",
+ "DirectoryRecord",
+]
+
+from xml.etree.ElementTree import parse as parseXML
+from xml.etree.ElementTree import ParseError as XMLParseError
+
+from twisted.python.constants import Values, ValueConstant
+
+from twext.who.idirectory import RecordType, FieldName
+from twext.who.idirectory import DirectoryServiceError
+from twext.who.directory import DirectoryService as BaseDirectoryService
+from twext.who.directory import DirectoryRecord
+
+
+
+##
+# XML Constants
+##
+
+class Element(Values):
+ directory = ValueConstant("directory")
+ record = ValueConstant("record")
+ uid = ValueConstant("uid")
+ guid = ValueConstant("guid")
+ shortName = ValueConstant("short-name")
+ fullName = ValueConstant("full-name")
+ emailAddress = ValueConstant("email")
+ password = ValueConstant("password")
+ member = ValueConstant("member-uid")
+
+ uid.fieldName = FieldName.uid
+ guid.fieldName = FieldName.guid
+ shortName.fieldName = FieldName.shortNames
+ fullName.fieldName = FieldName.fullNames
+ emailAddress.fieldName = FieldName.emailAddresses
+ password.fieldName = FieldName.password
+
+
+
+class Attribute(Values):
+ realm = ValueConstant("realm")
+ recordType = ValueConstant("type")
+
+
+
+class Value(Values):
+ # Booleans
+ true = ValueConstant("true")
+ false = ValueConstant("false")
+
+ # Record types
+ user = ValueConstant("user")
+ group = ValueConstant("group")
+
+ user.recordType = RecordType.user
+ group.recordType = RecordType.group
+
+
+
+##
+# Directory Service
+##
+
+class DirectoryService(BaseDirectoryService):
+ """
+ XML directory service.
+ """
+
+ ElementClass = Element
+ AttributeClass = Attribute
+ ValueClass = Value
+
+ indexedFields = (
+ FieldName.recordType,
+ FieldName.uid,
+ FieldName.guid,
+ FieldName.shortNames,
+ FieldName.emailAddresses,
+ )
+
+
+ def __init__(self, filePath, refreshInterval=4):
+ BaseDirectoryService.__init__(self, realmName=None)
+
+ self.filePath = filePath
+ self.refreshInterval = refreshInterval
+
+
+ def __repr__(self):
+ return "<%s %s>" % (
+ self.__class__.__name__,
+ self._realmName,
+ )
+
+
+ @property
+ def realmName(self):
+ if not hasattr(self, "_realmName"):
+ self.loadRecords()
+ return self._realmName
+
+ @realmName.setter
+ def realmName(self, value):
+ if value is not None:
+ raise AssertionError("realmName may not be set directly")
+
+ @property
+ def unknownRecordTypes(self):
+ if not hasattr(self, "_unknownRecordTypes"):
+ self.loadRecords()
+ return self._unknownRecordTypes
+
+ @property
+ def unknownFieldNames(self):
+ if not hasattr(self, "_unknownFieldNames"):
+ self.loadRecords()
+ return self._unknownFieldNames
+
+ @property
+ def index(self):
+ if not hasattr(self, "_index"):
+ self.loadRecords()
+ return self._index
+
+
+ def loadRecords(self):
+ #
+ # Open and parse the file
+ #
+ try:
+ fh = self.filePath.open()
+
+ try:
+ etree = parseXML(fh)
+ except XMLParseError, e:
+ raise DirectoryServiceError(e.getMessage())
+ finally:
+ fh.close()
+
+ #
+ # Pull data from DOM
+ #
+ directoryNode = etree.getroot()
+ if directoryNode.tag != self.ElementClass.directory.value:
+ raise DirectoryServiceError("Incorrect root element: %s" % (directoryNode.tag,))
+
+ def getAttribute(node, name):
+ return node.get(name, "").encode("utf-8")
+
+ realmName = getAttribute(directoryNode, self.AttributeClass.realm.value)
+
+ if not realmName:
+ raise DirectoryServiceError("No realm name.")
+
+ unknownRecordTypes = set()
+ unknownFieldNames = set()
+
+ records = set()
+
+ for recordNode in directoryNode.getchildren():
+ recordTypeAttribute = getAttribute(recordNode, self.AttributeClass.recordType.value)
+ if not recordTypeAttribute:
+ recordTypeAttribute = "user"
+
+ try:
+ recordType = self.ValueClass.lookupByValue(recordTypeAttribute).recordType
+ except (ValueError, AttributeError):
+ unknownRecordTypes.add(recordTypeAttribute)
+ break
+
+ fields = {}
+ fields[FieldName.recordType] = recordType
+
+ for fieldNode in recordNode.getchildren():
+ try:
+ fieldName = self.ElementClass.lookupByValue(fieldNode.tag).fieldName
+ except (ValueError, AttributeError):
+ unknownFieldNames.add(fieldNode.tag)
+
+ value = fieldNode.text.encode("utf-8")
+
+ if self.FieldNameClass.isMultiValue(fieldName):
+ values = fields.setdefault(fieldName, [])
+ values.append(value)
+ else:
+ fields[fieldName] = value
+
+ records.add(DirectoryRecord(self, fields))
+
+ #
+ # Store results
+ #
+
+ index = {}
+
+ for fieldName in self.indexedFields:
+ index[fieldName] = {}
+
+ for record in records:
+ for fieldName in self.indexedFields:
+ values = record.fields.get(fieldName, None)
+
+ if values is not None:
+ if not self.FieldNameClass.isMultiValue(fieldName):
+ values = (values,)
+
+ for value in values:
+ index[fieldName].setdefault(value, set()).add(record)
+
+ self._realmName = realmName
+
+ self._unknownRecordTypes = unknownRecordTypes
+ self._unknownFieldNames = unknownFieldNames
+
+ self._index = index
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130131/c6ae7bf9/attachment-0001.html>
More information about the calendarserver-changes
mailing list