[CalendarServer-changes] [13081] CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/ directory

source_changes at macosforge.org source_changes at macosforge.org
Mon Mar 31 19:02:50 PDT 2014


Revision: 13081
          http://trac.calendarserver.org//changeset/13081
Author:   wsanchez at apple.com
Date:     2014-03-31 19:02:50 -0700 (Mon, 31 Mar 2014)
Log Message:
-----------
Obsolete; no imports

Removed Paths:
-------------
    CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/directory/idirectory.py
    CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/directory/xmlaccountsparser.py
    CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/directory/xmlfile.py

Deleted: CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/directory/idirectory.py
===================================================================
--- CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/directory/idirectory.py	2014-04-01 01:41:35 UTC (rev 13080)
+++ CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/directory/idirectory.py	2014-04-01 02:02:50 UTC (rev 13081)
@@ -1,180 +0,0 @@
-##
-# Copyright (c) 2006-2014 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.
-##
-
-"""
-Directory service interfaces.
-"""
-
-__all__ = [
-    "IDirectoryService",
-    "IDirectoryRecord",
-]
-
-from zope.interface import Attribute, Interface
-
-class IDirectoryService(Interface):
-    """
-    Directory Service
-    """
-    realmName = Attribute("The name of the authentication realm this service represents.")
-    guid = Attribute("A GUID for this service.")
-
-    def recordTypes(): #@NoSelf
-        """
-        @return: a sequence of strings denoting the record types that
-            are kept in the directory.  For example: C{["users",
-            "groups", "resources"]}.
-        """
-
-    def listRecords(recordType): #@NoSelf
-        """
-        @param type: the type of records to retrieve.
-        @return: an iterable of records of the given type.
-        """
-
-    def recordWithShortName(recordType, shortName): #@NoSelf
-        """
-        @param recordType: the type of the record to look up.
-        @param shortName: the short name of the record to look up.
-        @return: an L{IDirectoryRecord} with the given short name, or
-            C{None} if no such record exists.
-        """
-
-    def recordWithUID(uid): #@NoSelf
-        """
-        @param uid: the UID of the record to look up.
-        @return: an L{IDirectoryRecord} with the given UID, or C{None}
-            if no such record exists.
-        """
-
-    def recordWithGUID(guid): #@NoSelf
-        """
-        @param guid: the GUID of the record to look up.
-        @return: an L{IDirectoryRecord} with the given GUID, or
-            C{None} if no such record exists.
-        """
-
-    def recordWithCalendarUserAddress(address): #@NoSelf
-        """
-        @param address: the calendar user address of the record to look up.
-        @type address: C{str}
-
-        @return: an L{IDirectoryRecord} with the given calendar user
-            address, or C{None} if no such record is found.  Note that
-            some directory services may not be able to locate records
-            by calendar user address, or may return partial results.
-            Note also that the calendar server may add to the list of
-            valid calendar user addresses for a user, and the
-            directory service may not be aware of these addresses.
-        """
-
-    def recordWithCachedGroupsAlias(recordType, alias): #@NoSelf
-        """
-        @param recordType: the type of the record to look up.
-        @param alias: the cached-groups alias of the record to look up.
-        @type alias: C{str}
-
-        @return: a deferred L{IDirectoryRecord} with the given cached-groups
-            alias, or C{None} if no such record is found.
-        """
-
-    def recordsMatchingFields(fields): #@NoSelf
-        """
-        @return: a deferred sequence of L{IDirectoryRecord}s which
-            match the given fields.
-        """
-
-    def recordsMatchingTokens(tokens, context=None): #@NoSelf
-        """
-        @param tokens: The tokens to search on
-        @type tokens: C{list} of C{str} (utf-8 bytes)
-
-        @param context: An indication of what the end user is searching for;
-            "attendee", "location", or None
-        @type context: C{str}
-
-        @return: a deferred sequence of L{IDirectoryRecord}s which match the
-            given tokens and optional context.
-
-            Each token is searched for within each record's full name and email
-            address; if each token is found within a record that record is
-            returned in the results.
-
-            If context is None, all record types are considered.  If context is
-            "location", only locations are considered.  If context is
-            "attendee", only users, groups, and resources are considered.
-        """
-
-    def setRealm(realmName): #@NoSelf
-        """
-        Set a new realm name for this (and nested services if any)
-
-        @param realmName: the realm name this service should use.
-        """
-
-
-
-class IDirectoryRecord(Interface):
-    """
-    Directory Record
-    """
-    service = Attribute("The L{IDirectoryService} this record exists in.")
-    recordType = Attribute("The type of this record.")
-    guid = Attribute("The GUID of this record.")
-    uid = Attribute("The UID of this record.")
-    enabled = Attribute("Determines whether this record should allow a principal to be created.")
-    serverID = Attribute("Identifies the server that actually hosts data for the record.")
-    shortNames = Attribute("The names for this record.")
-    authIDs = Attribute("Alternative security identities for this record.")
-    fullName = Attribute("The full name of this record.")
-    firstName = Attribute("The first name of this record.")
-    lastName = Attribute("The last name of this record.")
-    emailAddresses = Attribute("The email addresses of this record.")
-    enabledForCalendaring = Attribute("Determines whether this record creates a principal with a calendar home.")
-    enabledForAddressBooks = Attribute("Determines whether this record creates a principal with an address book home.")
-    calendarUserAddresses = Attribute(
-        """
-        An iterable of C{str}s representing calendar user addresses for this
-        L{IDirectoryRecord}.
-
-        A "calendar user address", as defined by U{RFC 2445 section
-        4.3.3<http://xml.resource.org/public/rfc/html/rfc2445.html#anchor50>},
-        is simply an URI which identifies this user.  Some of these URIs are
-        relative references to URLs from the root of the calendar server's HTTP
-        hierarchy.
-        """
-    )
-
-    def members(): #@NoSelf
-        """
-        @return: an iterable of L{IDirectoryRecord}s for the members of this
-            (group) record.
-        """
-
-    def groups(): #@NoSelf
-        """
-        @return: an iterable of L{IDirectoryRecord}s for the groups this
-            record is a member of.
-        """
-
-    def verifyCredentials(credentials): #@NoSelf
-        """
-        Verify that the given credentials can authenticate the principal
-        represented by this record.
-        @param credentials: the credentials to authenticate with.
-        @return: C{True} if the given credentials match this record,
-            C{False} otherwise.
-        """

