[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