<!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>[13651] CalendarServer/branches/release/CalendarServer-5.3-dev</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/13651">13651</a></dd>
<dt>Author</dt> <dd>cdaboo@apple.com</dd>
<dt>Date</dt> <dd>2014-06-18 11:06:15 -0700 (Wed, 18 Jun 2014)</dd>
</dl>
<h3>Log Message</h3>
<pre>Hide missing or invalid proxyFors. Fix some sharing issues related to missing or invalid users.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesreleaseCalendarServer53devtwistedcaldavdirectoryprincipalpy">CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/principal.py</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer53devtwistedcaldavdirectorytestaccountsxml">CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/accounts.xml</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer53devtwistedcaldavdirectorytestaugmentsxml">CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/augments.xml</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer53devtwistedcaldavdirectorytesttest_proxyprincipalmemberspy">CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/test_proxyprincipalmembers.py</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer53devtwistedcaldavsharingpy">CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/sharing.py</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer53devtwistedcaldavtesttest_sharingpy">CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/test/test_sharing.py</a></li>
<li><a href="#CalendarServerbranchesreleaseCalendarServer53devtxdavcommondatastoresqlpy">CalendarServer/branches/release/CalendarServer-5.3-dev/txdav/common/datastore/sql.py</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServerbranchesreleaseCalendarServer53devtwistedcaldavdirectoryprincipalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/principal.py (13650 => 13651)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/principal.py        2014-06-17 21:55:29 UTC (rev 13650)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/principal.py        2014-06-18 18:06:15 UTC (rev 13651)
</span><span class="lines">@@ -923,7 +923,7 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def proxyFor(self, read_write, resolve_memberships=True):
</del><ins>+ def proxyFor(self, read_write, resolve_memberships=True, ignoreDisabled=True):
</ins><span class="cx">
</span><span class="cx"> proxyFors = set()
</span><span class="cx">
</span><span class="lines">@@ -960,7 +960,7 @@
</span><span class="cx">
</span><span class="cx"> uids = set()
</span><span class="cx"> for principal in tuple(proxyFors):
</span><del>- if principal.principalUID() in uids:
</del><ins>+ if principal.principalUID() in uids or (ignoreDisabled and not principal.record.enabledForCalendaring):
</ins><span class="cx"> proxyFors.remove(principal)
</span><span class="cx"> else:
</span><span class="cx"> uids.add(principal.principalUID())
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer53devtwistedcaldavdirectorytestaccountsxml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/accounts.xml (13650 => 13651)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/accounts.xml        2014-06-17 21:55:29 UTC (rev 13650)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/accounts.xml        2014-06-18 18:06:15 UTC (rev 13651)
</span><span class="lines">@@ -139,7 +139,7 @@
</span><span class="cx"> <member type="users">delegateviagroup</member>
</span><span class="cx"> </members>
</span><span class="cx"> </group>
</span><del>- <user repeat="2">
</del><ins>+ <user repeat="3">
</ins><span class="cx"> <uid>user%02d</uid>
</span><span class="cx"> <guid>user%02d</guid>
</span><span class="cx"> <password>%02duser</password>
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer53devtwistedcaldavdirectorytestaugmentsxml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/augments.xml (13650 => 13651)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/augments.xml        2014-06-17 21:55:29 UTC (rev 13650)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/augments.xml        2014-06-18 18:06:15 UTC (rev 13651)
</span><span class="lines">@@ -57,7 +57,7 @@
</span><span class="cx"> <enable-calendar>false</enable-calendar>
</span><span class="cx"> <enable-addressbook>false</enable-addressbook>
</span><span class="cx"> </record>
</span><del>- <record repeat="2">
</del><ins>+ <record repeat="3">
</ins><span class="cx"> <uid>user%02d</uid>
</span><span class="cx"> <enable>true</enable>
</span><span class="cx"> <enable-calendar>true</enable-calendar>
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer53devtwistedcaldavdirectorytesttest_proxyprincipalmemberspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/test_proxyprincipalmembers.py (13650 => 13651)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/test_proxyprincipalmembers.py        2014-06-17 21:55:29 UTC (rev 13650)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/directory/test/test_proxyprincipalmembers.py        2014-06-18 18:06:15 UTC (rev 13651)
</span><span class="lines">@@ -504,3 +504,49 @@
</span><span class="cx"> DirectoryService.recordType_users, "delegator", "calendar-proxy-write",
</span><span class="cx"> ("Occasional Delegate", "Delegate Via Group", "Delegate Group"),
</span><span class="cx"> )
</span><ins>+
+
+ @inlineCallbacks
+ def test_hideDisabledProxies(self):
+ """
+ Make sure users that are missing or not enabled for calendaring are removed
+ from the proxyFor list.
+ """
+
+ # Check proxies empty right now
+ principal01 = self._getPrincipalByShortName(self.directoryService.recordType_users, "user01")
+ self.assertTrue(len((yield principal01.proxyFor(False))) == 0)
+ self.assertTrue(len((yield principal01.proxyFor(True))) == 0)
+
+ principal02 = self._getPrincipalByShortName(self.directoryService.recordType_users, "user02")
+ self.assertTrue(len((yield principal02.proxyFor(False))) == 0)
+ self.assertTrue(len((yield principal02.proxyFor(True))) == 0)
+
+ principal03 = self._getPrincipalByShortName(self.directoryService.recordType_users, "user03")
+ self.assertTrue(len((yield principal03.proxyFor(False))) == 0)
+ self.assertTrue(len((yield principal03.proxyFor(True))) == 0)
+
+ # Make user01 a read-only proxy for user02 and user03
+ yield self._addProxy(principal02, "calendar-proxy-read", principal01)
+ yield self._addProxy(principal03, "calendar-proxy-read", principal01)
+
+ self.assertTrue(len((yield principal01.proxyFor(False))) == 2)
+ self.assertTrue(len((yield principal01.proxyFor(True))) == 0)
+
+ # Now disable user02
+ principal02.record.enabledForCalendaring = False
+
+ self.assertTrue(len((yield principal01.proxyFor(False))) == 1)
+ self.assertTrue(len((yield principal01.proxyFor(True))) == 0)
+
+ # Now enable user02
+ principal02.record.enabledForCalendaring = True
+
+ self.assertTrue(len((yield principal01.proxyFor(False))) == 2)
+ self.assertTrue(len((yield principal01.proxyFor(True))) == 0)
+
+ # Now remove user02
+ self.directoryService._removeFromIndex(principal02.record)
+
+ self.assertTrue(len((yield principal01.proxyFor(False))) == 1)
+ self.assertTrue(len((yield principal01.proxyFor(True))) == 0)
</ins></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer53devtwistedcaldavsharingpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/sharing.py (13650 => 13651)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/sharing.py        2014-06-17 21:55:29 UTC (rev 13650)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/sharing.py        2014-06-18 18:06:15 UTC (rev 13651)
</span><span class="lines">@@ -103,12 +103,16 @@
</span><span class="cx"> invitations = yield original.validateInvites(request)
</span><span class="cx">
</span><span class="cx"> ownerPrincipal = (yield original.ownerPrincipal(request))
</span><del>- # FIXME: use urn:uuid in all cases
- if self.isCalendarCollection():
- owner = ownerPrincipal.principalURL()
</del><ins>+ if ownerPrincipal is None:
+ owner = "invalid"
+ ownerCN = "Invalid"
</ins><span class="cx"> else:
</span><del>- owner = "urn:uuid:" + ownerPrincipal.principalUID()
- ownerCN = ownerPrincipal.displayName()
</del><ins>+ # FIXME: use urn:uuid in all cases
+ if self.isCalendarCollection():
+ owner = ownerPrincipal.principalURL()
+ else:
+ owner = "urn:uuid:" + ownerPrincipal.principalUID()
+ ownerCN = ownerPrincipal.displayName()
</ins><span class="cx">
</span><span class="cx"> returnValue(customxml.Invite(
</span><span class="cx"> customxml.Organizer(
</span><span class="lines">@@ -315,7 +319,7 @@
</span><span class="cx"> access control mechanism has dictate the home should no longer have
</span><span class="cx"> any access at all.
</span><span class="cx"> """
</span><del>- if self._share.direct():
</del><ins>+ if self._share is not None and self._share.direct():
</ins><span class="cx"> ownerUID = self._share.ownerUID()
</span><span class="cx"> owner = self.principalForUID(ownerUID)
</span><span class="cx"> if owner.record.recordType == WikiDirectoryService.recordType_wikis:
</span><span class="lines">@@ -482,14 +486,17 @@
</span><span class="cx"> """
</span><span class="cx"> # assert request
</span><span class="cx"> invitations = yield self._allInvitations()
</span><ins>+ adjusted_invitations = []
</ins><span class="cx"> for invitation in invitations:
</span><span class="cx"> if invitation.status() != _BIND_STATUS_INVALID:
</span><span class="cx"> if not (yield self.validUserIDForShare("urn:uuid:" + invitation.shareeUID(), request)):
</span><span class="cx"> # FIXME: temporarily disable this to deal with flaky directory
</span><span class="cx"> #yield self._updateInvitation(invitation, status=_BIND_STATUS_INVALID)
</span><span class="cx"> self.log.error("Invalid sharee detected: {uid}", uid=invitation.shareeUID())
</span><ins>+ invitation = invitation._replace(status=_BIND_STATUS_INVALID)
+ adjusted_invitations.append(invitation)
</ins><span class="cx">
</span><del>- returnValue(invitations)
</del><ins>+ returnValue(adjusted_invitations)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def inviteUserToShare(self, userid, cn, ace, summary, request):
</span><span class="lines">@@ -648,15 +655,19 @@
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def uninviteSingleUserFromShare(self, userid, aces, request): #@UnusedVariable
</span><del>- # Cancel invites - we'll just use whatever userid we are given
</del><span class="cx">
</span><ins>+ # Cancel invites - we'll just use whatever userid we are given. However, if we
+ # cannot find a matching principal, try to extract the uid from the userid
+ # and use that (to allow invalid principals to be removed).
</ins><span class="cx"> sharee = self.principalForCalendarUserAddress(userid)
</span><del>- if not sharee:
</del><ins>+ if sharee is not None:
+ uid = sharee.principalUID()
+ elif userid.startswith("urn:uuid:"):
+ uid = userid[9:]
+ else:
</ins><span class="cx"> returnValue(False)
</span><span class="cx">
</span><del>- shareeUID = sharee.principalUID()
-
- invitation = yield self._invitationForShareeUID(shareeUID)
</del><ins>+ invitation = yield self._invitationForShareeUID(uid)
</ins><span class="cx"> if invitation:
</span><span class="cx"> result = (yield self.uninviteFromShare(invitation, request))
</span><span class="cx"> else:
</span><span class="lines">@@ -1026,8 +1037,8 @@
</span><span class="cx"> L{SharedHomeMixin} is a I{sharee}'s view of a shared calendar object,
</span><span class="cx"> associate it with a L{Share}.
</span><span class="cx"> """
</span><del>- share = yield self._shareForStoreObject(child._newStoreObject, request)
- if share:
</del><ins>+ if child._newStoreObject is not None and not child._newStoreObject.owned():
+ share = yield self._shareForStoreObject(child._newStoreObject, request)
</ins><span class="cx"> child.setShare(share)
</span><span class="cx"> access = yield child._checkAccessControl()
</span><span class="cx"> if access is None:
</span></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer53devtwistedcaldavtesttest_sharingpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/test/test_sharing.py (13650 => 13651)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/test/test_sharing.py        2014-06-17 21:55:29 UTC (rev 13650)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/twistedcaldav/test/test_sharing.py        2014-06-18 18:06:15 UTC (rev 13651)
</span><span class="lines">@@ -51,6 +51,12 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><ins>+class FakeRequest(object):
+ def locateResource(self, url):
+ return succeed(None)
+
+
+
</ins><span class="cx"> class FakeHome(object):
</span><span class="cx"> def removeShareByUID(self, request, uid):
</span><span class="cx"> pass
</span><span class="lines">@@ -171,8 +177,8 @@
</span><span class="cx"> that marks any principal without 'bogus' in its name.
</span><span class="cx"> """
</span><span class="cx"> result = principalForCalendarUserAddress(resourceSelf, userid)
</span><del>- if result is None:
- return result
</del><ins>+ if result is None or userid[9:] in result.invalid_names:
+ return None
</ins><span class="cx"> return result.principalURL()
</span><span class="cx">
</span><span class="cx"> @patched
</span><span class="lines">@@ -221,8 +227,8 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def _doPOSTSharerAccept(self, body, resultcode=responsecode.OK):
- request = SimpleStoreRequest(self, "POST", "/calendars/__uids__/user02/", content=body, authid="user02")
</del><ins>+ def _doPOSTSharerAccept(self, body, resultcode=responsecode.OK, sharer="user02"):
+ request = SimpleStoreRequest(self, "POST", "/calendars/__uids__/{}/".format(sharer), content=body, authid=sharer)
</ins><span class="cx"> request.headers.setHeader("content-type", MimeType("text", "xml"))
</span><span class="cx"> response = yield self.send(request)
</span><span class="cx"> response = IResponse(response)
</span><span class="lines">@@ -252,12 +258,22 @@
</span><span class="cx"> return None
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def _getUIDElementValues(self, xml):
+
+ results = {}
+ for user in xml.children:
+ href = str(user.childOfType(davxml.HRef))
+ uid = str(user.childOfType(customxml.UID))
+ results[href] = uid
+ return results
+
+
</ins><span class="cx"> def _clearUIDElementValue(self, xml):
</span><span class="cx">
</span><span class="cx"> for user in xml.children:
</span><del>- for element in user.children:
- if type(element) == customxml.UID:
- element.children[0].data = ""
</del><ins>+ uid = user.childOfType(customxml.UID)
+ if uid is not None:
+ uid.children[0].data = ""
</ins><span class="cx"> return xml
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -717,25 +733,23 @@
</span><span class="cx"> )
</span><span class="cx"> ))
</span><span class="cx">
</span><del>- self.resource.validUserIDForShare = lambda userid, request: None
- self.resource.principalForCalendarUserAddress = lambda cuaddr: None
- self.resource.principalForUID = lambda principalUID: None
</del><ins>+ self.patch(FakePrincipal, "invalid_names", set(("user02",)))
</ins><span class="cx">
</span><span class="cx"> propInvite = (yield self.resource.readProperty(customxml.Invite, None))
</span><span class="cx"> self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
</span><span class="cx"> customxml.InviteUser(
</span><span class="cx"> customxml.UID.fromString(""),
</span><span class="cx"> davxml.HRef.fromString("urn:uuid:user02"),
</span><del>- customxml.CommonName.fromString("user02"),
</del><ins>+ customxml.CommonName.fromString("USER02"),
</ins><span class="cx"> customxml.InviteAccess(customxml.ReadWriteAccess()),
</span><del>- customxml.InviteStatusNoResponse(),
</del><ins>+ customxml.InviteStatusInvalid(),
</ins><span class="cx"> )
</span><span class="cx"> ))
</span><span class="cx">
</span><span class="cx"> yield self._doPOST("""<?xml version="1.0" encoding="utf-8" ?>
</span><span class="cx"> <CS:share xmlns:D="DAV:" xmlns:CS="http://calendarserver.org/ns/">
</span><span class="cx"> <CS:remove>
</span><del>- <D:href>mailto:user02@example.com</D:href>
</del><ins>+ <D:href>urn:uuid:user02</D:href>
</ins><span class="cx"> </CS:remove>
</span><span class="cx"> </CS:share>
</span><span class="cx"> """)
</span><span class="lines">@@ -828,7 +842,7 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def test_POSTDowngradeWithDisabledInvitee(self):
</del><ins>+ def test_POSTDowngradeWithMissingInvitee(self):
</ins><span class="cx">
</span><span class="cx"> yield self.resource.upgradeToShare()
</span><span class="cx">
</span><span class="lines">@@ -858,7 +872,7 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def test_POSTRemoveWithDisabledInvitee(self):
</del><ins>+ def test_POSTRemoveWithMissingInvitee(self):
</ins><span class="cx">
</span><span class="cx"> yield self.resource.upgradeToShare()
</span><span class="cx">
</span><span class="lines">@@ -888,7 +902,7 @@
</span><span class="cx"> yield self._doPOST("""<?xml version="1.0" encoding="utf-8" ?>
</span><span class="cx"> <CS:share xmlns:D="DAV:" xmlns:CS="http://calendarserver.org/ns/">
</span><span class="cx"> <CS:remove>
</span><del>- <D:href>mailto:user02@example.com</D:href>
</del><ins>+ <D:href>urn:uuid:user02</D:href>
</ins><span class="cx"> </CS:remove>
</span><span class="cx"> </CS:share>
</span><span class="cx"> """)
</span><span class="lines">@@ -951,3 +965,252 @@
</span><span class="cx">
</span><span class="cx"> resource = (yield self._getResourceSharer(href))
</span><span class="cx"> self.assertFalse(resource.exists())
</span><ins>+
+
+ @inlineCallbacks
+ def test_POSTShareeRemoveWithMissingSharer(self):
+
+ yield self.resource.upgradeToShare()
+
+ yield self._doPOST("""<?xml version="1.0" encoding="utf-8" ?>
+ <CS:share xmlns:D="DAV:" xmlns:CS="http://calendarserver.org/ns/">
+ <CS:set>
+ <D:href>mailto:user02@example.com</D:href>
+ <CS:summary>My Shared Calendar</CS:summary>
+ <CS:read-write/>
+ </CS:set>
+ </CS:share>
+ """)
+
+ propInvite = (yield self.resource.readProperty(customxml.Invite, None))
+ uid = self._getUIDElementValue(propInvite)
+ self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
+ customxml.InviteUser(
+ customxml.UID.fromString(""),
+ davxml.HRef.fromString("urn:uuid:user02"),
+ customxml.CommonName.fromString("USER02"),
+ customxml.InviteAccess(customxml.ReadWriteAccess()),
+ customxml.InviteStatusNoResponse(),
+ ),
+ ))
+
+ result = (yield self._doPOSTSharerAccept("""<?xml version='1.0' encoding='UTF-8'?>
+ <invite-reply xmlns='http://calendarserver.org/ns/'>
+ <href xmlns='DAV:'>mailto:user01@example.com</href>
+ <invite-accepted/>
+ <hosturl>
+ <href xmlns='DAV:'>/calendars/__uids__/user01/calendar/</href>
+ </hosturl>
+ <in-reply-to>%s</in-reply-to>
+ <summary>The Shared Calendar</summary>
+ <common-name>USER02</common-name>
+ <first-name>user</first-name>
+ <last-name>02</last-name>
+ </invite-reply>
+ """ % (uid,))
+ )
+ href = self._getHRefElementValue(result) + "/"
+
+ self.patch(FakePrincipal, "invalid_names", set(("user01",)))
+
+ resource = (yield self._getResourceSharer(href))
+ yield resource.removeShareeResource(SimpleStoreRequest(self, "DELETE", href))
+
+ resource = (yield self._getResourceSharer(href))
+ self.assertFalse(resource.exists())
+
+
+ @inlineCallbacks
+ def test_shareeInviteWithDisabledSharer(self):
+
+ yield self.resource.upgradeToShare()
+
+ yield self._doPOST("""<?xml version="1.0" encoding="utf-8" ?>
+ <CS:share xmlns:D="DAV:" xmlns:CS="http://calendarserver.org/ns/">
+ <CS:set>
+ <D:href>mailto:user02@example.com</D:href>
+ <CS:summary>My Shared Calendar</CS:summary>
+ <CS:read-write/>
+ </CS:set>
+ </CS:share>
+ """)
+
+ propInvite = (yield self.resource.readProperty(customxml.Invite, None))
+ uid = self._getUIDElementValue(propInvite)
+ self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
+ customxml.InviteUser(
+ customxml.UID.fromString(""),
+ davxml.HRef.fromString("urn:uuid:user02"),
+ customxml.CommonName.fromString("USER02"),
+ customxml.InviteAccess(customxml.ReadWriteAccess()),
+ customxml.InviteStatusNoResponse(),
+ ),
+ ))
+
+ result = (yield self._doPOSTSharerAccept("""<?xml version='1.0' encoding='UTF-8'?>
+ <invite-reply xmlns='http://calendarserver.org/ns/'>
+ <href xmlns='DAV:'>mailto:user01@example.com</href>
+ <invite-accepted/>
+ <hosturl>
+ <href xmlns='DAV:'>/calendars/__uids__/user01/calendar/</href>
+ </hosturl>
+ <in-reply-to>%s</in-reply-to>
+ <summary>The Shared Calendar</summary>
+ <common-name>USER02</common-name>
+ <first-name>user</first-name>
+ <last-name>02</last-name>
+ </invite-reply>
+ """ % (uid,))
+ )
+ href = self._getHRefElementValue(result) + "/"
+
+ self.patch(FakePrincipal, "invalid_names", set(("user01",)))
+
+ resource = (yield self._getResourceSharer(href))
+ propInvite = yield resource.inviteProperty(FakeRequest())
+ self.assertEquals(propInvite, None)
+
+
+ @inlineCallbacks
+ def test_shareeInviteWithMissingSharer(self):
+
+ yield self.resource.upgradeToShare()
+
+ yield self._doPOST("""<?xml version="1.0" encoding="utf-8" ?>
+ <CS:share xmlns:D="DAV:" xmlns:CS="http://calendarserver.org/ns/">
+ <CS:set>
+ <D:href>mailto:user02@example.com</D:href>
+ <CS:summary>My Shared Calendar</CS:summary>
+ <CS:read-write/>
+ </CS:set>
+ </CS:share>
+ """)
+
+ propInvite = (yield self.resource.readProperty(customxml.Invite, None))
+ uid = self._getUIDElementValue(propInvite)
+ self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
+ customxml.InviteUser(
+ customxml.UID.fromString(""),
+ davxml.HRef.fromString("urn:uuid:user02"),
+ customxml.CommonName.fromString("USER02"),
+ customxml.InviteAccess(customxml.ReadWriteAccess()),
+ customxml.InviteStatusNoResponse(),
+ ),
+ ))
+
+ result = (yield self._doPOSTSharerAccept("""<?xml version='1.0' encoding='UTF-8'?>
+ <invite-reply xmlns='http://calendarserver.org/ns/'>
+ <href xmlns='DAV:'>mailto:user01@example.com</href>
+ <invite-accepted/>
+ <hosturl>
+ <href xmlns='DAV:'>/calendars/__uids__/user01/calendar/</href>
+ </hosturl>
+ <in-reply-to>%s</in-reply-to>
+ <summary>The Shared Calendar</summary>
+ <common-name>USER02</common-name>
+ <first-name>user</first-name>
+ <last-name>02</last-name>
+ </invite-reply>
+ """ % (uid,))
+ )
+ href = self._getHRefElementValue(result) + "/"
+
+ self.patch(FakePrincipal, "invalid_names", set(("user01",)))
+
+ resource = (yield self._getResourceSharer(href))
+ propInvite = yield resource.inviteProperty(FakeRequest())
+ self.assertEquals(propInvite, None)
+
+
+ @inlineCallbacks
+ def test_hideInvalidSharers(self):
+
+ yield self.resource.upgradeToShare()
+
+ yield self._doPOST("""<?xml version="1.0" encoding="utf-8" ?>
+ <CS:share xmlns:D="DAV:" xmlns:CS="http://calendarserver.org/ns/">
+ <CS:set>
+ <D:href>mailto:user02@example.com</D:href>
+ <CS:summary>My Shared Calendar</CS:summary>
+ <CS:read-write/>
+ </CS:set>
+ <CS:set>
+ <D:href>mailto:user03@example.com</D:href>
+ <CS:summary>My Shared Calendar</CS:summary>
+ <CS:read-write/>
+ </CS:set>
+ </CS:share>
+ """)
+
+ propInvite = (yield self.resource.readProperty(customxml.Invite, None))
+ uids = self._getUIDElementValues(propInvite)
+ self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
+ customxml.InviteUser(
+ customxml.UID.fromString(""),
+ davxml.HRef.fromString("urn:uuid:user02"),
+ customxml.CommonName.fromString("USER02"),
+ customxml.InviteAccess(customxml.ReadWriteAccess()),
+ customxml.InviteStatusNoResponse(),
+ ),
+ customxml.InviteUser(
+ customxml.UID.fromString(""),
+ davxml.HRef.fromString("urn:uuid:user03"),
+ customxml.CommonName.fromString("USER03"),
+ customxml.InviteAccess(customxml.ReadWriteAccess()),
+ customxml.InviteStatusNoResponse(),
+ ),
+ ))
+
+ yield self._doPOSTSharerAccept("""<?xml version='1.0' encoding='UTF-8'?>
+ <invite-reply xmlns='http://calendarserver.org/ns/'>
+ <href xmlns='DAV:'>mailto:user01@example.com</href>
+ <invite-accepted/>
+ <hosturl>
+ <href xmlns='DAV:'>/calendars/__uids__/user01/calendar/</href>
+ </hosturl>
+ <in-reply-to>%s</in-reply-to>
+ <summary>The Shared Calendar</summary>
+ <common-name>USER02</common-name>
+ <first-name>user</first-name>
+ <last-name>02</last-name>
+ </invite-reply>
+ """ % (uids["urn:uuid:user02"],))
+
+ yield self._doPOSTSharerAccept(
+ """<?xml version='1.0' encoding='UTF-8'?>
+ <invite-reply xmlns='http://calendarserver.org/ns/'>
+ <href xmlns='DAV:'>mailto:user01@example.com</href>
+ <invite-accepted/>
+ <hosturl>
+ <href xmlns='DAV:'>/calendars/__uids__/user01/calendar/</href>
+ </hosturl>
+ <in-reply-to>%s</in-reply-to>
+ <summary>The Shared Calendar</summary>
+ <common-name>USER03</common-name>
+ <first-name>user</first-name>
+ <last-name>03</last-name>
+ </invite-reply>
+ """ % (uids["urn:uuid:user03"],),
+ sharer="user03"
+ )
+
+ self.patch(FakePrincipal, "invalid_names", set(("user02",)))
+
+ resource = yield self._getResource()
+ propInvite = yield resource.inviteProperty(None)
+ self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
+ customxml.InviteUser(
+ customxml.UID.fromString(""),
+ davxml.HRef.fromString("urn:uuid:user02"),
+ customxml.CommonName.fromString("USER02"),
+ customxml.InviteAccess(customxml.ReadWriteAccess()),
+ customxml.InviteStatusInvalid(),
+ ),
+ customxml.InviteUser(
+ customxml.UID.fromString(""),
+ davxml.HRef.fromString("urn:uuid:user03"),
+ customxml.CommonName.fromString("USER03"),
+ customxml.InviteAccess(customxml.ReadWriteAccess()),
+ customxml.InviteStatusAccepted(),
+ ),
+ ))
</ins></span></pre></div>
<a id="CalendarServerbranchesreleaseCalendarServer53devtxdavcommondatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/release/CalendarServer-5.3-dev/txdav/common/datastore/sql.py (13650 => 13651)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/release/CalendarServer-5.3-dev/txdav/common/datastore/sql.py        2014-06-17 21:55:29 UTC (rev 13650)
+++ CalendarServer/branches/release/CalendarServer-5.3-dev/txdav/common/datastore/sql.py        2014-06-18 18:06:15 UTC (rev 13651)
</span><span class="lines">@@ -2740,6 +2740,27 @@
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def _replace(self, status):
+ """
+ Create a copy of this invitation with the status changed to the specified value.
+
+ @param status: the new status value
+ @type status: L{int}
+ """
+ return SharingInvitation(
+ self._uid,
+ self._owner_uid,
+ self._owner_rid,
+ self._sharee_uid,
+ self._sharee_rid,
+ self._resource_id,
+ self._resource_name,
+ self._mode,
+ status,
+ self._summary,
+ )
+
+
</ins><span class="cx"> def uid(self):
</span><span class="cx"> """
</span><span class="cx"> This maps to the resource name in the bind table, the "bind name". This is used as the "uid"
</span></span></pre>
</div>
</div>
</body>
</html>