[CalendarServer-changes] [12835] CalendarServer/branches/users/sagen/move2who
source_changes at macosforge.org
source_changes at macosforge.org
Thu Mar 6 17:22:21 PST 2014
Revision: 12835
http://trac.calendarserver.org//changeset/12835
Author: sagen at apple.com
Date: 2014-03-06 17:22:21 -0800 (Thu, 06 Mar 2014)
Log Message:
-----------
Halfway through rewrite of calendarserver_manage_principals. Added recordsMatchingTokens()
Modified Paths:
--------------
CalendarServer/branches/users/sagen/move2who/calendarserver/tap/caldav.py
CalendarServer/branches/users/sagen/move2who/calendarserver/tools/principals.py
CalendarServer/branches/users/sagen/move2who/calendarserver/tools/util.py
CalendarServer/branches/users/sagen/move2who/txdav/dps/client.py
CalendarServer/branches/users/sagen/move2who/txdav/dps/commands.py
CalendarServer/branches/users/sagen/move2who/txdav/dps/server.py
CalendarServer/branches/users/sagen/move2who/txdav/dps/test/test_client.py
Modified: CalendarServer/branches/users/sagen/move2who/calendarserver/tap/caldav.py
===================================================================
--- CalendarServer/branches/users/sagen/move2who/calendarserver/tap/caldav.py 2014-03-06 23:06:36 UTC (rev 12834)
+++ CalendarServer/branches/users/sagen/move2who/calendarserver/tap/caldav.py 2014-03-07 01:22:21 UTC (rev 12835)
@@ -549,8 +549,8 @@
self.monitor.addProcessObject(process, PARENT_ENVIRONMENT)
if (
- config.DirectoryProxy.Enabled and
- config.DirectoryProxy.SocketPath != ""
+ config.DirectoryProxy.Enabled and
+ config.DirectoryProxy.SocketPath != ""
):
log.info("Adding directory proxy service")
Modified: CalendarServer/branches/users/sagen/move2who/calendarserver/tools/principals.py
===================================================================
--- CalendarServer/branches/users/sagen/move2who/calendarserver/tools/principals.py 2014-03-06 23:06:36 UTC (rev 12834)
+++ CalendarServer/branches/users/sagen/move2who/calendarserver/tools/principals.py 2014-03-07 01:22:21 UTC (rev 12835)
@@ -26,17 +26,16 @@
from twisted.internet import reactor
from twisted.internet.defer import inlineCallbacks, returnValue, succeed
from txdav.xml import element as davxml
+from txdav.who.delegates import addDelegate, removeDelegate
-from txdav.xml.base import decodeXMLName, encodeXMLName
from twistedcaldav.config import config
-from twistedcaldav.directory.directory import UnknownRecordTypeError, DirectoryError
+from twistedcaldav.directory.directory import UnknownRecordTypeError
from txdav.who.groups import schedulePolledGroupCachingUpdate
from calendarserver.tools.util import (
- booleanArgument, proxySubprincipal, action_addProxyPrincipal,
- principalForPrincipalID, prettyPrincipal, ProxyError,
- action_removeProxyPrincipal
+ booleanArgument, proxySubprincipal,
+ recordForPrincipalID, prettyPrincipal, prettyRecord, ProxyError
)
from twistedcaldav.directory.augment import allowedAutoScheduleModes
@@ -74,10 +73,10 @@
print(" --search <search-string>: search for matching principals")
print(" --list-principal-types: list all of the known principal types")
print(" --list-principals type: list all principals of the given type")
- print(" --read-property=property: read DAV property (eg.: {DAV:}group-member-set)")
print(" --list-read-proxies: list proxies with read-only access")
print(" --list-write-proxies: list proxies with read-write access")
print(" --list-proxies: list all proxies")
+ print(" --list-proxy-for: principals this principal is a proxy for")
print(" --add-read-proxy=principal: add a read-only proxy")
print(" --add-write-proxy=principal: add a read-write proxy")
print(" --remove-proxy=principal: remove a proxy")
@@ -118,33 +117,32 @@
resource, directory, store, and whatever has been assigned to "params".
"""
if self.function is not None:
- rootResource = self.rootResource()
- directory = rootResource.getDirectory()
- yield self.function(rootResource, directory, self.store, *self.params)
+ yield self.function(self.store, *self.params)
attrMap = {
- 'GeneratedUID' : { 'attr' : 'guid', },
- 'RealName' : { 'attr' : 'fullName', },
- 'RecordName' : { 'attr' : 'shortNames', },
- 'AutoSchedule' : { 'attr' : 'autoSchedule', },
- 'AutoAcceptGroup' : { 'attr' : 'autoAcceptGroup', },
+ 'GeneratedUID': {'attr': 'guid', },
+ 'RealName': {'attr': 'fullName', },
+ 'RecordName': {'attr': 'shortNames', },
+ 'AutoSchedule': {'attr': 'autoSchedule', },
+ 'AutoAcceptGroup': {'attr': 'autoAcceptGroup', },
- 'Comment' : { 'extras' : True, 'attr' : 'comment', },
- 'Description' : { 'extras' : True, 'attr' : 'description', },
- 'Type' : { 'extras' : True, 'attr' : 'type', },
+ 'Comment': {'extras': True, 'attr': 'comment', },
+ 'Description': {'extras': True, 'attr': 'description', },
+ 'Type': {'extras': True, 'attr': 'type', },
# For "Locations", i.e. scheduled spaces
- 'Capacity' : { 'extras' : True, 'attr' : 'capacity', },
- 'Floor' : { 'extras' : True, 'attr' : 'floor', },
- 'AssociatedAddress' : { 'extras' : True, 'attr' : 'associatedAddress', },
+ 'Capacity': {'extras': True, 'attr': 'capacity', },
+ 'Floor': {'extras': True, 'attr': 'floor', },
+ 'AssociatedAddress': {'extras': True, 'attr': 'associatedAddress', },
# For "Addresses", i.e. nonscheduled areas containing Locations
- 'AbbreviatedName' : { 'extras' : True, 'attr' : 'abbreviatedName', },
- 'StreetAddress' : { 'extras' : True, 'attr' : 'streetAddress', },
- 'Geo' : { 'extras' : True, 'attr' : 'geo', },
+ 'AbbreviatedName': {'extras': True, 'attr': 'abbreviatedName', },
+ 'StreetAddress': {'extras': True, 'attr': 'streetAddress', },
+ 'Geo': {'extras': True, 'attr': 'geo', },
}
+ at inlineCallbacks
def main():
try:
(optargs, args) = getopt(
@@ -156,10 +154,10 @@
"search=",
"list-principal-types",
"list-principals=",
- "read-property=",
"list-read-proxies",
"list-write-proxies",
"list-proxies",
+ "list-proxy-for",
"add-read-proxy=",
"add-write-proxy=",
"remove-proxy=",
@@ -185,7 +183,7 @@
# Get configuration
#
configFileName = None
- addType = None
+ # addType = None
listPrincipalTypes = False
listPrincipals = None
searchPrincipals = None
@@ -202,11 +200,11 @@
elif opt in ("-f", "--config"):
configFileName = arg
- elif opt in ("-a", "--add"):
- addType = arg
+ # elif opt in ("-a", "--add"):
+ # addType = arg
- elif opt in ("-r", "--remove"):
- principalActions.append((action_removePrincipal,))
+ # elif opt in ("-r", "--remove"):
+ # principalActions.append((action_removePrincipal,))
elif opt in ("", "--list-principal-types"):
listPrincipalTypes = True
@@ -217,13 +215,6 @@
elif opt in ("", "--search"):
searchPrincipals = arg
- elif opt in ("", "--read-property"):
- try:
- qname = decodeXMLName(arg)
- except ValueError, e:
- abort(e)
- principalActions.append((action_readProperty, qname))
-
elif opt in ("", "--list-read-proxies"):
principalActions.append((action_listProxies, "read"))
@@ -233,6 +224,9 @@
elif opt in ("-L", "--list-proxies"):
principalActions.append((action_listProxies, "read", "write"))
+ elif opt in ("--list-proxy-for"):
+ principalActions.append((action_listProxyFor, "read", "write"))
+
elif opt in ("--add-read-proxy", "--add-write-proxy"):
if "read" in opt:
proxyType = "read"
@@ -242,7 +236,7 @@
raise AssertionError("Unknown proxy type")
try:
- principalForPrincipalID(arg, checkOnly=True)
+ yield recordForPrincipalID(arg, checkOnly=True)
except ValueError, e:
abort(e)
@@ -250,64 +244,64 @@
elif opt in ("", "--remove-proxy"):
try:
- principalForPrincipalID(arg, checkOnly=True)
+ yield recordForPrincipalID(arg, checkOnly=True)
except ValueError, e:
abort(e)
principalActions.append((action_removeProxy, arg))
- elif opt in ("", "--set-auto-schedule"):
- try:
- autoSchedule = booleanArgument(arg)
- except ValueError, e:
- abort(e)
+ # elif opt in ("", "--set-auto-schedule"):
+ # try:
+ # autoSchedule = booleanArgument(arg)
+ # except ValueError, e:
+ # abort(e)
- principalActions.append((action_setAutoSchedule, autoSchedule))
+ # principalActions.append((action_setAutoSchedule, autoSchedule))
- elif opt in ("", "--get-auto-schedule"):
- principalActions.append((action_getAutoSchedule,))
+ # elif opt in ("", "--get-auto-schedule"):
+ # principalActions.append((action_getAutoSchedule,))
- elif opt in ("", "--set-auto-schedule-mode"):
- try:
- if arg not in allowedAutoScheduleModes:
- raise ValueError("Unknown auto-schedule mode: %s" % (arg,))
- autoScheduleMode = arg
- except ValueError, e:
- abort(e)
+ # elif opt in ("", "--set-auto-schedule-mode"):
+ # try:
+ # if arg not in allowedAutoScheduleModes:
+ # raise ValueError("Unknown auto-schedule mode: %s" % (arg,))
+ # autoScheduleMode = arg
+ # except ValueError, e:
+ # abort(e)
- principalActions.append((action_setAutoScheduleMode, autoScheduleMode))
+ # principalActions.append((action_setAutoScheduleMode, autoScheduleMode))
- elif opt in ("", "--get-auto-schedule-mode"):
- principalActions.append((action_getAutoScheduleMode,))
+ # elif opt in ("", "--get-auto-schedule-mode"):
+ # principalActions.append((action_getAutoScheduleMode,))
- elif opt in ("", "--set-auto-accept-group"):
- try:
- principalForPrincipalID(arg, checkOnly=True)
- except ValueError, e:
- abort(e)
+ # elif opt in ("", "--set-auto-accept-group"):
+ # try:
+ # yield recordForPrincipalID(arg, checkOnly=True)
+ # except ValueError, e:
+ # abort(e)
- principalActions.append((action_setAutoAcceptGroup, arg))
+ # principalActions.append((action_setAutoAcceptGroup, arg))
- elif opt in ("", "--get-auto-accept-group"):
- principalActions.append((action_getAutoAcceptGroup,))
+ # elif opt in ("", "--get-auto-accept-group"):
+ # principalActions.append((action_getAutoAcceptGroup,))
- elif opt in ("", "--set-geo"):
- principalActions.append((action_setValue, "Geo", arg))
+ # elif opt in ("", "--set-geo"):
+ # principalActions.append((action_setValue, "Geo", arg))
- elif opt in ("", "--get-geo"):
- principalActions.append((action_getValue, "Geo"))
+ # elif opt in ("", "--get-geo"):
+ # principalActions.append((action_getValue, "Geo"))
- elif opt in ("", "--set-street-address"):
- principalActions.append((action_setValue, "StreetAddress", arg))
+ # elif opt in ("", "--set-street-address"):
+ # principalActions.append((action_setValue, "StreetAddress", arg))
- elif opt in ("", "--get-street-address"):
- principalActions.append((action_getValue, "StreetAddress"))
+ # elif opt in ("", "--get-street-address"):
+ # principalActions.append((action_getValue, "StreetAddress"))
- elif opt in ("", "--set-address"):
- principalActions.append((action_setValue, "AssociatedAddress", arg))
+ # elif opt in ("", "--set-address"):
+ # principalActions.append((action_setValue, "AssociatedAddress", arg))
- elif opt in ("", "--get-address"):
- principalActions.append((action_getValue, "AssociatedAddress"))
+ # elif opt in ("", "--get-address"):
+ # principalActions.append((action_getValue, "AssociatedAddress"))
else:
raise NotImplementedError(opt)
@@ -322,32 +316,34 @@
function = runListPrincipalTypes
params = ()
- elif addType:
+ # elif addType:
- try:
- addType = matchStrings(addType, ["locations", "resources", "addresses"])
- except ValueError, e:
- print(e)
- return
+ # try:
+ # addType = matchStrings(addType, ["locations", "resources", "addresses"])
+ # except ValueError, e:
+ # print(e)
+ # return
- try:
- fullName, shortName, guid = parseCreationArgs(args)
- except ValueError, e:
- print(e)
- return
+ # try:
+ # fullName, shortName, guid = parseCreationArgs(args)
+ # except ValueError, e:
+ # print(e)
+ # return
- if shortName is not None:
- shortNames = [shortName]
- else:
- shortNames = ()
+ # if shortName is not None:
+ # shortNames = [shortName]
+ # else:
+ # shortNames = ()
- function = runAddPrincipal
- params = (addType, guid, shortNames, fullName)
+ # function = runAddPrincipal
+ # params = (addType, guid, shortNames, fullName)
elif listPrincipals:
try:
- listPrincipals = matchStrings(listPrincipals, ["users", "groups",
- "locations", "resources", "addresses"])
+ listPrincipals = matchStrings(
+ listPrincipals,
+ ["users", "groups", "locations", "resources", "addresses"]
+ )
except ValueError, e:
print(e)
return
@@ -372,7 +368,7 @@
for arg in args:
try:
- principalForPrincipalID(arg, checkOnly=True)
+ yield recordForPrincipalID(arg, checkOnly=True)
except ValueError, e:
abort(e)
@@ -385,17 +381,20 @@
-def runListPrincipalTypes(service, rootResource, directory, store):
+def runListPrincipalTypes(service, store):
+ directory = store.directoryService()
for recordType in directory.recordTypes():
- print(recordType)
+ print(directory.recordTypeToOldString(recordType))
return succeed(None)
@inlineCallbacks
-def runListPrincipals(service, rootResource, directory, store, listPrincipals):
+def runListPrincipals(service, store, listPrincipals):
+ directory = store.directoryService()
+ recordType = directory.oldNameToRecordType(listPrincipals)
try:
- records = list((yield directory.listRecords(listPrincipals)))
+ records = list((yield directory.recordsWithRecordType(recordType)))
if records:
printRecordList(records)
else:
@@ -407,51 +406,48 @@
@inlineCallbacks
-def runPrincipalActions(service, rootResource, directory, store, principalIDs,
- actions):
+def runPrincipalActions(service, store, principalIDs, actions):
+ directory = store.directoryService()
for principalID in principalIDs:
- # Resolve the given principal IDs to principals
+ # Resolve the given principal IDs to records
try:
- principal = yield principalForPrincipalID(principalID, directory=directory)
+ record = yield recordForPrincipalID(
+ principalID, directory=directory
+ )
except ValueError:
- principal = None
+ record = None
- if principal is None:
+ if record is None:
sys.stderr.write("Invalid principal ID: %s\n" % (principalID,))
continue
# Performs requested actions
for action in actions:
- (yield action[0](rootResource, directory, store, principal,
- *action[1:]))
+ (yield action[0](store, record, *action[1:]))
print("")
@inlineCallbacks
-def runSearch(service, rootResource, directory, store, searchTerm):
-
+def runSearch(service, store, searchTerm):
+ directory = store.directoryService()
fields = []
- for fieldName in ("fullName", "firstName", "lastName", "emailAddresses"):
+ for fieldName in ("fullNames", "emailAddresses"):
fields.append((fieldName, searchTerm, True, "contains"))
records = list((yield directory.recordsMatchingTokens(searchTerm.strip().split())))
if records:
- records.sort(key=operator.attrgetter('fullName'))
+ records.sort(key=operator.attrgetter('fullNames'))
print("%d matches found:" % (len(records),))
for record in records:
- print("\n%s (%s)" % (record.fullName,
- {"users" : "User",
- "groups" : "Group",
- "locations" : "Place",
- "resources" : "Resource",
- "addresses" : "Address",
- }.get(record.recordType),
- ))
- print(" GUID: %s" % (record.guid,))
+ print(
+ "\n{d} {rt}".format(
+ d=record.displayName,
+ rt=record.recordType.name
+ )
+ )
+ print(" UID: %s" % (record.uid,))
print(" Record name(s): %s" % (", ".join(record.shortNames),))
- if record.authIDs:
- print(" Auth ID(s): %s" % (", ".join(record.authIDs),))
if record.emailAddresses:
print(" Email(s): %s" % (", ".join(record.emailAddresses),))
else:
@@ -461,292 +457,318 @@
- at inlineCallbacks
-def runAddPrincipal(service, rootResource, directory, store, addType, guid,
- shortNames, fullName):
- try:
- yield updateRecord(True, directory, addType, guid=guid,
- shortNames=shortNames, fullName=fullName)
- print("Added '%s'" % (fullName,))
- except DirectoryError, e:
- print(e)
+# @inlineCallbacks
+# def runAddPrincipal(service, store, addType, guid, shortNames, fullName):
+# directory = store.directoryService()
+# try:
+# # FIXME STOP USING GUID
+# yield updateRecord(
+# True, directory, addType, guid=guid,
+# shortNames=shortNames, fullName=fullName
+# )
+# print("Added '%s'" % (fullName,))
+# except DirectoryError, e:
+# print(e)
-def action_removePrincipal(rootResource, directory, store, principal):
- record = principal.record
- fullName = record.fullName
- shortName = record.shortNames[0]
- guid = record.guid
+# def action_removePrincipal(store, record):
+# directory = store.directoryService()
+# fullName = record.displayName
+# shortName = record.shortNames[0]
- directory.destroyRecord(record.recordType, guid=guid)
- print("Removed '%s' %s %s" % (fullName, shortName, guid))
+# yield directory.destroyRecord(record.recordType, uid=record.uid)
+# print("Removed '%s' %s %s" % (fullName, shortName, record.uid))
+
@inlineCallbacks
-def action_readProperty(rootResource, directory, store, resource, qname):
- property = (yield resource.readProperty(qname, None))
- print("%r on %s:" % (encodeXMLName(*qname), resource))
- print("")
- print(property.toxml())
+def action_listProxies(store, record, *proxyTypes):
+ directory = store.directoryService()
+ for proxyType in proxyTypes:
+ groupRecordType = {
+ "read": directory.recordType.readDelegateGroup,
+ "write": directory.recordType.writeDelegateGroup,
+ }.get(proxyType)
+ pseudoGroup = yield directory.recordWithShortName(
+ groupRecordType,
+ record.uid
+ )
+ proxies = yield pseudoGroup.members()
+ if proxies:
+ print("%s proxies for %s:" % (
+ {"read": "Read-only", "write": "Read/write"}[proxyType],
+ prettyRecord(record)
+ ))
+ printRecordList(proxies)
+ print("")
+ else:
+ print("No %s proxies for %s" % (proxyType, prettyRecord(record)))
+
@inlineCallbacks
-def action_listProxies(rootResource, directory, store, principal, *proxyTypes):
+def action_listProxyFor(store, record, *proxyTypes):
+ directory = store.directoryService()
for proxyType in proxyTypes:
- subPrincipal = proxySubprincipal(principal, proxyType)
- if subPrincipal is None:
- print("No %s proxies for %s" % (proxyType,
- prettyPrincipal(principal)))
- continue
- membersProperty = (yield subPrincipal.readProperty(davxml.GroupMemberSet, None))
+ groupRecordType = {
+ "read": directory.recordType.readDelegatorGroup,
+ "write": directory.recordType.writeDelegatorGroup,
+ }.get(proxyType)
- if membersProperty.children:
- print("%s proxies for %s:" % (
- {"read": "Read-only", "write": "Read/write"}[proxyType],
- prettyPrincipal(principal)
+ pseudoGroup = yield directory.recordWithShortName(
+ groupRecordType,
+ record.uid
+ )
+ proxies = yield pseudoGroup.members()
+ if proxies:
+ print("%s is a %s proxy for:" % (
+ prettyRecord(record),
+ {"read": "Read-only", "write": "Read/write"}[proxyType]
))
- records = []
- for member in membersProperty.children:
- proxyPrincipal = principalForPrincipalID(str(member),
- directory=directory)
- records.append(proxyPrincipal.record)
-
- printRecordList(records)
- print
+ printRecordList(proxies)
+ print("")
else:
- print("No %s proxies for %s" % (proxyType,
- prettyPrincipal(principal)))
+ print(
+ "{r} is not a {t} proxy for anyone".format(
+ r=prettyRecord(record),
+ t={"read": "Read-only", "write": "Read/write"}[proxyType]
+ )
+ )
-
@inlineCallbacks
-def action_addProxy(rootResource, directory, store, principal, proxyType, *proxyIDs):
+def _addRemoveProxy(fn, store, record, proxyType, *proxyIDs):
+ directory = store.directoryService()
+ readWrite = (proxyType == "write")
for proxyID in proxyIDs:
- proxyPrincipal = yield principalForPrincipalID(proxyID, directory=directory)
- if proxyPrincipal is None:
+ proxyRecord = yield recordForPrincipalID(proxyID, directory=directory)
+ if proxyRecord is None:
print("Invalid principal ID: %s" % (proxyID,))
else:
- (yield action_addProxyPrincipal(rootResource, directory, store,
- principal, proxyType, proxyPrincipal))
+ txn = store.newTransaction()
+ yield fn(txn, record, proxyRecord, readWrite)
+ yield txn.commit()
+def action_addProxy(store, record, proxyType, *proxyIDs):
+ return _addRemoveProxy(addDelegate, store, record, proxyType, *proxyIDs)
+
@inlineCallbacks
-def setProxies(store, principal, readProxyPrincipals, writeProxyPrincipals, directory=None):
- """
- Set read/write proxies en masse for a principal
- @param principal: DirectoryPrincipalResource
- @param readProxyPrincipals: a list of principal IDs (see principalForPrincipalID)
- @param writeProxyPrincipals: a list of principal IDs (see principalForPrincipalID)
- """
+def action_removeProxy(store, record, *proxyIDs):
+ # Write
+ yield _addRemoveProxy(removeDelegate, store, record, "write", *proxyIDs)
+ # Read
+ yield _addRemoveProxy(removeDelegate, store, record, "read", *proxyIDs)
- proxyTypes = [
- ("read", readProxyPrincipals),
- ("write", writeProxyPrincipals),
- ]
- for proxyType, proxyIDs in proxyTypes:
- if proxyIDs is None:
- continue
- subPrincipal = proxySubprincipal(principal, proxyType)
- if subPrincipal is None:
- raise ProxyError("Unable to edit %s proxies for %s\n" % (proxyType,
- prettyPrincipal(principal)))
- memberURLs = []
- for proxyID in proxyIDs:
- proxyPrincipal = yield principalForPrincipalID(proxyID, directory=directory)
- proxyURL = proxyPrincipal.url()
- memberURLs.append(davxml.HRef(proxyURL))
- membersProperty = davxml.GroupMemberSet(*memberURLs)
- yield subPrincipal.writeProperty(membersProperty, None)
- if store is not None:
- # Schedule work the PeerConnectionPool will pick up as overdue
- yield schedulePolledGroupCachingUpdate(store)
+# @inlineCallbacks
+# def setProxies(store, principal, readProxyPrincipals, writeProxyPrincipals, directory=None):
+# """
+# Set read/write proxies en masse for a principal
+# @param principal: DirectoryPrincipalResource
+# @param readProxyPrincipals: a list of principal IDs (see principalForPrincipalID)
+# @param writeProxyPrincipals: a list of principal IDs (see principalForPrincipalID)
+# """
- at inlineCallbacks
-def getProxies(principal, directory=None):
- """
- Returns a tuple containing the GUIDs for read proxies and write proxies
- of the given principal
- """
+# proxyTypes = [
+# ("read", readProxyPrincipals),
+# ("write", writeProxyPrincipals),
+# ]
+# for proxyType, proxyIDs in proxyTypes:
+# if proxyIDs is None:
+# continue
+# subPrincipal = proxySubprincipal(principal, proxyType)
+# if subPrincipal is None:
+# raise ProxyError("Unable to edit %s proxies for %s\n" % (proxyType,
+# prettyPrincipal(principal)))
+# memberURLs = []
+# for proxyID in proxyIDs:
+# proxyPrincipal = yield principalForPrincipalID(proxyID, directory=directory)
+# proxyURL = proxyPrincipal.url()
+# memberURLs.append(davxml.HRef(proxyURL))
+# membersProperty = davxml.GroupMemberSet(*memberURLs)
+# yield subPrincipal.writeProperty(membersProperty, None)
+# if store is not None:
+# # Schedule work the PeerConnectionPool will pick up as overdue
+# yield schedulePolledGroupCachingUpdate(store)
- proxies = {
- "read" : [],
- "write" : [],
- }
- for proxyType in proxies.iterkeys():
- subPrincipal = proxySubprincipal(principal, proxyType)
- if subPrincipal is not None:
- membersProperty = (yield subPrincipal.readProperty(davxml.GroupMemberSet, None))
- if membersProperty.children:
- for member in membersProperty.children:
- proxyPrincipal = yield principalForPrincipalID(str(member), directory=directory)
- proxies[proxyType].append(proxyPrincipal.record.guid)
- returnValue((proxies['read'], proxies['write']))
+# @inlineCallbacks
+# def getProxies(principal, directory=None):
+# """
+# Returns a tuple containing the GUIDs for read proxies and write proxies
+# of the given principal
+# """
+# proxies = {
+# "read": [],
+# "write": [],
+# }
+# for proxyType in proxies.iterkeys():
+# subPrincipal = proxySubprincipal(principal, proxyType)
+# if subPrincipal is not None:
+# membersProperty = (yield subPrincipal.readProperty(davxml.GroupMemberSet, None))
+# if membersProperty.children:
+# for member in membersProperty.children:
+# proxyPrincipal = yield principalForPrincipalID(str(member), directory=directory)
+# proxies[proxyType].append(proxyPrincipal.record.guid)
- at inlineCallbacks
-def action_removeProxy(rootResource, directory, store, principal, *proxyIDs, **kwargs):
- for proxyID in proxyIDs:
- proxyPrincipal = yield principalForPrincipalID(proxyID, directory=directory)
- if proxyPrincipal is None:
- print("Invalid principal ID: %s" % (proxyID,))
- else:
- (yield action_removeProxyPrincipal(rootResource, directory, store,
- principal, proxyPrincipal, **kwargs))
+# returnValue((proxies['read'], proxies['write']))
- at inlineCallbacks
-def action_setAutoSchedule(rootResource, directory, store, principal, autoSchedule):
- if principal.record.recordType == "groups":
- print("Enabling auto-schedule for %s is not allowed." % (principal,))
- elif principal.record.recordType == "users" and not config.Scheduling.Options.AutoSchedule.AllowUsers:
- print("Enabling auto-schedule for %s is not allowed." % (principal,))
- else:
- print("Setting auto-schedule to %s for %s" % (
- {True: "true", False: "false"}[autoSchedule],
- prettyPrincipal(principal),
- ))
+# @inlineCallbacks
+# def action_setAutoSchedule(rootResource, directory, store, principal, autoSchedule):
+# if principal.record.recordType == "groups":
+# print("Enabling auto-schedule for %s is not allowed." % (principal,))
- (yield updateRecord(False, directory,
- principal.record.recordType,
- guid=principal.record.guid,
- shortNames=principal.record.shortNames,
- fullName=principal.record.fullName,
- autoSchedule=autoSchedule,
- **principal.record.extras
- ))
+# elif principal.record.recordType == "users" and not config.Scheduling.Options.AutoSchedule.AllowUsers:
+# print("Enabling auto-schedule for %s is not allowed." % (principal,))
+# else:
+# print("Setting auto-schedule to %s for %s" % (
+# {True: "true", False: "false"}[autoSchedule],
+# prettyPrincipal(principal),
+# ))
+# (yield updateRecord(False, directory,
+# principal.record.recordType,
+# guid=principal.record.guid,
+# shortNames=principal.record.shortNames,
+# fullName=principal.record.fullName,
+# autoSchedule=autoSchedule,
+# **principal.record.extras
+# ))
-def action_getAutoSchedule(rootResource, directory, store, principal):
- autoSchedule = principal.getAutoSchedule()
- print("Auto-schedule for %s is %s" % (
- prettyPrincipal(principal),
- {True: "true", False: "false"}[autoSchedule],
- ))
+# def action_getAutoSchedule(rootResource, directory, store, principal):
+# autoSchedule = principal.getAutoSchedule()
+# print("Auto-schedule for %s is %s" % (
+# prettyPrincipal(principal),
+# {True: "true", False: "false"}[autoSchedule],
+# ))
- at inlineCallbacks
-def action_setAutoScheduleMode(rootResource, directory, store, principal, autoScheduleMode):
- if principal.record.recordType == "groups":
- print("Setting auto-schedule mode for %s is not allowed." % (principal,))
- elif principal.record.recordType == "users" and not config.Scheduling.Options.AutoSchedule.AllowUsers:
- print("Setting auto-schedule mode for %s is not allowed." % (principal,))
- else:
- print("Setting auto-schedule mode to %s for %s" % (
- autoScheduleMode,
- prettyPrincipal(principal),
- ))
+# @inlineCallbacks
+# def action_setAutoScheduleMode(rootResource, directory, store, principal, autoScheduleMode):
+# if principal.record.recordType == "groups":
+# print("Setting auto-schedule mode for %s is not allowed." % (principal,))
- (yield updateRecord(False, directory,
- principal.record.recordType,
- guid=principal.record.guid,
- shortNames=principal.record.shortNames,
- fullName=principal.record.fullName,
- autoScheduleMode=autoScheduleMode,
- **principal.record.extras
- ))
+# elif principal.record.recordType == "users" and not config.Scheduling.Options.AutoSchedule.AllowUsers:
+# print("Setting auto-schedule mode for %s is not allowed." % (principal,))
+# else:
+# print("Setting auto-schedule mode to %s for %s" % (
+# autoScheduleMode,
+# prettyPrincipal(principal),
+# ))
+# (yield updateRecord(False, directory,
+# principal.record.recordType,
+# guid=principal.record.guid,
+# shortNames=principal.record.shortNames,
+# fullName=principal.record.fullName,
+# autoScheduleMode=autoScheduleMode,
+# **principal.record.extras
+# ))
-def action_getAutoScheduleMode(rootResource, directory, store, principal):
- autoScheduleMode = principal.getAutoScheduleMode()
- if not autoScheduleMode:
- autoScheduleMode = "automatic"
- print("Auto-schedule mode for %s is %s" % (
- prettyPrincipal(principal),
- autoScheduleMode,
- ))
+# def action_getAutoScheduleMode(rootResource, directory, store, principal):
+# autoScheduleMode = principal.getAutoScheduleMode()
+# if not autoScheduleMode:
+# autoScheduleMode = "automatic"
+# print("Auto-schedule mode for %s is %s" % (
+# prettyPrincipal(principal),
+# autoScheduleMode,
+# ))
- at inlineCallbacks
-def action_setAutoAcceptGroup(rootResource, directory, store, principal, autoAcceptGroup):
- if principal.record.recordType == "groups":
- print("Setting auto-accept-group for %s is not allowed." % (principal,))
- elif principal.record.recordType == "users" and not config.Scheduling.Options.AutoSchedule.AllowUsers:
- print("Setting auto-accept-group for %s is not allowed." % (principal,))
- else:
- groupPrincipal = yield principalForPrincipalID(autoAcceptGroup, directory=directory)
- if groupPrincipal is None or groupPrincipal.record.recordType != "groups":
- print("Invalid principal ID: %s" % (autoAcceptGroup,))
- else:
- print("Setting auto-accept-group to %s for %s" % (
- prettyPrincipal(groupPrincipal),
- prettyPrincipal(principal),
- ))
+# @inlineCallbacks
+# def action_setAutoAcceptGroup(rootResource, directory, store, principal, autoAcceptGroup):
+# if principal.record.recordType == "groups":
+# print("Setting auto-accept-group for %s is not allowed." % (principal,))
- (yield updateRecord(False, directory,
- principal.record.recordType,
- guid=principal.record.guid,
- shortNames=principal.record.shortNames,
- fullName=principal.record.fullName,
- autoAcceptGroup=groupPrincipal.record.guid,
- **principal.record.extras
- ))
+# elif principal.record.recordType == "users" and not config.Scheduling.Options.AutoSchedule.AllowUsers:
+# print("Setting auto-accept-group for %s is not allowed." % (principal,))
+# else:
+# groupPrincipal = yield principalForPrincipalID(autoAcceptGroup, directory=directory)
+# if groupPrincipal is None or groupPrincipal.record.recordType != "groups":
+# print("Invalid principal ID: %s" % (autoAcceptGroup,))
+# else:
+# print("Setting auto-accept-group to %s for %s" % (
+# prettyPrincipal(groupPrincipal),
+# prettyPrincipal(principal),
+# ))
+# (yield updateRecord(False, directory,
+# principal.record.recordType,
+# guid=principal.record.guid,
+# shortNames=principal.record.shortNames,
+# fullName=principal.record.fullName,
+# autoAcceptGroup=groupPrincipal.record.guid,
+# **principal.record.extras
+# ))
-def action_getAutoAcceptGroup(rootResource, directory, store, principal):
- autoAcceptGroup = principal.getAutoAcceptGroup()
- if autoAcceptGroup:
- record = yield directory.recordWithGUID(autoAcceptGroup)
- if record is not None:
- groupPrincipal = yield directory.principalCollection.principalForUID(record.uid)
- if groupPrincipal is not None:
- print("Auto-accept-group for %s is %s" % (
- prettyPrincipal(principal),
- prettyPrincipal(groupPrincipal),
- ))
- return
- print("Invalid auto-accept-group assigned: %s" % (autoAcceptGroup,))
- else:
- print("No auto-accept-group assigned to %s" % (prettyPrincipal(principal),))
+# def action_getAutoAcceptGroup(rootResource, directory, store, principal):
+# autoAcceptGroup = principal.getAutoAcceptGroup()
+# if autoAcceptGroup:
+# record = yield directory.recordWithGUID(autoAcceptGroup)
+# if record is not None:
+# groupPrincipal = yield directory.principalCollection.principalForUID(record.uid)
+# if groupPrincipal is not None:
+# print("Auto-accept-group for %s is %s" % (
+# prettyPrincipal(principal),
+# prettyPrincipal(groupPrincipal),
+# ))
+# return
+# print("Invalid auto-accept-group assigned: %s" % (autoAcceptGroup,))
+# else:
+# print("No auto-accept-group assigned to %s" % (prettyPrincipal(principal),))
- at inlineCallbacks
-def action_setValue(rootResource, directory, store, principal, name, value):
- print("Setting %s to %s for %s" % (
- name, value, prettyPrincipal(principal),
- ))
- principal.record.extras[attrMap[name]["attr"]] = value
- (yield updateRecord(False, directory,
- principal.record.recordType,
- guid=principal.record.guid,
- shortNames=principal.record.shortNames,
- fullName=principal.record.fullName,
- **principal.record.extras
- ))
+# @inlineCallbacks
+# def action_setValue(rootResource, directory, store, principal, name, value):
+# print("Setting %s to %s for %s" % (
+# name, value, prettyPrincipal(principal),
+# ))
+# principal.record.extras[attrMap[name]["attr"]] = value
+# (yield updateRecord(False, directory,
+# principal.record.recordType,
+# guid=principal.record.guid,
+# shortNames=principal.record.shortNames,
+# fullName=principal.record.fullName,
+# **principal.record.extras
+# ))
-def action_getValue(rootResource, directory, store, principal, name):
- print("%s for %s is %s" % (
- name,
- prettyPrincipal(principal),
- principal.record.extras[attrMap[name]["attr"]]
- ))
+# def action_getValue(rootResource, directory, store, principal, name):
+# print("%s for %s is %s" % (
+# name,
+# prettyPrincipal(principal),
+# principal.record.extras[attrMap[name]["attr"]]
+# ))
+
+
def abort(msg, status=1):
sys.stdout.write("%s\n" % (msg,))
try:
@@ -804,14 +826,16 @@
def printRecordList(records):
- results = [(record.fullName, record.shortNames[0], record.guid)
- for record in records]
+ results = [
+ (record.displayName, record.recordType.name, record.uid, record.shortNames)
+ for record in records
+ ]
results.sort()
- format = "%-22s %-17s %s"
- print(format % ("Full name", "Record name", "UUID"))
- print(format % ("---------", "-----------", "----"))
- for fullName, shortName, guid in results:
- print(format % (fullName, shortName, guid))
+ format = "%-22s %-10s %-20s %s"
+ print(format % ("Full name", "Type", "UID", "Short names"))
+ print(format % ("---------", "----", "---", "-----------"))
+ for fullName, recordType, uid, shortNames in results:
+ print(format % (fullName, recordType, uid, u", ".join(shortNames)))
Modified: CalendarServer/branches/users/sagen/move2who/calendarserver/tools/util.py
===================================================================
--- CalendarServer/branches/users/sagen/move2who/calendarserver/tools/util.py 2014-03-06 23:06:36 UTC (rev 12834)
+++ CalendarServer/branches/users/sagen/move2who/calendarserver/tools/util.py 2014-03-07 01:22:21 UTC (rev 12835)
@@ -334,6 +334,7 @@
+ at inlineCallbacks
def principalForPrincipalID(principalID, checkOnly=False, directory=None):
# Allow a directory parameter to be passed in, but default to config.directory
@@ -351,16 +352,16 @@
raise ValueError("Can't resolve all paths yet")
if checkOnly:
- return None
+ returnValue(None)
- return directory.principalCollection.principalForUID(uid)
+ returnValue((yield directory.principalCollection.principalForUID(uid)))
if principalID.startswith("("):
try:
i = principalID.index(")")
if checkOnly:
- return None
+ returnValue(None)
recordType = principalID[1:i]
shortName = principalID[i + 1:]
@@ -368,34 +369,93 @@
if not recordType or not shortName or "(" in recordType:
raise ValueError()
- return directory.principalCollection.principalForShortName(recordType, shortName)
+ returnValue((yield directory.principalCollection.principalForShortName(recordType, shortName)))
except ValueError:
pass
if ":" in principalID:
if checkOnly:
- return None
+ returnValue(None)
recordType, shortName = principalID.split(":", 1)
- return directory.principalCollection.principalForShortName(recordType, shortName)
+ returnValue((yield directory.principalCollection.principalForShortName(recordType, shortName)))
try:
UUID(principalID)
if checkOnly:
- return None
+ returnValue(None)
- x = directory.principalCollection.principalForUID(principalID)
- return x
+ returnValue((yield directory.principalCollection.principalForUID(principalID)))
except ValueError:
pass
raise ValueError("Invalid principal identifier: %s" % (principalID,))
+ at inlineCallbacks
+def recordForPrincipalID(principalID, checkOnly=False, directory=None):
+ # Allow a directory parameter to be passed in, but default to config.directory
+ # But config.directory isn't set right away, so only use it when we're doing more
+ # than checking.
+ if not checkOnly and not directory:
+ directory = config.directory
+
+ if principalID.startswith("/"):
+ segments = principalID.strip("/").split("/")
+ if (len(segments) == 3 and
+ segments[0] == "principals" and segments[1] == "__uids__"):
+ uid = segments[2]
+ else:
+ raise ValueError("Can't resolve all paths yet")
+
+ if checkOnly:
+ returnValue(None)
+
+ returnValue((yield directory.recordWithUID(uid)))
+
+ if principalID.startswith("("):
+ try:
+ i = principalID.index(")")
+
+ if checkOnly:
+ returnValue(None)
+
+ recordType = directory.oldNameToRecordType(principalID[1:i])
+ shortName = principalID[i + 1:]
+
+ if not recordType or not shortName or "(" in recordType:
+ raise ValueError()
+
+ returnValue((yield directory.recordWithShortName(recordType, shortName)))
+
+ except ValueError:
+ pass
+
+ if ":" in principalID:
+ if checkOnly:
+ returnValue(None)
+
+ recordType, shortName = principalID.split(":", 1)
+ recordType = directory.oldNameToRecordType(recordType)
+
+ returnValue((yield directory.recordWithShortName(recordType, shortName)))
+
+ try:
+ if checkOnly:
+ returnValue(None)
+
+ returnValue((yield directory.recordWithUID(principalID)))
+ except ValueError:
+ pass
+
+ raise ValueError("Invalid principal identifier: %s" % (principalID,))
+
+
+
def proxySubprincipal(principal, proxyType):
return principal.getChild("calendar-proxy-" + proxyType)
@@ -501,12 +561,19 @@
def prettyPrincipal(principal):
- record = principal.record
- return "\"%s\" (%s:%s)" % (record.fullName, record.recordType,
- record.shortNames[0])
+ prettyRecord(principal.record)
+def prettyRecord(record):
+ return "\"{d}\" {uid} ({rt}) {sn}".format(
+ d=record.displayName,
+ rt=record.recordType.name,
+ uid=record.uid,
+ sn=(", ".join(record.shortNames))
+ )
+
+
class ProxyError(Exception):
"""
Raised when proxy assignments cannot be performed
Modified: CalendarServer/branches/users/sagen/move2who/txdav/dps/client.py
===================================================================
--- CalendarServer/branches/users/sagen/move2who/txdav/dps/client.py 2014-03-06 23:06:36 UTC (rev 12834)
+++ CalendarServer/branches/users/sagen/move2who/txdav/dps/client.py 2014-03-07 01:22:21 UTC (rev 12835)
@@ -29,12 +29,12 @@
from twisted.internet.protocol import ClientCreator
from twisted.protocols import amp
from twisted.python.constants import Names, NamedConstant
-from txdav.caldav.datastore.scheduling.cuaddress import normalizeCUAddr
from txdav.caldav.icalendardirectoryservice import ICalendarStoreDirectoryRecord
from txdav.common.idirectoryservice import IStoreDirectoryService
from txdav.dps.commands import (
RecordWithShortNameCommand, RecordWithUIDCommand, RecordWithGUIDCommand,
RecordsWithRecordTypeCommand, RecordsWithEmailAddressCommand,
+ RecordsMatchingTokensCommand,
MembersCommand, GroupsCommand, SetMembersCommand,
VerifyPlaintextPasswordCommand, VerifyHTTPDigestCommand
)
@@ -239,13 +239,15 @@
)
- def listRecords(self, recordType):
- # MOVE2WHO
- return []
+ # def listRecords(self, recordType):
+ # # MOVE2WHO
+ # return []
@inlineCallbacks
def recordWithCalendarUserAddress(self, address):
+ # FIXME: Circular
+ from txdav.caldav.datastore.scheduling.cuaddress import normalizeCUAddr
address = normalizeCUAddr(address)
record = None
if address.startswith("urn:uuid:"):
@@ -270,18 +272,27 @@
returnValue(record if record and record.hasCalendars else None)
- @inlineCallbacks
def recordsMatchingTokens(self, tokens, context=None, limitResults=50,
timeoutSeconds=10):
- rec = yield self.recordWithShortName(
- twext.who.idirectory.RecordType.user,
- u"wsanchez"
+ return self._call(
+ RecordsMatchingTokensCommand,
+ self._processMultipleRecords,
+ tokens=[t.encode("utf-8") for t in tokens],
+ context=context
)
- returnValue([rec])
+ # FIXME: Existing code assumes record type names are plural. Is there any
+ # reason to maintain backwards compatibility? I suppose there could be
+ # scripts referring to record type of "users", "locations"
+ def recordTypeToOldName(self, recordType):
+ return recordType.name + u"s"
+ def oldNameToRecordType(self, oldName):
+ return self.recordType.lookupByName(oldName[:-1])
+
+
@implementer(ICalendarStoreDirectoryRecord)
class DirectoryRecord(BaseDirectoryRecord):
@@ -290,7 +301,7 @@
def verifyCredentials(self, credentials):
# XYZZY REMOVE THIS, it bypasses all authentication!:
- # returnValue(True)
+ returnValue(True)
if isinstance(credentials, UsernamePassword):
log.debug("UsernamePassword")
Modified: CalendarServer/branches/users/sagen/move2who/txdav/dps/commands.py
===================================================================
--- CalendarServer/branches/users/sagen/move2who/txdav/dps/commands.py 2014-03-06 23:06:36 UTC (rev 12834)
+++ CalendarServer/branches/users/sagen/move2who/txdav/dps/commands.py 2014-03-07 01:22:21 UTC (rev 12835)
@@ -67,7 +67,16 @@
]
+class RecordsMatchingTokensCommand(amp.Command):
+ arguments = [
+ ('tokens', amp.ListOf(amp.String())),
+ ('context', amp.String(optional=True)),
+ ]
+ response = [
+ ('fieldsList', amp.String()),
+ ]
+
class UpdateRecordsCommand(amp.Command):
arguments = [
('fieldsList', amp.String()),
Modified: CalendarServer/branches/users/sagen/move2who/txdav/dps/server.py
===================================================================
--- CalendarServer/branches/users/sagen/move2who/txdav/dps/server.py 2014-03-06 23:06:36 UTC (rev 12834)
+++ CalendarServer/branches/users/sagen/move2who/txdav/dps/server.py 2014-03-07 01:22:21 UTC (rev 12835)
@@ -21,6 +21,9 @@
from calendarserver.tap.util import getDBPool, storeFromConfig
from twext.python.log import Logger
from twext.who.aggregate import DirectoryService as AggregateDirectoryService
+from twext.who.expression import (
+ MatchType, Operand, MatchExpression, CompoundExpression, MatchFlags
+)
from twext.who.idirectory import RecordType
from twext.who.ldap import DirectoryService as LDAPDirectoryService
from twisted.application import service
@@ -39,6 +42,7 @@
from txdav.dps.commands import (
RecordWithShortNameCommand, RecordWithUIDCommand, RecordWithGUIDCommand,
RecordsWithRecordTypeCommand, RecordsWithEmailAddressCommand,
+ RecordsMatchingTokensCommand,
MembersCommand, GroupsCommand, SetMembersCommand,
VerifyPlaintextPasswordCommand, VerifyHTTPDigestCommand,
# UpdateRecordsCommand, RemoveRecordsCommand
@@ -171,7 +175,48 @@
returnValue(response)
+ @RecordsMatchingTokensCommand.responder
+ @inlineCallbacks
+ def recordsMatchingTokens(self, tokens, context=None):
+ tokens = [t.decode("utf-8") for t in tokens]
+ log.debug("RecordsMatchingTokens: {t}", t=(", ".join(tokens)))
+
+ fields = [
+ ("fullNames", MatchType.contains),
+ ("emailAddresses", MatchType.startsWith),
+ ]
+ outer = []
+ for token in tokens:
+ inner = []
+ for name, matchType in fields:
+ inner.append(
+ MatchExpression(
+ self._directory.fieldName.lookupByName(name),
+ token,
+ matchType,
+ MatchFlags.caseInsensitive
+ )
+ )
+ outer.append(
+ CompoundExpression(
+ inner,
+ Operand.OR
+ )
+ )
+ expression = CompoundExpression(outer, Operand.AND)
+ records = yield self._directory.recordsFromExpression(expression)
+
+ fieldsList = []
+ for record in records:
+ fieldsList.append(self.recordToDict(record))
+ response = {
+ "fieldsList": pickle.dumps(fieldsList),
+ }
+ log.debug("Responding with: {response}", response=response)
+ returnValue(response)
+
+
@MembersCommand.responder
@inlineCallbacks
def members(self, uid):
Modified: CalendarServer/branches/users/sagen/move2who/txdav/dps/test/test_client.py
===================================================================
--- CalendarServer/branches/users/sagen/move2who/txdav/dps/test/test_client.py 2014-03-06 23:06:36 UTC (rev 12834)
+++ CalendarServer/branches/users/sagen/move2who/txdav/dps/test/test_client.py 2014-03-07 01:22:21 UTC (rev 12835)
@@ -112,6 +112,18 @@
@inlineCallbacks
+ def test_recordsMatchingTokens(self):
+ records = (yield self.directory.recordsMatchingTokens(
+ [u"anche"]
+ ))
+ self.assertEquals(len(records), 2)
+ self.assertEquals(
+ set([u"__dre__", u"__wsanchez__"]),
+ set([r.uid for r in records])
+ )
+
+
+ @inlineCallbacks
def test_verifyPlaintextPassword(self):
if testMode == "xml":
expectations = (
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20140306/a7261203/attachment-0001.html>
More information about the calendarserver-changes
mailing list