def handleMissingTrailingSlash(self, request): try: _ignore_authnUser, authzUser = yield self.authenticate(request) except Exception: authzUser = None # Turn 301 into 401 if authzUser is None: response = (yield UnauthorizedResponse.makeResponse( request.credentialFactories, request.remoteAddr)) returnValue(response) else: response = RedirectResponse( request.unparseURL(path=urllib.quote( urllib.unquote(request.path), safe=':/') + '/')) returnValue(response)
def render(self, request): if config.EnableMonolithicCalendars: # # Send listing instead of iCalendar data to HTML agents # This is mostly useful for debugging... # # FIXME: Add a self-link to the dirlist with a query string so # users can still download the actual iCalendar data? # # FIXME: Are there better ways to detect this than hacking in # user agents? # # FIXME: In the meantime, make this a configurable regex list? # agent = request.headers.getHeader("user-agent") if agent is not None and (agent.startswith("Mozilla/") and agent.find("Gecko") != -1): renderAsHTML = True else: renderAsHTML = False else: renderAsHTML = True if not renderAsHTML: # Render a monolithic iCalendar file if request.path[-1] != "/": # Redirect to include trailing '/' in URI return RedirectResponse( request.unparseURL(path=urllib.quote( urllib.unquote(request.path), safe=':/') + '/')) def _defer(data): response = Response() response.stream = MemoryStream(str(data)) response.headers.setHeader( "content-type", MimeType.fromString("text/calendar")) return response d = self.iCalendarRolledup(request) d.addCallback(_defer) return d return super(CalDAVResource, self).render(request)
def locateChild(self, request, segments): for filter in self.contentFilters: request.addResponseFilter(filter[0], atEnd=filter[1]) # Examine cookies for wiki auth token; if there, ask the paired wiki # server for the corresponding record name. If that maps to a # principal, assign that to authnuser. # Also, certain non-browser clients send along the wiki auth token # sometimes, so we now also look for the presence of x-requested-with # header that the webclient sends. However, in the case of a GET on # /webcal that header won't be sent so therefore we allow wiki auth # for any path in the authServiceMap even if that header is missing. allowWikiAuth = False topLevel = request.path.strip("/").split("/")[0] if self.authServiceMap.get(topLevel, False): allowWikiAuth = True if not hasattr(request, "checkedWiki"): # Only do this once per request request.checkedWiki = True wikiConfig = config.Authentication.Wiki cookies = request.headers.getHeader("cookie") requestedWith = request.headers.hasHeader("x-requested-with") if ( wikiConfig["Enabled"] and (requestedWith or allowWikiAuth) and cookies is not None ): for cookie in cookies: if cookie.name == wikiConfig["Cookie"]: token = cookie.value break else: token = None if token is not None and token != "unauthenticated": log.debug( "Wiki sessionID cookie value: {token}", token=token ) try: uid = yield uidForAuthToken(token, wikiConfig["EndpointDescriptor"]) if uid == "unauthenticated": uid = None except WebError as w: uid = None # FORBIDDEN status means it's an unknown token if int(w.status) == responsecode.NOT_FOUND: log.debug( "Unknown wiki token: {token}", token=token ) else: log.error( "Failed to look up wiki token {token}: {msg}", token=token, msg=w.message ) except Exception as e: log.error( "Failed to look up wiki token: {error}", error=e ) uid = None if uid is not None: log.debug( "Wiki lookup returned uid: {uid}", uid=uid ) principal = yield self.principalForUID(request, uid) if principal: log.debug( "Wiki-authenticated principal {uid} " "being assigned to authnUser and authzUser", uid=uid ) request.authzUser = request.authnUser = principal if not hasattr(request, "authzUser") and config.WebCalendarAuthPath: topLevel = request.path.strip("/").split("/")[0] if self.authServiceMap.get(topLevel, False): # We've not been authenticated and the auth service is enabled # for this resource, so redirect. # Use config.ServerHostName if no x-forwarded-host header, # otherwise use the final hostname in x-forwarded-host. host = request.headers.getRawHeaders( "x-forwarded-host", [config.ServerHostName] )[-1].split(",")[-1].strip() port = 443 if (config.EnableSSL or config.BehindTLSProxy) else 80 scheme = "https" if config.EnableSSL else "http" response = RedirectResponse( request.unparseURL( host=host, port=port, scheme=scheme, path=config.WebCalendarAuthPath, querystring="redirect={}://{}{}".format( scheme, host, request.path ) ), temporary=True ) raise HTTPError(response) # We don't want the /inbox resource to pay attention to SACLs because # we just want it to use the hard-coded ACL for the imip reply user. # The /timezones resource is used by the wiki web calendar, so open # up that resource. if segments[0] in ("inbox", "timezones"): request.checkedSACL = True elif ( ( len(segments) > 2 and segments[0] in ("calendars", "principals") and ( segments[1] == "wikis" or ( segments[1] == "__uids__" and segments[2].startswith(WikiDirectoryService.uidPrefix) ) ) ) ): # This is a wiki-related calendar resource. SACLs are not checked. request.checkedSACL = True # The authzuser value is set to that of the wiki principal if # not already set. if not hasattr(request, "authzUser") and segments[2]: wikiUid = None if segments[1] == "wikis": wikiUid = "{}{}".format(WikiDirectoryService.uidPrefix, segments[2]) else: wikiUid = segments[2] if wikiUid: log.debug( "Wiki principal {name} being assigned to authzUser", name=wikiUid ) request.authzUser = yield self.principalForUID(request, wikiUid) elif ( self.useSacls and not hasattr(request, "checkedSACL") ): yield self.checkSACL(request) if config.RejectClients: # # Filter out unsupported clients # agent = request.headers.getHeader("user-agent") if agent is not None: for reject in config.RejectClients: if reject.search(agent) is not None: log.info("Rejecting user-agent: {agent}", agent=agent) raise HTTPError(StatusResponse( responsecode.FORBIDDEN, "Your client software ({}) is not allowed to " "access this service." .format(agent) )) if not hasattr(request, "authnUser"): try: authnUser, authzUser = yield self.authenticate(request) request.authnUser = authnUser request.authzUser = authzUser except (UnauthorizedLogin, LoginFailed): response = yield UnauthorizedResponse.makeResponse( request.credentialFactories, request.remoteAddr ) raise HTTPError(response) if ( config.EnableResponseCache and request.method == "PROPFIND" and not getattr(request, "notInCache", False) and len(segments) > 1 ): try: if not getattr(request, "checkingCache", False): request.checkingCache = True response = yield self.responseCache.getResponseForRequest( request ) if response is None: request.notInCache = True raise KeyError("Not found in cache.") returnValue((_CachedResponseResource(response), [])) except KeyError: pass child = yield super(RootResource, self).locateChild( request, segments ) returnValue(child)