<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[11902] CalendarServer/branches/users/sagen/groupcacher</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.calendarserver.org//changeset/11902">11902</a></dd>
<dt>Author</dt> <dd>sagen@apple.com</dd>
<dt>Date</dt> <dd>2013-11-07 10:24:00 -0800 (Thu, 07 Nov 2013)</dd>
</dl>
<h3>Log Message</h3>
<pre>Clean up the API a bit, and more testing</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesuserssagengroupcachertwextwhodelegatespy">CalendarServer/branches/users/sagen/groupcacher/twext/who/delegates.py</a></li>
<li><a href="#CalendarServerbranchesuserssagengroupcachertwextwhogroupspy">CalendarServer/branches/users/sagen/groupcacher/twext/who/groups.py</a></li>
<li><a href="#CalendarServerbranchesuserssagengroupcachertwextwhotesttest_delegatespy">CalendarServer/branches/users/sagen/groupcacher/twext/who/test/test_delegates.py</a></li>
<li><a href="#CalendarServerbranchesuserssagengroupcachertwextwhotesttest_groupspy">CalendarServer/branches/users/sagen/groupcacher/twext/who/test/test_groups.py</a></li>
<li><a href="#CalendarServerbranchesuserssagengroupcachertxdavcommondatastoresqlpy">CalendarServer/branches/users/sagen/groupcacher/txdav/common/datastore/sql.py</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServerbranchesuserssagengroupcachertwextwhodelegatespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/groupcacher/twext/who/delegates.py (11901 => 11902)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/groupcacher/twext/who/delegates.py        2013-11-07 17:02:26 UTC (rev 11901)
+++ CalendarServer/branches/users/sagen/groupcacher/twext/who/delegates.py        2013-11-07 18:24:00 UTC (rev 11902)
</span><span class="lines">@@ -34,19 +34,23 @@
</span><span class="cx"> if delegate.recordType == RecordType.group:
</span><span class="cx"> # find the groupID
</span><span class="cx"> groupID, name, membershipHash = (yield txn.groupByGUID(delegate.guid))
</span><del>- yield txn.addDelegate(delegator.guid, groupID,
- 1 if readWrite else 0, True)
</del><ins>+ yield txn.addDelegateGroup(delegator.guid, groupID, readWrite)
</ins><span class="cx"> else:
</span><del>- yield txn.addDelegate(delegator.guid, delegate.guid,
- 1 if readWrite else 0, False)
</del><ins>+ yield txn.addDelegate(delegator.guid, delegate.guid, readWrite)
</ins><span class="cx">
</span><span class="cx">
</span><ins>+@inlineCallbacks
</ins><span class="cx"> def removeDelegate(txn, delegator, delegate, readWrite):
</span><span class="cx"> """
</span><span class="cx"> Args are records
</span><span class="cx"> """
</span><del>- return txn.removeDelegate(delegator.guid, delegate.guid,
- 1 if readWrite else 0, delegate.recordType==RecordType.group)
</del><ins>+ if delegate.recordType == RecordType.group:
+ # find the groupID
+ groupID, name, membershipHash = (yield txn.groupByGUID(delegate.guid))
+ yield txn.removeDelegateGroup(delegator.guid, groupID, readWrite)
+ else:
+ yield txn.removeDelegate(delegator.guid, delegate.guid,
+ readWrite)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -56,32 +60,30 @@
</span><span class="cx"> """
</span><span class="cx"> records = []
</span><span class="cx"> directory = delegator.service
</span><del>- results = (yield txn.delegates(delegator.guid, 1 if readWrite else 0))
- for row in results:
- if row[0] != delegator.guid:
- record = (yield directory.recordWithGUID(row[0]))
</del><ins>+ delegateGUIDs = (yield txn.delegates(delegator.guid, readWrite))
+ for guid in delegateGUIDs:
+ if guid != delegator.guid:
+ record = (yield directory.recordWithGUID(guid))
</ins><span class="cx"> if record is not None:
</span><span class="cx"> records.append(record)
</span><span class="cx"> returnValue(records)
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>-def delegateFor(txn, delegate, readWrite):
</del><ins>+def delegatedTo(txn, delegate, readWrite):
</ins><span class="cx"> """
</span><span class="cx"> Args are records
</span><span class="cx"> """
</span><span class="cx"> records = []
</span><span class="cx"> directory = delegate.service
</span><del>- results = (yield txn.delegators(delegate.guid, 1 if readWrite else 0))
- for row in results:
- if row[0] != delegate.guid:
- record = (yield directory.recordWithGUID(row[0]))
</del><ins>+ delegatorGUIDs = (yield txn.delegators(delegate.guid, readWrite))
+ for guid in delegatorGUIDs:
+ if guid != delegate.guid:
+ record = (yield directory.recordWithGUID(guid))
</ins><span class="cx"> if record is not None:
</span><span class="cx"> records.append(record)
</span><span class="cx"> returnValue(records)
</span><span class="cx">
</span><span class="cx">
</span><del>-@inlineCallbacks
</del><span class="cx"> def allGroupDelegates(txn):
</span><del>- results = (yield txn.allGroupDelegates())
- returnValue([r[0] for r in results])
</del><ins>+ return txn.allGroupDelegates()
</ins></span></pre></div>
<a id="CalendarServerbranchesuserssagengroupcachertwextwhogroupspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/groupcacher/twext/who/groups.py (11901 => 11902)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/groupcacher/twext/who/groups.py        2013-11-07 17:02:26 UTC (rev 11901)
+++ CalendarServer/branches/users/sagen/groupcacher/twext/who/groups.py        2013-11-07 18:24:00 UTC (rev 11902)
</span><span class="lines">@@ -27,6 +27,7 @@
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks, returnValue, succeed
</span><span class="cx"> from twext.enterprise.dal.syntax import Delete
</span><span class="cx"> from twext.who.idirectory import RecordType
</span><ins>+from twext.who.delegates import allGroupDelegates
</ins><span class="cx">
</span><span class="cx"> from twext.python.log import Logger
</span><span class="cx"> log = Logger()
</span><span class="lines">@@ -156,7 +157,9 @@
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def update(self, txn):
</span><del>- # Pull in external proxy assignments and stick in proxy db
</del><ins>+ # Pull in external delegate assignments and stick in delegate db
+ # TODO
+
</ins><span class="cx"> # Figure out which groups matter
</span><span class="cx"> groupGUIDs = yield self.groupsToRefresh()
</span><span class="cx"> # For each of those groups, create a per-group refresh work item
</span><span class="lines">@@ -166,9 +169,7 @@
</span><span class="cx"> yield txn.enqueue(GroupRefreshWork,
</span><span class="cx"> groupGUID=groupGUID, notBefore=notBefore)
</span><span class="cx">
</span><del>- pass
</del><span class="cx">
</span><del>-
</del><span class="cx"> @inlineCallbacks
</span><span class="cx"> def refreshGroup(self, txn, groupGUID):
</span><span class="cx"> # Does the work of a per-group refresh work item
</span><span class="lines">@@ -253,21 +254,11 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def groupsToRefresh(self):
- delegatedGUIDs = set((yield self.proxyDB.getAllMembers()))
- self.log.info("There are %d proxies" % (len(delegatedGUIDs),))
- self.log.info("Retrieving group hierarchy from directory")
</del><ins>+ def groupsToRefresh(self, txn):
+ delegatedGUIDs = set((yield allGroupDelegates(txn)))
+ self.log.info("There are %d group delegates" % (len(delegatedGUIDs),))
</ins><span class="cx">
</span><del>- # "groups" maps a group to its members; the keys and values consist
- # of whatever directory attribute is used to refer to members. The
- # attribute value comes from record.cachedGroupsAlias().
- # "aliases" maps the record.cachedGroupsAlias() value for a group
- # back to the group's guid.
- groups, aliases = (yield self.getGroups(guids=delegatedGUIDs))
- groupGUIDs = set(aliases.keys())
- self.log.info("%d groups retrieved from the directory" %
- (len(groupGUIDs),))
</del><ins>+ # TODO: Retrieve the set of attendee group guids
+ attendeeGroupGUIDs = set()
</ins><span class="cx">
</span><del>- delegatedGUIDs = delegatedGUIDs.intersection(groupGUIDs)
- self.log.info("%d groups are proxies" % (len(delegatedGUIDs),))
- returnValue(delegatedGUIDs)
</del><ins>+ returnValue(delegatedGUIDs.union(attendeeGroupGUIDs))
</ins></span></pre></div>
<a id="CalendarServerbranchesuserssagengroupcachertwextwhotesttest_delegatespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/groupcacher/twext/who/test/test_delegates.py (11901 => 11902)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/groupcacher/twext/who/test/test_delegates.py        2013-11-07 17:02:26 UTC (rev 11901)
+++ CalendarServer/branches/users/sagen/groupcacher/twext/who/test/test_delegates.py        2013-11-07 18:24:00 UTC (rev 11902)
</span><span class="lines">@@ -19,12 +19,13 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> from twext.who.delegates import (
</span><del>- addDelegate, removeDelegate, delegatesOf, delegateFor, allGroupDelegates
</del><ins>+ addDelegate, removeDelegate, delegatesOf, delegatedTo, allGroupDelegates
</ins><span class="cx"> )
</span><span class="cx"> from twext.who.groups import GroupCacher
</span><span class="cx"> from twext.who.test.test_xml import xmlService
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks
</span><span class="cx"> from twistedcaldav.test.util import StoreTestCase
</span><ins>+from twext.who.idirectory import RecordType
</ins><span class="cx">
</span><span class="cx"> class DelegationTest(StoreTestCase):
</span><span class="cx">
</span><span class="lines">@@ -53,7 +54,7 @@
</span><span class="cx"> yield addDelegate(txn, delegator, delegate1, True)
</span><span class="cx"> delegates = (yield delegatesOf(txn, delegator, True))
</span><span class="cx"> self.assertEquals(["sagen"], [d.shortNames[0] for d in delegates])
</span><del>- delegators = (yield delegateFor(txn, delegate1, True))
</del><ins>+ delegators = (yield delegatedTo(txn, delegate1, True))
</ins><span class="cx"> self.assertEquals(["wsanchez"], [d.shortNames[0] for d in delegators])
</span><span class="cx">
</span><span class="cx"> # Add another delegate
</span><span class="lines">@@ -61,21 +62,21 @@
</span><span class="cx"> delegates = (yield delegatesOf(txn, delegator, True))
</span><span class="cx"> self.assertEquals(set(["sagen", "cdaboo"]),
</span><span class="cx"> set([d.shortNames[0] for d in delegates]))
</span><del>- delegators = (yield delegateFor(txn, delegate2, True))
</del><ins>+ delegators = (yield delegatedTo(txn, delegate2, True))
</ins><span class="cx"> self.assertEquals(["wsanchez"], [d.shortNames[0] for d in delegators])
</span><span class="cx">
</span><span class="cx"> # Remove 1 delegate
</span><span class="cx"> yield removeDelegate(txn, delegator, delegate1, True)
</span><span class="cx"> delegates = (yield delegatesOf(txn, delegator, True))
</span><span class="cx"> self.assertEquals(["cdaboo"], [d.shortNames[0] for d in delegates])
</span><del>- delegators = (yield delegateFor(txn, delegate1, True))
</del><ins>+ delegators = (yield delegatedTo(txn, delegate1, True))
</ins><span class="cx"> self.assertEquals(0, len(delegators))
</span><span class="cx">
</span><span class="cx"> # Remove the other delegate
</span><span class="cx"> yield removeDelegate(txn, delegator, delegate2, True)
</span><span class="cx"> delegates = (yield delegatesOf(txn, delegator, True))
</span><span class="cx"> self.assertEquals(0, len(delegates))
</span><del>- delegators = (yield delegateFor(txn, delegate2, True))
</del><ins>+ delegators = (yield delegatedTo(txn, delegate2, True))
</ins><span class="cx"> self.assertEquals(0, len(delegators))
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -85,8 +86,9 @@
</span><span class="cx"> txn = store.newTransaction()
</span><span class="cx">
</span><span class="cx"> delegator = yield self.xmlService.recordWithUID("__wsanchez__")
</span><ins>+ delegate1 = yield self.xmlService.recordWithUID("__sagen__")
</ins><span class="cx"> group1 = yield self.xmlService.recordWithUID("__top_group_1__")
</span><del>- delegate1 = yield self.xmlService.recordWithUID("__sagen__")
</del><ins>+ group2 = yield self.xmlService.recordWithUID("__sub_group_1__")
</ins><span class="cx">
</span><span class="cx"> # Add group delegate, but before the group membership has been
</span><span class="cx"> # pulled in
</span><span class="lines">@@ -96,16 +98,16 @@
</span><span class="cx">
</span><span class="cx"> # Now refresh the group and there will be 3 delegates (contained
</span><span class="cx"> # within 2 nested groups)
</span><del>- guid = "49b350c69611477b94d95516b13856ab"
- yield self.groupCacher.refreshGroup(txn, guid)
</del><ins>+ # guid = "49b350c69611477b94d95516b13856ab"
+ yield self.groupCacher.refreshGroup(txn, group1.guid)
+ yield self.groupCacher.refreshGroup(txn, group2.guid)
</ins><span class="cx"> delegates = (yield delegatesOf(txn, delegator, True))
</span><span class="cx"> self.assertEquals(set(["sagen", "cdaboo", "glyph"]),
</span><span class="cx"> set([d.shortNames[0] for d in delegates]))
</span><del>- delegators = (yield delegateFor(txn, delegate1, True))
</del><ins>+ delegators = (yield delegatedTo(txn, delegate1, True))
</ins><span class="cx"> self.assertEquals(["wsanchez"], [d.shortNames[0] for d in delegators])
</span><span class="cx">
</span><span class="cx"> # Verify we can ask for all delegated-to groups
</span><del>- group2 = yield self.xmlService.recordWithUID("__sub_group_1__")
</del><span class="cx"> yield addDelegate(txn, delegator, group2, True)
</span><span class="cx"> groups = (yield allGroupDelegates(txn))
</span><span class="cx"> self.assertEquals(
</span><span class="lines">@@ -114,8 +116,39 @@
</span><span class="cx"> "86144f73345a409782f1b782672087c7"
</span><span class="cx"> ]), set(groups))
</span><span class="cx">
</span><ins>+ # Delegate to a user who is already indirectly delegated-to
+ yield addDelegate(txn, delegator, delegate1, True)
+ delegates = (yield delegatesOf(txn, delegator, True))
+ self.assertEquals(set(["sagen", "cdaboo", "glyph"]),
+ set([d.shortNames[0] for d in delegates]))
</ins><span class="cx">
</span><ins>+ # Add a member to the group; they become a delegate
+ newSet = set()
+ for name in ("wsanchez", "cdaboo", "sagen", "glyph", "dre"):
+ record = (yield self.xmlService.recordWithShortName(RecordType.user,
+ name))
+ newSet.add(record.guid)
+ groupID, name, membershipHash = (yield txn.groupByGUID(group1.guid))
+ numAdded, numRemoved = (yield self.groupCacher.synchronizeMembers(txn,
+ groupID, newSet))
+ delegates = (yield delegatesOf(txn, delegator, True))
+ self.assertEquals(set(["sagen", "cdaboo", "glyph", "dre"]),
+ set([d.shortNames[0] for d in delegates]))
</ins><span class="cx">
</span><ins>+ # Remove delegate access from the top group
+ yield removeDelegate(txn, delegator, group1, True)
+ delegates = (yield delegatesOf(txn, delegator, True))
+ self.assertEquals(set(["sagen", "cdaboo"]),
+ set([d.shortNames[0] for d in delegates]))
+
+ # Remove delegate access from the sub group
+ yield removeDelegate(txn, delegator, group2, True)
+ delegates = (yield delegatesOf(txn, delegator, True))
+ self.assertEquals(set(["sagen"]),
+ set([d.shortNames[0] for d in delegates]))
+
+
+
</ins><span class="cx"> testXMLConfig = """<?xml version="1.0" encoding="utf-8"?>
</span><span class="cx">
</span><span class="cx"> <directory realm="xyzzy">
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagengroupcachertwextwhotesttest_groupspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/groupcacher/twext/who/test/test_groups.py (11901 => 11902)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/groupcacher/twext/who/test/test_groups.py        2013-11-07 17:02:26 UTC (rev 11901)
+++ CalendarServer/branches/users/sagen/groupcacher/twext/who/test/test_groups.py        2013-11-07 18:24:00 UTC (rev 11902)
</span><span class="lines">@@ -75,15 +75,19 @@
</span><span class="cx"> self.assertEquals(membershipHash, "e90052eb63d47f32d5b03df0073f7854")
</span><span class="cx">
</span><span class="cx"> results = (yield txn.membersOfGroup(groupID))
</span><del>- for row in results:
- print row[0]
</del><ins>+ self.assertEquals(
+ set(["9064df911dbc4e079c2b6839b0953876",
+ "4ad155cbae9b475f986ce08a7537893e",
+ "3bdcb95484d54f6d8035eac19a6d6e1f",
+ "7d45cb10479e456bb54d528958c5734b"]),
+ set([r[0] for r in results])
+ )
</ins><span class="cx">
</span><span class="cx"> records = (yield self.groupCacher.cachedMembers(txn, groupID))
</span><span class="cx"> self.assertEquals(
</span><span class="cx"> set([r.shortNames[0] for r in records]),
</span><span class="cx"> set(["wsanchez", "cdaboo", "glyph", "sagen"])
</span><span class="cx"> )
</span><del>- print records
</del><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagengroupcachertxdavcommondatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/groupcacher/txdav/common/datastore/sql.py (11901 => 11902)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/groupcacher/txdav/common/datastore/sql.py        2013-11-07 17:02:26 UTC (rev 11901)
+++ CalendarServer/branches/users/sagen/groupcacher/txdav/common/datastore/sql.py        2013-11-07 18:24:00 UTC (rev 11902)
</span><span class="lines">@@ -1016,6 +1016,7 @@
</span><span class="cx"> de.READ_WRITE: Parameter("readWrite"),
</span><span class="cx"> })
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @classproperty
</span><span class="cx"> def _addDelegateGroupQuery(cls): #@NoSelf
</span><span class="cx"> ds = schema.DELEGATE_GROUPS
</span><span class="lines">@@ -1024,6 +1025,7 @@
</span><span class="cx"> ds.READ_WRITE: Parameter("readWrite"),
</span><span class="cx"> })
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @classproperty
</span><span class="cx"> def _removeDelegateQuery(cls): #@NoSelf
</span><span class="cx"> de = schema.DELEGATES
</span><span class="lines">@@ -1033,6 +1035,7 @@
</span><span class="cx"> de.READ_WRITE == Parameter("readWrite"))
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @classproperty
</span><span class="cx"> def _removeDelegateGroupQuery(cls): #@NoSelf
</span><span class="cx"> ds = schema.DELEGATE_GROUPS
</span><span class="lines">@@ -1042,6 +1045,7 @@
</span><span class="cx"> ds.READ_WRITE == Parameter("readWrite"))
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @classproperty
</span><span class="cx"> def _selectDelegatesQuery(cls): #@NoSelf
</span><span class="cx"> de = schema.DELEGATES
</span><span class="lines">@@ -1050,6 +1054,7 @@
</span><span class="cx"> de.READ_WRITE == Parameter("readWrite"))
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @classproperty
</span><span class="cx"> def _selectDelegateGroupsQuery(cls): #@NoSelf
</span><span class="cx"> ds = schema.DELEGATE_GROUPS
</span><span class="lines">@@ -1059,6 +1064,7 @@
</span><span class="cx"> ds.READ_WRITE == Parameter("readWrite"))
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @classproperty
</span><span class="cx"> def _selectDirectDelegatorsQuery(cls): #@NoSelf
</span><span class="cx"> de = schema.DELEGATES
</span><span class="lines">@@ -1068,17 +1074,6 @@
</span><span class="cx"> de.READ_WRITE == Parameter("readWrite"))
</span><span class="cx"> )
</span><span class="cx">
</span><del>- """
- @classproperty
- def _selectDelegatorsGroupsQuery(cls): #@NoSelf
- ds = schema.DELEGATE_GROUPS
- return Select([ds.DELEGATOR], From=ds,
- Where=(
- ds.GROUP_ID == Parameter("groupID").And(
- ds.READ_WRITE == Parameter("readWrite"))
- )
- )
- """
</del><span class="cx">
</span><span class="cx"> @classproperty
</span><span class="cx"> def _selectIndirectDelegatorsQuery(cls): #@NoSelf
</span><span class="lines">@@ -1101,6 +1096,7 @@
</span><span class="cx"> )
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @classproperty
</span><span class="cx"> def _selectIndirectDelegatesQuery(cls): #@NoSelf
</span><span class="cx"> dg = schema.DELEGATE_GROUPS
</span><span class="lines">@@ -1121,70 +1117,166 @@
</span><span class="cx"> )
</span><span class="cx"> )
</span><span class="cx">
</span><del>- def addDelegate(self, delegator, delegate, readWrite, isGroup):
- if isGroup:
- return self._addDelegateGroupQuery.on(self, delegator=delegator,
- groupID=delegate, readWrite=readWrite)
- else:
- return self._addDelegateQuery.on(self, delegator=delegator,
- delegate=delegate, readWrite=readWrite)
</del><span class="cx">
</span><del>- def removeDelegate(self, delegator, delegate, readWrite, isGroup):
- if isGroup:
- return self._removeDelegateGroupQuery.on(self, delegator=delegator,
- groupID=delegate, readWrite=readWrite)
- else:
- return self._removeDelegateQuery.on(self, delegator=delegator,
- delegate=delegate, readWrite=readWrite)
</del><ins>+ def addDelegate(self, delegator, delegate, readWrite):
+ """
+ Adds a row to the DELEGATES table. The delegate should not be a
+ group. To delegate to a group, call addDelegateGroup() instead.
</ins><span class="cx">
</span><del>- """
- def directDelegates(self, delegator, readWrite):
- return self._selectDelegatesQuery.on(self, delegator=delegator,
- readWrite=readWrite)
</del><ins>+ @param delegator: the GUID of the delegator
+ @type delegator: C{str} in normalized form (lowercase, no dashes)
+ @param delegate: the GUID of the delegate
+ @type delegate: C{str} in normalized form (lowercase, no dashes)
+ @param readWrite: grant read and write access if True, otherwise
+ read-only access
+ @type readWrite: C{boolean}
+ """
+ return self._addDelegateQuery.on(self, delegator=delegator,
+ delegate=delegate, readWrite=1 if readWrite else 0)
</ins><span class="cx">
</span><del>- def groupDelegates(self, delegator, readWrite):
- return self._selectDelegateGroupssQuery.on(self, delegator=delegator,
- readWrite=readWrite)
- """
</del><span class="cx">
</span><ins>+ def addDelegateGroup(self, delegator, delegateGroupID, readWrite):
+ """
+ Adds a row to the DELEGATE_GROUPS table. The delegate should be a
+ group. To delegate to a person, call addDelegate() instead.
+
+ @param delegator: the GUID of the delegator
+ @type delegator: C{str} in normalized form (lowercase, no dashes)
+ @param delegateGroupID: the GROUP_ID of the delegate group
+ @type delegateGroupID: C{int}
+ @param readWrite: grant read and write access if True, otherwise
+ read-only access
+ @type readWrite: C{boolean}
+ """
+ return self._addDelegateGroupQuery.on(self, delegator=delegator,
+ groupID=delegateGroupID, readWrite=1 if readWrite else 0)
+
+
+ def removeDelegate(self, delegator, delegate, readWrite):
+ """
+ Removes a row from the DELEGATES table. The delegate should not be a
+ group. To remove a delegate group, call removeDelegateGroup() instead.
+
+ @param delegator: the GUID of the delegator
+ @type delegator: C{str} in normalized form (lowercase, no dashes)
+ @param delegate: the GUID of the delegate
+ @type delegate: C{str} in normalized form (lowercase, no dashes)
+ @param readWrite: remove read and write access if True, otherwise
+ read-only access
+ @type readWrite: C{boolean}
+ """
+ return self._removeDelegateQuery.on(self, delegator=delegator,
+ delegate=delegate, readWrite=1 if readWrite else 0)
+
+
+ def removeDelegateGroup(self, delegator, delegateGroupID, readWrite):
+ """
+ Removes a row from the DELEGATE_GROUPS table. The delegate should be a
+ group. To remove a delegate person, call removeDelegate() instead.
+
+ @param delegator: the GUID of the delegator
+ @type delegator: C{str} in normalized form (lowercase, no dashes)
+ @param delegateGroupID: the GROUP_ID of the delegate group
+ @type delegateGroupID: C{int}
+ @param readWrite: remove read and write access if True, otherwise
+ read-only access
+ @type readWrite: C{boolean}
+ """
+ return self._removeDelegateGroupQuery.on(self, delegator=delegator,
+ groupID=delegateGroupID, readWrite=1 if readWrite else 0)
+
+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def delegates(self, delegator, readWrite):
</span><ins>+ """
+ Returns the GUIDs of all delegates for the given delegator. If
+ delegate access was granted to any groups, those groups' members
+ (flattened) will be included. No GUIDs of the groups themselves
+ will be returned.
</ins><span class="cx">
</span><ins>+ @param delegator: the GUID of the delegator
+ @type delegator: C{str} in normalized form (lowercase, no dashes)
+ @param readWrite: the access-type to check for; read and write
+ access if True, otherwise read-only access
+ @type readWrite: C{boolean}
+ @returns: the GUIDs of the delegates (for the specified access
+ type)
+ @rtype: a Deferred resulting in a set
+ """
+ delegates = set()
+
</ins><span class="cx"> # First get the direct delegates
</span><span class="cx"> results = (yield self._selectDelegatesQuery.on(self,
</span><del>- delegator=delegator, readWrite=readWrite))
</del><ins>+ delegator=delegator, readWrite=1 if readWrite else 0))
+ for row in results:
+ delegates.add(row[0])
</ins><span class="cx">
</span><span class="cx"> # Finally get those who are in groups which have been delegated to
</span><del>- results.extend((yield self._selectIndirectDelegatesQuery.on(self,
- delegator=delegator, readWrite=readWrite)))
</del><ins>+ results = (yield self._selectIndirectDelegatesQuery.on(self,
+ delegator=delegator, readWrite=1 if readWrite else 0))
+ for row in results:
+ delegates.add(row[0])
</ins><span class="cx">
</span><del>- returnValue(results)
</del><ins>+ returnValue(delegates)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def delegators(self, delegate, readWrite):
</span><ins>+ """
+ Returns the GUIDs of all delegators which have granted access to
+ the given delegate, either directly or indirectly via groups.
</ins><span class="cx">
</span><ins>+ @param delegate: the GUID of the delegate
+ @type delegate: C{str} in normalized form (lowercase, no dashes)
+ @param readWrite: the access-type to check for; read and write
+ access if True, otherwise read-only access
+ @type readWrite: C{boolean}
+ @returns: the GUIDs of the delegators (for the specified access
+ type)
+ @rtype: a Deferred resulting in a set
+ """
+ delegators = set()
+
</ins><span class="cx"> # First get the direct delegators
</span><span class="cx"> results = (yield self._selectDirectDelegatorsQuery.on(self,
</span><del>- delegate=delegate, readWrite=readWrite))
</del><ins>+ delegate=delegate, readWrite=1 if readWrite else 0))
+ for row in results:
+ delegators.add(row[0])
</ins><span class="cx">
</span><span class="cx"> # Finally get those who have delegated to groups the delegate
</span><span class="cx"> # is a member of
</span><del>- results.extend((yield self._selectIndirectDelegatorsQuery.on(self,
- delegate=delegate, readWrite=readWrite)))
- returnValue(results)
</del><ins>+ results = (yield self._selectIndirectDelegatorsQuery.on(self,
+ delegate=delegate, readWrite=1 if readWrite else 0))
+ for row in results:
+ delegators.add(row[0])
</ins><span class="cx">
</span><ins>+ returnValue(delegators)
</ins><span class="cx">
</span><ins>+
+ @inlineCallbacks
</ins><span class="cx"> def allGroupDelegates(self):
</span><ins>+ """
+ Return the GUIDs of all groups which have been delegated to. Useful
+ for obtaining the set of groups which need to be synchronized from
+ the directory.
+
+ @returns: the GUIDs of all delegated-to groups
+ @rtype: a Deferred resulting in a set
+ """
</ins><span class="cx"> gr = schema.GROUPS
</span><span class="cx"> dg = schema.DELEGATE_GROUPS
</span><span class="cx">
</span><del>- return Select(
</del><ins>+ results = (yield Select(
</ins><span class="cx"> [gr.GROUP_GUID],
</span><span class="cx"> From=gr,
</span><span class="cx"> Where=(gr.GROUP_ID.In(Select([dg.GROUP_ID], From=dg, Where=None)))
</span><del>- ).on(self)
</del><ins>+ ).on(self))
+ delegates = set()
+ for row in results:
+ delegates.add(row[0])
</ins><span class="cx">
</span><ins>+ returnValue(delegates)
+
</ins><span class="cx"> # End of Delegates
</span><span class="cx">
</span><span class="cx">
</span></span></pre>
</div>
</div>
</body>
</html>