[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