Deleted: CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/directory/xmlaccountsparser.py
===================================================================
--- CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/directory/xmlaccountsparser.py	2014-04-01 01:41:35 UTC (rev 13080)
+++ CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/directory/xmlaccountsparser.py	2014-04-01 02:02:50 UTC (rev 13081)
@@ -1,283 +0,0 @@
-##
-# Copyright (c) 2006-2014 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 based user/group/resource configuration file handling.
-"""
-
-__all__ = [
-    "XMLAccountsParser",
-]
-
-from twext.python.filepath import CachingFilePath as FilePath
-
-from twext.python.log import Logger
-
-from twistedcaldav.directory.directory import DirectoryService
-from twistedcaldav.directory.util import normalizeUUID
-from twistedcaldav.xmlutil import readXML
-
-import re
-import hashlib
-
-log = Logger()
-
-ELEMENT_ACCOUNTS = "accounts"
-ELEMENT_USER = "user"
-ELEMENT_GROUP = "group"
-ELEMENT_LOCATION = "location"
-ELEMENT_RESOURCE = "resource"
-ELEMENT_ADDRESS = "address"
-
-ELEMENT_SHORTNAME = "uid"
-ELEMENT_GUID = "guid"
-ELEMENT_PASSWORD = "password"
-ELEMENT_NAME = "name"
-ELEMENT_FIRST_NAME = "first-name"
-ELEMENT_LAST_NAME = "last-name"
-ELEMENT_EMAIL_ADDRESS = "email-address"
-ELEMENT_MEMBERS = "members"
-ELEMENT_MEMBER = "member"
-ELEMENT_EXTRAS = "extras"
-
-ATTRIBUTE_REALM = "realm"
-ATTRIBUTE_REPEAT = "repeat"
-ATTRIBUTE_RECORDTYPE = "type"
-
-VALUE_TRUE = "true"
-VALUE_FALSE = "false"
-
-RECORD_TYPES = {
-    ELEMENT_USER     : DirectoryService.recordType_users,
-    ELEMENT_GROUP    : DirectoryService.recordType_groups,
-    ELEMENT_LOCATION : DirectoryService.recordType_locations,
-    ELEMENT_RESOURCE : DirectoryService.recordType_resources,
-    ELEMENT_ADDRESS  : DirectoryService.recordType_addresses,
-}
-
-class XMLAccountsParser(object):
-    """
-    XML account configuration file parser.
-    """
-    def __repr__(self):
-        return "<%s %r>" % (self.__class__.__name__, self.xmlFile)
-
-
-    def __init__(self, xmlFile, externalUpdate=True):
-
-        if type(xmlFile) is str:
-            xmlFile = FilePath(xmlFile)
-
-        self.xmlFile = xmlFile
-        self.realm = None
-        self.items = {}
-
-        for recordType in RECORD_TYPES.values():
-            self.items[recordType] = {}
-
-        # Read in XML
-        try:
-            _ignore_tree, accounts_node = readXML(self.xmlFile.path, ELEMENT_ACCOUNTS)
-        except ValueError, e:
-            raise RuntimeError("XML parse error for '%s' because: %s" % (self.xmlFile, e,))
-        self._parseXML(accounts_node)
-
-
-    def _parseXML(self, node):
-        """
-        Parse the XML root node from the accounts configuration document.
-        @param node: the L{Node} to parse.
-        """
-        self.realm = node.get(ATTRIBUTE_REALM, "").encode("utf-8")
-
-        def updateMembership(group):
-            # Update group membership
-            for recordType, shortName in group.members:
-                item = self.items[recordType].get(shortName)
-                if item is not None:
-                    item.groups.add(group.shortNames[0])
-
-        for child in node:
-            try:
-                recordType = RECORD_TYPES[child.tag]
-            except KeyError:
-                raise RuntimeError("Unknown account type: %s" % (child.tag,))
-
-            repeat = int(child.get(ATTRIBUTE_REPEAT, 0))
-
-            principal = XMLAccountRecord(recordType)
-            principal.parseXML(child)
-            if repeat > 0:
-                for i in xrange(1, repeat + 1):
-                    newprincipal = principal.repeat(i)
-                    self.items[recordType][newprincipal.shortNames[0]] = newprincipal
-            else:
-                self.items[recordType][principal.shortNames[0]] = principal
-
-        # Do reverse membership mapping only after all records have been read in
-        for records in self.items.itervalues():
-            for principal in records.itervalues():
-                updateMembership(principal)
-
-
-
-class XMLAccountRecord (object):
-    """
-    Contains provision information for one user.
-    """
-    def __init__(self, recordType):
-        """
-        @param recordType: record type for directory entry.
-        """
-        self.recordType = recordType
-        self.shortNames = []
-        self.guid = None
-        self.password = None
-        self.fullName = None
-        self.firstName = None
-        self.lastName = None
-        self.emailAddresses = set()
-        self.members = set()
-        self.groups = set()
-        self.extras = {}
-
-
-    def repeat(self, ctr):
-        """
-        Create another object like this but with all text items having % substitution
-        done on them with the numeric value provided.
-        @param ctr: an integer to substitute into text.
-        """
-
-        # Regular expression which matches ~ followed by a number
-        matchNumber = re.compile(r"(~\d+)")
-
-        def expand(text, ctr):
-            """
-            Returns a string where ~<number> is replaced by the first <number>
-            characters from the md5 hexdigest of str(ctr), e.g.::
-
-                expand("~9 foo", 1)
-
-            returns::
-
-                "c4ca4238a foo"
-
-            ...since "c4ca4238a" is the first 9 characters of::
-
-                hashlib.md5(str(1)).hexdigest()
-
-            If <number> is larger than 32, the hash will repeat as needed.
-            """
-            if text:
-                m = matchNumber.search(text)
-                if m:
-                    length = int(m.group(0)[1:])
-                    hash = hashlib.md5(str(ctr)).hexdigest()
-                    string = (hash * ((length / 32) + 1))[:-(32 - (length % 32))]
-                    return text.replace(m.group(0), string)
-            return text
-
-        shortNames = []
-        for shortName in self.shortNames:
-            if shortName.find("%") != -1:
-                shortNames.append(shortName % ctr)
-            else:
-                shortNames.append(shortName)
-        if self.guid and self.guid.find("%") != -1:
-            guid = self.guid % ctr
-        else:
-            guid = self.guid
-        if self.password.find("%") != -1:
-            password = self.password % ctr
-        else:
-            password = self.password
-        if self.fullName.find("%") != -1:
-            fullName = self.fullName % ctr
-        else:
-            fullName = self.fullName
-        fullName = expand(fullName, ctr)
-        if self.firstName and self.firstName.find("%") != -1:
-            firstName = self.firstName % ctr
-        else:
-            firstName = self.firstName
-        firstName = expand(firstName, ctr)
-        if self.lastName and self.lastName.find("%") != -1:
-            lastName = self.lastName % ctr
-        else:
-            lastName = self.lastName
-        lastName = expand(lastName, ctr)
-        emailAddresses = set()
-        for emailAddr in self.emailAddresses:
-            emailAddr = expand(emailAddr, ctr)
-            if emailAddr.find("%") != -1:
-                emailAddresses.add(emailAddr % ctr)
-            else:
-                emailAddresses.add(emailAddr)
-
-        result = XMLAccountRecord(self.recordType)
-        result.shortNames = shortNames
-        result.guid = normalizeUUID(guid)
-        result.password = password
-        result.fullName = fullName
-        result.firstName = firstName
-        result.lastName = lastName
-        result.emailAddresses = emailAddresses
-        result.members = self.members
-        result.extras = self.extras
-        return result
-
-
-    def parseXML(self, node):
-        for child in node:
-            if child.tag == ELEMENT_SHORTNAME:
-                self.shortNames.append(child.text.encode("utf-8"))
-            elif child.tag == ELEMENT_GUID:
-                self.guid = normalizeUUID(child.text.encode("utf-8"))
-                if len(self.guid) < 4:
-                    self.guid += "?" * (4 - len(self.guid))
-            elif child.tag == ELEMENT_PASSWORD:
-                self.password = child.text.encode("utf-8")
-            elif child.tag == ELEMENT_NAME:
-                self.fullName = child.text.encode("utf-8")
-            elif child.tag == ELEMENT_FIRST_NAME:
-                self.firstName = child.text.encode("utf-8")
-            elif child.tag == ELEMENT_LAST_NAME:
-                self.lastName = child.text.encode("utf-8")
-            elif child.tag == ELEMENT_EMAIL_ADDRESS:
-                self.emailAddresses.add(child.text.encode("utf-8").lower())
-            elif child.tag == ELEMENT_MEMBERS:
-                self._parseMembers(child, self.members)
-            elif child.tag == ELEMENT_EXTRAS:
-                self._parseExtras(child, self.extras)
-            else:
-                raise RuntimeError("Unknown account attribute: %s" % (child.tag,))
-
-        if not self.shortNames:
-            self.shortNames.append(self.guid)
-
-
-    def _parseMembers(self, node, addto):
-        for child in node:
-            if child.tag == ELEMENT_MEMBER:
-                recordType = child.get(ATTRIBUTE_RECORDTYPE, DirectoryService.recordType_users)
-                addto.add((recordType, child.text.encode("utf-8")))
-
-
-    def _parseExtras(self, node, addto):
-        for child in node:
-            addto[child.tag] = child.text.encode("utf-8")

Deleted: CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/directory/xmlfile.py
===================================================================
--- CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/directory/xmlfile.py	2014-04-01 01:41:35 UTC (rev 13080)
+++ CalendarServer/branches/users/sagen/move2who-4/twistedcaldav/directory/xmlfile.py	2014-04-01 02:02:50 UTC (rev 13081)
@@ -1,633 +0,0 @@
-##
-# Copyright (c) 2006-2014 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 based user/group/resource directory service implementation.
-"""
-
-__all__ = [
-    "XMLDirectoryService",
-]
-
-from time import time
-import grp
-import os
-import pwd
-import types
-
-from twisted.cred.credentials import UsernamePassword
-from txweb2.auth.digest import DigestedCredentials
-from twext.python.filepath import CachingFilePath as FilePath
-from twistedcaldav.config import config
-from twisted.internet.defer import succeed
-
-from twistedcaldav.config import fullServerPath
-from twistedcaldav.directory.directory import DirectoryService, DirectoryRecord, DirectoryError
-from twistedcaldav.directory.xmlaccountsparser import XMLAccountsParser, XMLAccountRecord
-from twistedcaldav.directory.util import normalizeUUID
-from txdav.caldav.datastore.scheduling.cuaddress import normalizeCUAddr
-from twistedcaldav.xmlutil import addSubElement, createElement, elementToXML
-from uuid import uuid4
-
-
-class XMLDirectoryService(DirectoryService):
-    """
-    XML based implementation of L{IDirectoryService}.
-    """
-    baseGUID = "9CA8DEC5-5A17-43A9-84A8-BE77C1FB9172"
-
-    realmName = None
-
-    INDEX_TYPE_GUID = "guid"
-    INDEX_TYPE_SHORTNAME = "shortname"
-    INDEX_TYPE_CUA = "cua"
-    INDEX_TYPE_AUTHID = "authid"
-
-
-    def __repr__(self):
-        return "<%s %r: %r>" % (self.__class__.__name__, self.realmName, self.xmlFile)
-
-
-    def __init__(self, params, alwaysStat=False):
-
-        defaults = {
-            'xmlFile' : None,
-            'directoryBackedAddressBook': None,
-            'recordTypes' : (
-                self.recordType_users,
-                self.recordType_groups,
-                self.recordType_locations,
-                self.recordType_resources,
-                self.recordType_addresses,
-            ),
-            'realmName' : '/Search',
-            'statSeconds' : 15,
-            'augmentService' : None,
-            'groupMembershipCache' : None,
-        }
-        ignored = None
-        params = self.getParams(params, defaults, ignored)
-
-        self._recordTypes = params['recordTypes']
-        self.realmName = params['realmName']
-        self.statSeconds = params['statSeconds']
-        self.augmentService = params['augmentService']
-        self.groupMembershipCache = params['groupMembershipCache']
-
-        super(XMLDirectoryService, self).__init__()
-
-        xmlFile = fullServerPath(config.DataRoot, params.get("xmlFile"))
-        if type(xmlFile) is str:
-            xmlFile = FilePath(xmlFile)
-
-        if not xmlFile.exists():
-            xmlFile.setContent("""<?xml version="1.0" encoding="utf-8"?>
-
-<accounts realm="%s">
-</accounts>
-""" % (self.realmName,))
-
-        uid = -1
-        if config.UserName:
-            try:
-                uid = pwd.getpwnam(config.UserName).pw_uid
-            except KeyError:
-                self.log.error("User not found: %s" % (config.UserName,))
-
-        gid = -1
-        if config.GroupName:
-            try:
-                gid = grp.getgrnam(config.GroupName).gr_gid
-            except KeyError:
-                self.log.error("Group not found: %s" % (config.GroupName,))
-
-        if uid != -1 and gid != -1:
-            os.chown(xmlFile.path, uid, gid)
-
-        self.xmlFile = xmlFile
-        self._fileInfo = None
-        self._lastCheck = 0
-        self._alwaysStat = alwaysStat
-        self.directoryBackedAddressBook = params.get('directoryBackedAddressBook')
-        self._initIndexes()
-        self._accounts()
-
-
-    def _initIndexes(self):
-        """
-        Create empty indexes
-        """
-        self.records = {}
-        self.recordIndexes = {}
-
-        for recordType in self.recordTypes():
-            self.records[recordType] = set()
-            self.recordIndexes[recordType] = {
-                self.INDEX_TYPE_GUID     : {},
-                self.INDEX_TYPE_SHORTNAME: {},
-                self.INDEX_TYPE_CUA      : {},
-                self.INDEX_TYPE_AUTHID   : {},
-            }
-
-
-    def _accounts(self):
-        """
-        Parses XML file, creates XMLDirectoryRecords and indexes them, and
-        because some other code in this module still works directly with
-        XMLAccountRecords as returned by XMLAccountsParser, returns a list
-        of XMLAccountRecords.
-
-        The XML file is only stat'ed at most every self.statSeconds, and is
-        only reparsed if it's been modified.
-
-        FIXME: don't return XMLAccountRecords, and have any code in this module
-        which currently does work with XMLAccountRecords, modify such code to
-        use XMLDirectoryRecords instead.
-        """
-        currentTime = time()
-        if self._alwaysStat or currentTime - self._lastCheck > self.statSeconds:
-            self.xmlFile.restat()
-            self._lastCheck = currentTime
-            fileInfo = (self.xmlFile.getmtime(), self.xmlFile.getsize())
-            if fileInfo != self._fileInfo:
-                self._initIndexes()
-                parser = XMLAccountsParser(self.xmlFile)
-                self._parsedAccounts = parser.items
-                self.realmName = parser.realm
-                self._fileInfo = fileInfo
-
-                for accountDict in self._parsedAccounts.itervalues():
-                    for xmlAccountRecord in accountDict.itervalues():
-                        if xmlAccountRecord.recordType not in self.recordTypes():
-                            continue
-                        record = XMLDirectoryRecord(
-                            service=self,
-                            recordType=xmlAccountRecord.recordType,
-                            shortNames=tuple(xmlAccountRecord.shortNames),
-                            xmlPrincipal=xmlAccountRecord,
-                        )
-                        if self.augmentService is not None:
-                            d = self.augmentService.getAugmentRecord(record.guid,
-                                record.recordType)
-                            d.addCallback(lambda x: record.addAugmentInformation(x))
-
-                        self._addToIndex(record)
-
-        return self._parsedAccounts
-
-
-    def _addToIndex(self, record):
-        """
-        Index the record by GUID, shortName(s), authID(s) and CUA(s)
-        """
-
-        self.recordIndexes[record.recordType][self.INDEX_TYPE_GUID][record.guid] = record
-        for shortName in record.shortNames:
-            self.recordIndexes[record.recordType][self.INDEX_TYPE_SHORTNAME][shortName] = record
-        for authID in record.authIDs:
-            self.recordIndexes[record.recordType][self.INDEX_TYPE_AUTHID][authID] = record
-        for cua in record.calendarUserAddresses:
-            cua = normalizeCUAddr(cua)
-            self.recordIndexes[record.recordType][self.INDEX_TYPE_CUA][cua] = record
-        self.records[record.recordType].add(record)
-
-
-    def _removeFromIndex(self, record):
-        """
-        Removes a record from all indexes.  Note this is only used for unit
-        testing, to simulate a user being removed from the directory.
-        """
-        del self.recordIndexes[record.recordType][self.INDEX_TYPE_GUID][record.guid]
-        for shortName in record.shortNames:
-            del self.recordIndexes[record.recordType][self.INDEX_TYPE_SHORTNAME][shortName]
-        for authID in record.authIDs:
-            del self.recordIndexes[record.recordType][self.INDEX_TYPE_AUTHID][authID]
-        for cua in record.calendarUserAddresses:
-            cua = normalizeCUAddr(cua)
-            del self.recordIndexes[record.recordType][self.INDEX_TYPE_CUA][cua]
-        if record in self.records[record.recordType]:
-            self.records[record.recordType].remove(record)
-
-
-    def _lookupInIndex(self, recordType, indexType, key):
-        """
-        Look for an existing record of the given recordType with the key for
-        the given index type.  Returns None if no match.
-        """
-        self._accounts()
-        return self.recordIndexes.get(recordType, {}).get(indexType, {}).get(key, None)
-
-
-    def _initCaches(self):
-        """
-        Invalidates the indexes
-        """
-        self._lastCheck = 0
-        self._initIndexes()
-
-
-    def _forceReload(self):
-        """
-        Invalidates the indexes, re-reads the XML file and re-indexes
-        """
-        self._initCaches()
-        self._fileInfo = None
-        return self._accounts()
-
-
-    def recordWithShortName(self, recordType, shortName):
-        return self._lookupInIndex(recordType, self.INDEX_TYPE_SHORTNAME, shortName)
-
-
-    def recordWithAuthID(self, authID):
-        for recordType in self.recordTypes():
-            record = self._lookupInIndex(recordType, self.INDEX_TYPE_AUTHID, authID)
-            if record is not None:
-                return record
-        return None
-
-
-    def recordWithGUID(self, guid):
-        guid = normalizeUUID(guid)
-        for recordType in self.recordTypes():
-            record = self._lookupInIndex(recordType, self.INDEX_TYPE_GUID, guid)
-            if record is not None:
-                return record
-        return None
-
-    recordWithUID = recordWithGUID
-
-    def createCache(self):
-        """
-        No-op to pacify addressbook backing.
-        """
-
-    def recordTypes(self):
-        return self._recordTypes
-
-
-    def listRecords(self, recordType):
-        self._accounts()
-        return self.records[recordType]
-
-
-    def recordsMatchingFields(self, fields, operand="or", recordType=None):
-        # Default, brute force method search of underlying XML data
-
-        def fieldMatches(fieldValue, value, caseless, matchType):
-            if fieldValue is None:
-                return False
-            elif type(fieldValue) in types.StringTypes:
-                fieldValue = (fieldValue,)
-
-            for testValue in fieldValue:
-                if caseless:
-                    testValue = testValue.lower()
-                    value = value.lower()
-
-                if matchType == 'starts-with':
-                    if testValue.startswith(value):
-                        return True
-                elif matchType == 'contains':
-                    try:
-                        testValue.index(value)
-                        return True
-                    except ValueError:
-                        pass
-                else: # exact
-                    if testValue == value:
-                        return True
-
-            return False
-
-        def xmlPrincipalMatches(xmlPrincipal):
-            if operand == "and":
-                for fieldName, value, caseless, matchType in fields:
-                    try:
-                        fieldValue = getattr(xmlPrincipal, fieldName)
-                        if not fieldMatches(fieldValue, value, caseless, matchType):
-                            return False
-                    except AttributeError:
-                        # No property => no match
-                        return False
-                # we hit on every property
-                return True
-            else: # "or"
-                for fieldName, value, caseless, matchType in fields:
-                    try:
-                        fieldValue = getattr(xmlPrincipal, fieldName)
-                        if fieldMatches(fieldValue, value, caseless, matchType):
-                            return True
-                    except AttributeError:
-                        # No value
-                        pass
-                # we didn't hit any
-                return False
-
-        if recordType is None:
-            recordTypes = list(self.recordTypes())
-        else:
-            recordTypes = (recordType,)
-
-        records = []
-        for recordType in recordTypes:
-            for xmlPrincipal in self._accounts()[recordType].itervalues():
-                if xmlPrincipalMatches(xmlPrincipal):
-
-                    # Load/cache record from its GUID
-                    record = self.recordWithGUID(xmlPrincipal.guid)
-                    if 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
-        """
-
-        # TODO: derive this from xmlaccountsparser.py
-        xmlTypes = {
-            'users'     : 'user',
-            'groups'    : 'group',
-            'locations' : 'location',
-            'resources' : 'resource',
-            'addresses' : 'address',
-        }
-        xmlType = xmlTypes[principal.recordType]
-
-        element = addSubElement(parent, xmlType)
-        for value in principal.shortNames:
-            addSubElement(element, "uid", text=value.decode("utf-8"))
-        addSubElement(element, "guid", text=principal.guid)
-        if principal.fullName is not None:
-            addSubElement(element, "name", text=principal.fullName.decode("utf-8"))
-        if principal.firstName is not None:
-            addSubElement(element, "first-name", text=principal.firstName.decode("utf-8"))
-        if principal.lastName is not None:
-            addSubElement(element, "last-name", text=principal.lastName.decode("utf-8"))
-        for value in principal.emailAddresses:
-            addSubElement(element, "email-address", text=value.decode("utf-8"))
-        if principal.extras:
-            extrasElement = addSubElement(element, "extras")
-            for key, value in principal.extras.iteritems():
-                addSubElement(extrasElement, key, text=value.decode("utf-8"))
-
-        return element
-
-
-    def _persistRecords(self, element):
-
-        def indent(elem, level=0):
-            i = "\n" + level * "  "
-            if len(elem):
-                if not elem.text or not elem.text.strip():
-                    elem.text = i + "  "
-                if not elem.tail or not elem.tail.strip():
-                    elem.tail = i
-                for elem in elem:
-                    indent(elem, level + 1)
-                if not elem.tail or not elem.tail.strip():
-                    elem.tail = i
-            else:
-                if level and (not elem.tail or not elem.tail.strip()):
-                    elem.tail = i
-
-        indent(element)
-
-        self.xmlFile.setContent(elementToXML(element))
-
-        # Fix up the file ownership because setContent doesn't maintain it
-        uid = -1
-        if config.UserName:
-            try:
-                uid = pwd.getpwnam(config.UserName).pw_uid
-            except KeyError:
-                self.log.error("User not found: %s" % (config.UserName,))
-
-        gid = -1
-        if config.GroupName:
-            try:
-                gid = grp.getgrnam(config.GroupName).gr_gid
-            except KeyError:
-                self.log.error("Group not found: %s" % (config.GroupName,))
-
-        if uid != -1 and gid != -1:
-            os.chown(self.xmlFile.path, uid, gid)
-
-
-    def createRecord(self, recordType, guid=None, shortNames=(), authIDs=set(),
-        fullName=None, firstName=None, lastName=None, emailAddresses=set(),
-        uid=None, password=None, **kwargs):
-        """
-        Create and persist a record using the provided information.  In this
-        XML-based implementation, the xml accounts are read in and converted
-        to elementtree elements, a new element is added for the new record,
-        and the document is serialized to disk.
-        """
-        if guid is None:
-            guid = str(uuid4())
-        guid = normalizeUUID(guid)
-
-        if not shortNames:
-            shortNames = (guid,)
-
-        # Make sure latest XML records are read in
-        accounts = self._forceReload()
-
-        accountsElement = createElement("accounts", realm=self.realmName)
-        for recType in self.recordTypes():
-            for xmlPrincipal in accounts[recType].itervalues():
-                if xmlPrincipal.guid == guid:
-                    raise DirectoryError("Duplicate guid: %s" % (guid,))
-                for shortName in shortNames:
-                    if shortName in xmlPrincipal.shortNames:
-                        raise DirectoryError("Duplicate shortName: %s" %
-                            (shortName,))
-                self._addElement(accountsElement, xmlPrincipal)
-
-        xmlPrincipal = XMLAccountRecord(recordType)
-        xmlPrincipal.shortNames = shortNames
-        xmlPrincipal.guid = guid
-        xmlPrincipal.password = password
-        xmlPrincipal.fullName = fullName
-        xmlPrincipal.firstName = firstName
-        xmlPrincipal.lastName = lastName
-        xmlPrincipal.emailAddresses = emailAddresses
-        xmlPrincipal.extras = kwargs
-        self._addElement(accountsElement, xmlPrincipal)
-
-        self._persistRecords(accountsElement)
-        self._forceReload()
-        return self.recordWithGUID(guid)
-
-
-    def destroyRecord(self, recordType, guid=None):
-        """
-        Remove the record matching guid.  In this XML-based implementation,
-        the xml accounts are read in and those not matching the given guid are
-        converted to elementtree elements, then the document is serialized to
-        disk.
-        """
-
-        guid = normalizeUUID(guid)
-
-        # Make sure latest XML records are read in
-        accounts = self._forceReload()
-
-        accountsElement = createElement("accounts", realm=self.realmName)
-        for recType in self.recordTypes():
-
-            for xmlPrincipal in accounts[recType].itervalues():
-                if xmlPrincipal.guid != guid:
-                    self._addElement(accountsElement, xmlPrincipal)
-
-        self._persistRecords(accountsElement)
-        self._forceReload()
-
-
-    def updateRecord(self, recordType, guid=None, shortNames=(), authIDs=set(),
-        fullName=None, firstName=None, lastName=None, emailAddresses=set(),
-        uid=None, password=None, **kwargs):
-        """
-        Update the record matching guid.  In this XML-based implementation,
-        the xml accounts are read in and converted to elementtree elements.
-        The account matching the given guid is replaced, then the document
-        is serialized to disk.
-        """
-
-        guid = normalizeUUID(guid)
-
-        # Make sure latest XML records are read in
-        accounts = self._forceReload()
-
-        accountsElement = createElement("accounts", realm=self.realmName)
-        for recType in self.recordTypes():
-
-            for xmlPrincipal in accounts[recType].itervalues():
-                if xmlPrincipal.guid == guid:
-                    # Replace this record
-                    xmlPrincipal.shortNames = shortNames
-                    xmlPrincipal.password = password
-                    xmlPrincipal.fullName = fullName
-                    xmlPrincipal.firstName = firstName
-                    xmlPrincipal.lastName = lastName
-                    xmlPrincipal.emailAddresses = emailAddresses
-                    xmlPrincipal.extras = kwargs
-                    self._addElement(accountsElement, xmlPrincipal)
-                else:
-                    self._addElement(accountsElement, xmlPrincipal)
-
-        self._persistRecords(accountsElement)
-        self._forceReload()
-        return self.recordWithGUID(guid)
-
-
-    def createRecords(self, data):
-        """
-        Create records in bulk
-        """
-
-        # Make sure latest XML records are read in
-        accounts = self._forceReload()
-
-        knownGUIDs = {}
-        knownShortNames = {}
-
-        accountsElement = createElement("accounts", realm=self.realmName)
-        for recType in self.recordTypes():
-            for xmlPrincipal in accounts[recType].itervalues():
-                self._addElement(accountsElement, xmlPrincipal)
-                knownGUIDs[xmlPrincipal.guid] = 1
-                for shortName in xmlPrincipal.shortNames:
-                    knownShortNames[shortName] = 1
-
-        for recordType, recordData in data:
-            guid = recordData["guid"]
-            if guid is None:
-                guid = str(uuid4())
-
-            shortNames = recordData["shortNames"]
-            if not shortNames:
-                shortNames = (guid,)
-
-            if guid in knownGUIDs:
-                raise DirectoryError("Duplicate guid: %s" % (guid,))
-
-            for shortName in shortNames:
-                if shortName in knownShortNames:
-                    raise DirectoryError("Duplicate shortName: %s" %
-                        (shortName,))
-
-            xmlPrincipal = XMLAccountRecord(recordType)
-            xmlPrincipal.shortNames = shortNames
-            xmlPrincipal.guid = guid
-            xmlPrincipal.fullName = recordData["fullName"]
-            self._addElement(accountsElement, xmlPrincipal)
-
-        self._persistRecords(accountsElement)
-        self._forceReload()
-
-
-
-class XMLDirectoryRecord(DirectoryRecord):
-    """
-    XML based implementation implementation of L{IDirectoryRecord}.
-    """
-    def __init__(self, service, recordType, shortNames, xmlPrincipal):
-        super(XMLDirectoryRecord, self).__init__(
-            service=service,
-            recordType=recordType,
-            guid=xmlPrincipal.guid,
-            shortNames=shortNames,
-            fullName=xmlPrincipal.fullName,
-            firstName=xmlPrincipal.firstName,
-            lastName=xmlPrincipal.lastName,
-            emailAddresses=xmlPrincipal.emailAddresses,
-            **xmlPrincipal.extras
-        )
-
-        self.password = xmlPrincipal.password
-        self._members = xmlPrincipal.members
-        self._groups = xmlPrincipal.groups
-
-
-    def members(self):
-        for recordType, shortName in self._members:
-            yield self.service.recordWithShortName(recordType, shortName)
-
-
-    def groups(self):
-        for shortName in self._groups:
-            yield self.service.recordWithShortName(DirectoryService.recordType_groups, shortName)
-
-
-    def memberGUIDs(self):
-        results = set()
-        for recordType, shortName in self._members:
-            record = self.service.recordWithShortName(recordType, shortName)
-            results.add(record.guid)
-        return results
-
-
-    def verifyCredentials(self, credentials):
-        if self.enabled:
-            if isinstance(credentials, UsernamePassword):
-                return credentials.password == self.password
-            if isinstance(credentials, DigestedCredentials):
-                return credentials.checkPassword(self.password)
-
-        return super(XMLDirectoryRecord, self).verifyCredentials(credentials)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20140331/0f435711/attachment-0001.html>


More information about the calendarserver-changes mailing list