Revision: 2095 http://trac.macosforge.org/projects/calendarserver/changeset/2095 Author: cdaboo@apple.com Date: 2008-01-08 13:30:35 -0800 (Tue, 08 Jan 2008) Log Message: ----------- Filtering of data on REPORT for X-CALENDARSERVER-ACCESS. Plus a little refactoring of the GET code to pull out common pieces. Modified Paths: -------------- CalendarServer/branches/users/cdaboo/private_events-2081/twistedcaldav/caldavxml.py CalendarServer/branches/users/cdaboo/private_events-2081/twistedcaldav/method/get.py CalendarServer/branches/users/cdaboo/private_events-2081/twistedcaldav/method/report_calquery.py CalendarServer/branches/users/cdaboo/private_events-2081/twistedcaldav/method/report_common.py CalendarServer/branches/users/cdaboo/private_events-2081/twistedcaldav/method/report_multiget.py CalendarServer/branches/users/cdaboo/private_events-2081/twistedcaldav/resource.py Modified: CalendarServer/branches/users/cdaboo/private_events-2081/twistedcaldav/caldavxml.py =================================================================== --- CalendarServer/branches/users/cdaboo/private_events-2081/twistedcaldav/caldavxml.py 2008-01-08 21:29:20 UTC (rev 2094) +++ CalendarServer/branches/users/cdaboo/private_events-2081/twistedcaldav/caldavxml.py 2008-01-08 21:30:35 UTC (rev 2095) @@ -475,12 +475,7 @@ @param resource: the resource whose calendar data is to be returned. @return: an L{CalendarData} with the (filtered) calendar data. """ - # Check for filtering or not - if self.children: - filtered = self.getFromICalendar(resource.iCalendar()) - return CalendarData.fromCalendar(filtered) - else: - return resource.iCalendarXML() + return self.elementFromCalendar(resource.iCalendar()) def elementFromCalendar(self, calendar): """ @@ -494,6 +489,138 @@ filtered = self.getFromICalendar(calendar) return CalendarData.fromCalendar(filtered) + def elementFromResourceWithAccessRestrictions(self, resource, access): + """ + Return a new CalendarData element comprised of the possibly filtered + calendar data from the specified resource. If no filter is being applied + read the data directly from the resource without parsing it. If a filter + is required, parse the iCal data and filter using this CalendarData. + + Also, apply appropriate access restriction filtering to the data. + + @param resource: the resource whose calendar data is to be returned. + @param access: private event access restriction level. + @return: an L{CalendarData} with the (filtered) calendar data. + """ + return self.elementFromCalendarWithAccessRestrictions(resource.iCalendar(), access) + + def elementFromCalendarWithAccessRestrictions(self, calendar, access): + """ + Return a new CalendarData element comprised of the possibly filtered + calendar. + + Also, apply appropriate access restriction filtering to the data. + + @param calendar: the calendar that is to be filtered and returned. + @param access: private event access restriction level. + @return: an L{CalendarData} with the (filtered) calendar data. + """ + + # Do normal filtering first + filtered_calendar = self.getFromICalendar(calendar) + + if access in (iComponent.ACCESS_CONFIDENTIAL, iComponent.ACCESS_RESTRICTED): + # Create a CALDAV:calendar-data element with the appropriate iCalendar Component/Property + # filter in place for the access restriction in use + + extra_access = () + if access == iComponent.ACCESS_RESTRICTED: + extra_access = ( + Property(name="SUMMARY"), + Property(name="LOCATION"), + ) + + filter = CalendarData( + CalendarComponent( + + # VCALENDAR proeprties + Property(name="PRODID"), + Property(name="VERSION"), + Property(name="CALSCALE"), + Property(name=iComponent.ACCESS_PROPERTY), + + # VEVENT + CalendarComponent( + Property(name="UID"), + Property(name="RECURRENCE-ID"), + Property(name="SEQUENCE"), + Property(name="DTSTAMP"), + Property(name="STATUS"), + Property(name="TRANSP"), + Property(name="DTSTART"), + Property(name="DTEND"), + Property(name="DURATION"), + Property(name="RRULE"), + Property(name="RDATE"), + Property(name="EXRULE"), + Property(name="EXDATE"), + *extra_access, + **{"name":"VEVENT"} + ), + + # VTODO + CalendarComponent( + Property(name="UID"), + Property(name="RECURRENCE-ID"), + Property(name="SEQUENCE"), + Property(name="DTSTAMP"), + Property(name="STATUS"), + Property(name="DTSTART"), + Property(name="COMPLETED"), + Property(name="DUE"), + Property(name="DURATION"), + Property(name="RRULE"), + Property(name="RDATE"), + Property(name="EXRULE"), + Property(name="EXDATE"), + *extra_access, + **{"name":"VTODO"} + ), + + # VJOURNAL + CalendarComponent( + Property(name="UID"), + Property(name="RECURRENCE-ID"), + Property(name="SEQUENCE"), + Property(name="DTSTAMP"), + Property(name="STATUS"), + Property(name="TRANSP"), + Property(name="DTSTART"), + Property(name="RRULE"), + Property(name="RDATE"), + Property(name="EXRULE"), + Property(name="EXDATE"), + *extra_access, + **{"name":"VJOURNAL"} + ), + + # VFREEBUSY + CalendarComponent( + Property(name="UID"), + Property(name="DTSTAMP"), + Property(name="DTSTART"), + Property(name="DTEND"), + Property(name="DURATION"), + Property(name="FREEBUSY"), + *extra_access, + **{"name":"VFREEBUSY"} + ), + + # VTIMEZONE + CalendarComponent( + AllProperties(), + AllComponents(), + name="VTIMEZONE", + ), + name="VCALENDAR", + ), + ) + + # Now "filter" the resource calendar data through the CALDAV:calendar-data element + return filter.elementFromCalendar(filtered_calendar) + else: + return CalendarData.fromCalendar(filtered_calendar) + def getFromICalendar(self, calendar): """ Returns a calendar object containing the data in the given calendar Modified: CalendarServer/branches/users/cdaboo/private_events-2081/twistedcaldav/method/get.py =================================================================== --- CalendarServer/branches/users/cdaboo/private_events-2081/twistedcaldav/method/get.py 2008-01-08 21:29:20 UTC (rev 2094) +++ CalendarServer/branches/users/cdaboo/private_events-2081/twistedcaldav/method/get.py 2008-01-08 21:30:35 UTC (rev 2095) @@ -48,111 +48,16 @@ d.getResult() # Non DAV:owner's have limited access to the data - d = waitForDeferred(self.owner(request)) + d = waitForDeferred(self.isOwner(request)) yield d - owner = d.getResult() + isowner = d.getResult() - authz = self.currentPrincipal(request) - if davxml.Principal(owner) != authz: + if not isowner: - # Create a CALDAV:calendar-data element with the appropriate iCalendar Component/Property - # filter in place for the access restriction in use - - extra_access = () - if access == Component.ACCESS_RESTRICTED: - extra_access = ( - caldavxml.Property(name="SUMMARY"), - caldavxml.Property(name="LOCATION"), - ) + # Now "filter" the resource calendar data through the CALDAV:calendar-data element and apply + # access restrictions to the data. + caldata = caldavxml.CalendarData().elementFromResourceWithAccessRestrictions(self, access).calendarData() - filter = caldavxml.CalendarData( - caldavxml.CalendarComponent( - - # VCALENDAR proeprties - caldavxml.Property(name="PRODID"), - caldavxml.Property(name="VERSION"), - caldavxml.Property(name="CALSCALE"), - caldavxml.Property(name=Component.ACCESS_PROPERTY), - - # VEVENT - caldavxml.CalendarComponent( - caldavxml.Property(name="UID"), - caldavxml.Property(name="RECURRENCE-ID"), - caldavxml.Property(name="SEQUENCE"), - caldavxml.Property(name="DTSTAMP"), - caldavxml.Property(name="STATUS"), - caldavxml.Property(name="TRANSP"), - caldavxml.Property(name="DTSTART"), - caldavxml.Property(name="DTEND"), - caldavxml.Property(name="DURATION"), - caldavxml.Property(name="RRULE"), - caldavxml.Property(name="RDATE"), - caldavxml.Property(name="EXRULE"), - caldavxml.Property(name="EXDATE"), - *extra_access, - **{"name":"VEVENT"} - ), - - # VTODO - caldavxml.CalendarComponent( - caldavxml.Property(name="UID"), - caldavxml.Property(name="RECURRENCE-ID"), - caldavxml.Property(name="SEQUENCE"), - caldavxml.Property(name="DTSTAMP"), - caldavxml.Property(name="STATUS"), - caldavxml.Property(name="DTSTART"), - caldavxml.Property(name="COMPLETED"), - caldavxml.Property(name="DUE"), - caldavxml.Property(name="DURATION"), - caldavxml.Property(name="RRULE"), - caldavxml.Property(name="RDATE"), - caldavxml.Property(name="EXRULE"), - caldavxml.Property(name="EXDATE"), - *extra_access, - **{"name":"VTODO"} - ), - - # VJOURNAL - caldavxml.CalendarComponent( - caldavxml.Property(name="UID"), - caldavxml.Property(name="RECURRENCE-ID"), - caldavxml.Property(name="SEQUENCE"), - caldavxml.Property(name="DTSTAMP"), - caldavxml.Property(name="STATUS"), - caldavxml.Property(name="TRANSP"), - caldavxml.Property(name="DTSTART"), - caldavxml.Property(name="RRULE"), - caldavxml.Property(name="RDATE"), - caldavxml.Property(name="EXRULE"), - caldavxml.Property(name="EXDATE"), - *extra_access, - **{"name":"VJOURNAL"} - ), - - # VFREEBUSY - caldavxml.CalendarComponent( - caldavxml.Property(name="UID"), - caldavxml.Property(name="DTSTAMP"), - caldavxml.Property(name="DTSTART"), - caldavxml.Property(name="DTEND"), - caldavxml.Property(name="DURATION"), - caldavxml.Property(name="FREEBUSY"), - *extra_access, - **{"name":"VFREEBUSY"} - ), - - # VTIMEZONE - caldavxml.CalendarComponent( - caldavxml.AllProperties(), - caldavxml.AllComponents(), - name="VTIMEZONE", - ), - name="VCALENDAR", - ), - ) - - # Now "filter" the resource calendar data through the CALDAV:calendar-data element - caldata = filter.elementFromResource(self).calendarData() response = Response() response.stream = MemoryStream(caldata) response.headers.setHeader("content-type", MimeType.fromString("text/calendar; charset=utf-8")) Modified: CalendarServer/branches/users/cdaboo/private_events-2081/twistedcaldav/method/report_calquery.py =================================================================== --- CalendarServer/branches/users/cdaboo/private_events-2081/twistedcaldav/method/report_calquery.py 2008-01-08 21:29:20 UTC (rev 2094) +++ CalendarServer/branches/users/cdaboo/private_events-2081/twistedcaldav/method/report_calquery.py 2008-01-08 21:30:35 UTC (rev 2095) @@ -104,7 +104,7 @@ @param uri: the uri for the calendar collecton resource. """ - def queryCalendarObjectResource(resource, uri, name, calendar, query_ok = False): + def queryCalendarObjectResource(resource, uri, name, calendar, query_ok=False, isowner=True): """ Run a query on the specified calendar. @param resource: the L{CalDAVFile} for the calendar. @@ -124,7 +124,7 @@ else: href = davxml.HRef.fromString(uri) - return report_common.responseForHref(request, responses, href, resource, calendar, propertiesForResource, query) + return report_common.responseForHref(request, responses, href, resource, calendar, propertiesForResource, query, isowner) else: return succeed(None) @@ -146,7 +146,12 @@ filteredaces = waitForDeferred(calresource.inheritedACEsforChildren(request)) yield filteredaces filteredaces = filteredaces.getResult() - + + # Check private events access status + d = waitForDeferred(calresource.isOwner(request)) + yield d + isowner = d.getResult() + # Check for disabled access if filteredaces is not None: # See whether the filter is valid for an index only query @@ -183,7 +188,7 @@ else: calendar = None - d = waitForDeferred(queryCalendarObjectResource(child, uri, child_uri_name, calendar, query_ok = index_query_ok)) + d = waitForDeferred(queryCalendarObjectResource(child, uri, child_uri_name, calendar, query_ok = index_query_ok, isowner=isowner)) yield d d.getResult() else: @@ -205,6 +210,11 @@ tz = tz.getResult() filter.settimezone(tz) + # Check private events access status + d = waitForDeferred(calresource.isOwner(request)) + yield d + isowner = d.getResult() + calendar = calresource.iCalendar() d = waitForDeferred(queryCalendarObjectResource(calresource, uri, None, calendar)) yield d Modified: CalendarServer/branches/users/cdaboo/private_events-2081/twistedcaldav/method/report_common.py =================================================================== --- CalendarServer/branches/users/cdaboo/private_events-2081/twistedcaldav/method/report_common.py 2008-01-08 21:29:20 UTC (rev 2094) +++ CalendarServer/branches/users/cdaboo/private_events-2081/twistedcaldav/method/report_common.py 2008-01-08 21:30:35 UTC (rev 2095) @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. ## +from twistedcaldav.customxml import TwistedCalendarAccessProperty __all__ = [ "applyToCalendarCollections", @@ -93,7 +94,7 @@ applyToCalendarCollections = deferredGenerator(applyToCalendarCollections) -def responseForHref(request, responses, href, resource, calendar, propertiesForResource, propertyreq): +def responseForHref(request, responses, href, resource, calendar, propertiesForResource, propertyreq, isowner=True): """ Create an appropriate property status response for the given resource. @@ -102,10 +103,12 @@ @param href: the L{HRef} element of the resource being targetted. @param resource: the L{CalDAVFile} for the targetted resource. @param calendar: the L{Component} for the calendar for the resource. This may be None - if the calendar has not already been read in, in which case the resource - will be used to get the calendar if needed. + if the calendar has not already been read in, in which case the resource + will be used to get the calendar if needed. @param propertiesForResource: the method to use to get the list of properties to return. @param propertyreq: the L{PropertyContainer} element for the properties of interest. + @param isowner: C{True} if the authorized principal making the request is the DAV:owner, + C{False} otherwise. """ def _defer(properties_by_status): @@ -122,38 +125,42 @@ ) ) - d = propertiesForResource(request, propertyreq, resource, calendar) + d = propertiesForResource(request, propertyreq, resource, calendar, isowner) d.addCallback(_defer) return d -def allPropertiesForResource(request, prop, resource, calendar=None): #@UnusedVariable +def allPropertiesForResource(request, prop, resource, calendar=None, isowner=True): #@UnusedVariable """ Return all (non-hidden) properties for the specified resource. @param request: the L{IRequest} for the current request. @param prop: the L{PropertyContainer} element for the properties of interest. @param resource: the L{CalDAVFile} for the targetted resource. @param calendar: the L{Component} for the calendar for the resource. This may be None - if the calendar has not already been read in, in which case the resource - will be used to get the calendar if needed. + if the calendar has not already been read in, in which case the resource + will be used to get the calendar if needed. + @param isowner: C{True} if the authorized principal making the request is the DAV:owner, + C{False} otherwise. @return: a map of OK and NOT FOUND property values. """ def _defer(props): - return _namedPropertiesForResource(request, props, resource, calendar) + return _namedPropertiesForResource(request, props, resource, calendar, isowner) d = resource.listAllprop(request) d.addCallback(_defer) return d -def propertyNamesForResource(request, prop, resource, calendar=None): #@UnusedVariable +def propertyNamesForResource(request, prop, resource, calendar=None, isowner=True): #@UnusedVariable """ Return property names for all properties on the specified resource. @param request: the L{IRequest} for the current request. @param prop: the L{PropertyContainer} element for the properties of interest. @param resource: the L{CalDAVFile} for the targetted resource. @param calendar: the L{Component} for the calendar for the resource. This may be None - if the calendar has not already been read in, in which case the resource - will be used to get the calendar if needed. + if the calendar has not already been read in, in which case the resource + will be used to get the calendar if needed. + @param isowner: C{True} if the authorized principal making the request is the DAV:owner, + C{False} otherwise. @return: a map of OK and NOT FOUND property values. """ @@ -167,19 +174,21 @@ d.addCallback(_defer) return d -def propertyListForResource(request, prop, resource, calendar=None): +def propertyListForResource(request, prop, resource, calendar=None, isowner=True): """ Return the specified properties on the specified resource. @param request: the L{IRequest} for the current request. @param prop: the L{PropertyContainer} element for the properties of interest. @param resource: the L{CalDAVFile} for the targetted resource. @param calendar: the L{Component} for the calendar for the resource. This may be None - if the calendar has not already been read in, in which case the resource - will be used to get the calendar if needed. + if the calendar has not already been read in, in which case the resource + will be used to get the calendar if needed. + @param isowner: C{True} if the authorized principal making the request is the DAV:owner, + C{False} otherwise. @return: a map of OK and NOT FOUND property values. """ - return _namedPropertiesForResource(request, prop.children, resource, calendar) + return _namedPropertiesForResource(request, prop.children, resource, calendar, isowner) def validPropertyListCalendarDataTypeVersion(prop): """ @@ -204,15 +213,17 @@ return result, message, generate_calendar_data -def _namedPropertiesForResource(request, props, resource, calendar=None): +def _namedPropertiesForResource(request, props, resource, calendar=None, isowner=True): """ Return the specified properties on the specified resource. @param request: the L{IRequest} for the current request. @param props: a list of property elements or qname tuples for the properties of interest. @param resource: the L{CalDAVFile} for the targetted resource. @param calendar: the L{Component} for the calendar for the resource. This may be None - if the calendar has not already been read in, in which case the resource - will be used to get the calendar if needed. + if the calendar has not already been read in, in which case the resource + will be used to get the calendar if needed. + @param isowner: C{True} if the authorized principal making the request is the DAV:owner, + C{False} otherwise. @return: a map of OK and NOT FOUND property values. """ properties_by_status = { @@ -222,10 +233,19 @@ for property in props: if isinstance(property, caldavxml.CalendarData): + # Handle private events access restrictions + if not isowner: + try: + access = resource.readDeadProperty(TwistedCalendarAccessProperty) + except HTTPError: + access = None + else: + access = None + if calendar: - propvalue = property.elementFromCalendar(calendar) + propvalue = property.elementFromCalendarWithAccessRestrictions(calendar, access) else: - propvalue = property.elementFromResource(resource) + propvalue = property.elementFromResourceWithAccessRestrictions(resource, access) if propvalue is None: raise ValueError("Invalid CalDAV:calendar-data for request: %r" % (property,)) properties_by_status[responsecode.OK].append(propvalue) Modified: CalendarServer/branches/users/cdaboo/private_events-2081/twistedcaldav/method/report_multiget.py =================================================================== --- CalendarServer/branches/users/cdaboo/private_events-2081/twistedcaldav/method/report_multiget.py 2008-01-08 21:29:20 UTC (rev 2094) +++ CalendarServer/branches/users/cdaboo/private_events-2081/twistedcaldav/method/report_multiget.py 2008-01-08 21:30:35 UTC (rev 2095) @@ -63,17 +63,15 @@ if propertyreq.qname() == ("DAV:", "allprop"): propertiesForResource = report_common.allPropertiesForResource - generate_calendar_data = False elif propertyreq.qname() == ("DAV:", "propname"): propertiesForResource = report_common.propertyNamesForResource - generate_calendar_data = False elif propertyreq.qname() == ("DAV:", "prop"): propertiesForResource = report_common.propertyListForResource # Verify that any calendar-data element matches what we can handle - result, message, generate_calendar_data = report_common.validPropertyListCalendarDataTypeVersion(propertyreq) + result, message, _ignore = report_common.validPropertyListCalendarDataTypeVersion(propertyreq) if not result: log.err(message) raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "supported-calendar-data"))) @@ -113,14 +111,21 @@ # Check for disabled access if filteredaces is None: disabled = True + + # Check private events access status + d = waitForDeferred(self.isOwner(request)) + yield d + isowner = d.getResult() elif self.isCollection(): requestURIis = "collection" filteredaces = None lastParent = None + isowner = None else: requestURIis = "resource" filteredaces = None + isowner = None if not disabled: @@ -169,7 +174,7 @@ # Get properties for all valid readable resources for resource, href in ok_resources: - d = waitForDeferred(report_common.responseForHref(request, responses, davxml.HRef.fromString(href), resource, None, propertiesForResource, propertyreq)) + d = waitForDeferred(report_common.responseForHref(request, responses, davxml.HRef.fromString(href), resource, None, propertiesForResource, propertyreq, isowner=isowner)) yield d d.getResult() @@ -233,7 +238,11 @@ filteredaces = waitForDeferred(parent.inheritedACEsforChildren(request)) yield filteredaces filteredaces = filteredaces.getResult() - + + # Check private events access status + d = waitForDeferred(parent.isOwner(request)) + yield d + isowner = d.getResult() else: name = unquote(resource_uri[resource_uri.rfind("/") + 1:]) if (resource_uri != request.uri) or not self.exists(): @@ -254,6 +263,11 @@ filteredaces = waitForDeferred(parent.inheritedACEsforChildren(request)) yield filteredaces filteredaces = filteredaces.getResult() + + # Check private events access status + d = waitForDeferred(parent.isOwner(request)) + yield d + isowner = d.getResult() # Check privileges - must have at least DAV:read try: @@ -264,7 +278,7 @@ responses.append(davxml.StatusResponse(href, davxml.Status.fromResponseCode(responsecode.FORBIDDEN))) continue - d = waitForDeferred(report_common.responseForHref(request, responses, href, child, None, propertiesForResource, propertyreq)) + d = waitForDeferred(report_common.responseForHref(request, responses, href, child, None, propertiesForResource, propertyreq, isowner=isowner)) yield d d.getResult() Modified: CalendarServer/branches/users/cdaboo/private_events-2081/twistedcaldav/resource.py =================================================================== --- CalendarServer/branches/users/cdaboo/private_events-2081/twistedcaldav/resource.py 2008-01-08 21:29:20 UTC (rev 2094) +++ CalendarServer/branches/users/cdaboo/private_events-2081/twistedcaldav/resource.py 2008-01-08 21:30:35 UTC (rev 2095) @@ -304,6 +304,19 @@ else: yield None + @deferredGenerator + def isOwner(self, request): + """ + Determine whether the DAV:owner of this resource matches the currently authorized principal + in the request. + """ + + d = waitForDeferred(self.owner(request)) + yield d + owner = d.getResult() + result = (davxml.Principal(owner) == self.currentPrincipal(request)) + yield result + ## # CalDAV ##
participants (1)
-
source_changes@macosforge.org