示例#1
0
    def checkSACL(self, request):
        """
        Check SACLs against the current request
        """

        topLevel = request.path.strip("/").split("/")[0]
        saclServices = self.saclMap.get(topLevel, None)
        if not saclServices:
            returnValue(True)

        try:
            authnUser, authzUser = yield self.authenticate(request)
        except Exception:
            response = (yield UnauthorizedResponse.makeResponse(
                request.credentialFactories, request.remoteAddr))
            raise HTTPError(response)

        # SACLs are enabled in the plist, but there may not actually
        # be a SACL group assigned to this service.  Let's see if
        # unauthenticated users are allowed by calling CheckSACL
        # with an empty string.
        if authzUser is None:
            for saclService in saclServices:
                if checkSACL("", saclService):
                    # No group actually exists for this SACL, so allow
                    # unauthenticated access
                    returnValue(True)
            # There is a SACL group for at least one of the SACLs, so no
            # unauthenticated access
            response = (yield UnauthorizedResponse.makeResponse(
                request.credentialFactories, request.remoteAddr))
            log.info("Unauthenticated user denied by SACLs")
            raise HTTPError(response)

        # Cache the authentication details
        request.authnUser = authnUser
        request.authzUser = authzUser

        # Figure out the "username" from the davxml.Principal object
        username = authzUser.record.shortNames[0]

        access = False
        for saclService in saclServices:
            if checkSACL(username, saclService):
                # Access is allowed
                access = True
                break

        # Mark SACLs as having been checked so we can avoid doing it
        # multiple times
        request.checkedSACL = True

        if access:
            returnValue(True)

        log.warn("User {user!r} is not enabled with the {sacl!r} SACL(s)",
                 user=username,
                 sacl=saclServices)
        raise HTTPError(responsecode.FORBIDDEN)
示例#2
0
    def checkSACL(self, request):
        """
        Check SACLs against the current request
        """

        topLevel = request.path.strip("/").split("/")[0]
        saclServices = self.saclMap.get(topLevel, None)
        if not saclServices:
            returnValue(True)

        try:
            authnUser, authzUser = yield self.authenticate(request)
        except Exception:
            response = (yield UnauthorizedResponse.makeResponse(request.credentialFactories, request.remoteAddr))
            raise HTTPError(response)

        # SACLs are enabled in the plist, but there may not actually
        # be a SACL group assigned to this service.  Let's see if
        # unauthenticated users are allowed by calling CheckSACL
        # with an empty string.
        if authzUser is None:
            for saclService in saclServices:
                if checkSACL("", saclService):
                    # No group actually exists for this SACL, so allow
                    # unauthenticated access
                    returnValue(True)
            # There is a SACL group for at least one of the SACLs, so no
            # unauthenticated access
            response = (yield UnauthorizedResponse.makeResponse(request.credentialFactories, request.remoteAddr))
            log.info("Unauthenticated user denied by SACLs")
            raise HTTPError(response)

        # Cache the authentication details
        request.authnUser = authnUser
        request.authzUser = authzUser

        # Figure out the "username" from the davxml.Principal object
        username = authzUser.record.shortNames[0]

        access = False
        for saclService in saclServices:
            if checkSACL(username, saclService):
                # Access is allowed
                access = True
                break

        # Mark SACLs as having been checked so we can avoid doing it
        # multiple times
        request.checkedSACL = True

        if access:
            returnValue(True)

        log.warn("User {user!r} is not enabled with the {sacl!r} SACL(s)", user=username, sacl=saclServices)
        raise HTTPError(responsecode.FORBIDDEN)
示例#3
0
    def test_invalidNonce(self):
        """
        Test that login fails when the given nonce from the response, does not
        match the nonce encoded in the opaque.
        """

        credentialFactories = (FakeDigestCredentialFactory(
            'md5', 'auth', 'test realm', self.namespace1),
                               FakeDigestCredentialFactory(
                                   'md5', '', 'test realm', self.namespace2))

        for ctr, factory in enumerate(credentialFactories):
            challenge = (yield factory.getChallenge(clientAddress))
            challenge['nonce'] = "noNoncense"

            clientResponse = authRequest1[ctr] % (
                challenge['nonce'],
                self.getDigestResponse(challenge, "00000001"),
            )

            request = _trivial_GET()
            yield self.assertRaisesDeferred(error.LoginFailed, factory.decode,
                                            clientResponse, request)

            factory._invalidate(FAKE_STATIC_NONCE)
            response = (yield
                        UnauthorizedResponse.makeResponse({"Digest": factory},
                                                          request.remoteAddr))
            response.headers.getHeader("www-authenticate")[0][1]
示例#4
0
    def test_invalidNonce(self):
        """
        Test that login fails when the given nonce from the response, does not
        match the nonce encoded in the opaque.
        """

        credentialFactories = (
            FakeDigestCredentialFactory('md5', 'auth', 'test realm', self.namespace1),
            FakeDigestCredentialFactory('md5', '', 'test realm', self.namespace2)
        )

        for ctr, factory in enumerate(credentialFactories):
            challenge = (yield factory.getChallenge(clientAddress))
            challenge['nonce'] = "noNoncense"

            clientResponse = authRequest1[ctr] % (
                challenge['nonce'],
                self.getDigestResponse(challenge, "00000001"),
            )

            request = _trivial_GET()
            yield self.assertRaisesDeferred(
                error.LoginFailed,
                factory.decode,
                clientResponse,
                request
            )

            factory._invalidate(FAKE_STATIC_NONCE)
            response = (yield UnauthorizedResponse.makeResponse(
                {"Digest": factory},
                request.remoteAddr
            ))
            response.headers.getHeader("www-authenticate")[0][1]
示例#5
0
    def renderHTTP(self, request):

        try:
            _ignore_authnUser, authzUser = yield self.authenticate(request)
        except Exception:
            authzUser = None

        # Turn 404 into 401
        if authzUser is None:
            response = (yield UnauthorizedResponse.makeResponse(
                request.credentialFactories, request.remoteAddr))
            returnValue(response)
        else:
            response = StatusResponse(responsecode.NOT_FOUND,
                                      "Resource not found")
            returnValue(response)
示例#6
0
    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)
示例#7
0
    def renderHTTP(self, request):

        try:
            _ignore_authnUser, authzUser = yield self.authenticate(request)
        except Exception:
            authzUser = None

        # Turn 404 into 401
        if authzUser is None:
            response = (yield UnauthorizedResponse.makeResponse(
                request.credentialFactories,
                request.remoteAddr
            ))
            returnValue(response)
        else:
            response = StatusResponse(responsecode.NOT_FOUND, "Resource not found")
            returnValue(response)
示例#8
0
    def test_oldNonce(self):
        """
        Test that the login fails when the given opaque is older than
        DigestCredentialFactory.CHALLENGE_LIFETIME_SECS
        """

        credentialFactories = (FakeDigestCredentialFactory(
            'md5', 'auth', 'test realm', self.namespace1),
                               FakeDigestCredentialFactory(
                                   'md5', '', 'test realm', self.namespace2))

        for ctr, factory in enumerate(credentialFactories):
            challenge = (yield factory.getChallenge(clientAddress))
            nonce_count, timestamp = (yield factory.db.get(challenge['nonce']))
            factory.db.set(
                challenge['nonce'],
                (nonce_count, timestamp -
                 2 * digest.DigestCredentialFactory.CHALLENGE_LIFETIME_SECS))

            clientResponse = authRequest1[ctr] % (
                challenge['nonce'],
                self.getDigestResponse(challenge, "00000001"),
            )

            request = _trivial_GET()
            yield self.assertRaisesDeferred(error.LoginFailed, factory.decode,
                                            clientResponse, request)

            response = (yield UnauthorizedResponse.makeResponse(
                {"Digest": factory},
                request.remoteAddr,
            ))
            wwwhdrs = response.headers.getHeader("www-authenticate")[0][1]
            self.assertTrue(
                'stale' in wwwhdrs,
                msg="No stale parameter in Digest WWW-Authenticate headers: %s"
                % (wwwhdrs, ))
            self.assertEquals(
                wwwhdrs['stale'],
                'true',
                msg=
                "stale parameter not set to true in Digest WWW-Authenticate headers: %s"
                % (wwwhdrs, ))
示例#9
0
    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=quote(
                        unquote(request.path),
                        safe=':/') + '/'
                )
            )
            returnValue(response)
示例#10
0
    def test_oldNonce(self):
        """
        Test that the login fails when the given opaque is older than
        DigestCredentialFactory.CHALLENGE_LIFETIME_SECS
        """

        credentialFactories = (
            FakeDigestCredentialFactory('md5', 'auth', 'test realm', self.namespace1),
            FakeDigestCredentialFactory('md5', '', 'test realm', self.namespace2)
        )

        for ctr, factory in enumerate(credentialFactories):
            challenge = (yield factory.getChallenge(clientAddress))
            nonce_count, timestamp = (yield factory.db.get(challenge['nonce']))
            factory.db.set(challenge['nonce'], (nonce_count, timestamp - 2 * digest.DigestCredentialFactory.CHALLENGE_LIFETIME_SECS))

            clientResponse = authRequest1[ctr] % (
                challenge['nonce'],
                self.getDigestResponse(challenge, "00000001"),
            )

            request = _trivial_GET()
            yield self.assertRaisesDeferred(
                error.LoginFailed,
                factory.decode,
                clientResponse,
                request
            )

            response = (yield UnauthorizedResponse.makeResponse(
                {"Digest": factory},
                request.remoteAddr,
            ))
            wwwhdrs = response.headers.getHeader("www-authenticate")[0][1]
            self.assertTrue('stale' in wwwhdrs, msg="No stale parameter in Digest WWW-Authenticate headers: %s" % (wwwhdrs,))
            self.assertEquals(wwwhdrs['stale'], 'true', msg="stale parameter not set to true in Digest WWW-Authenticate headers: %s" % (wwwhdrs,))
示例#11
0
    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)
示例#12
0
def getWikiACL(resource, request):
    """
    Ask the wiki server we're paired with what level of access the authnUser has.

    Returns an ACL.

    Wiki authentication is a bit tricky because the end-user accessing a group
    calendar may not actually be enabled for calendaring.  Therefore in that
    situation, the authzUser will have been replaced with the wiki principal
    in locateChild( ), so that any changes the user makes will have the wiki
    as the originator.  The authnUser will always be the end-user.
    """
    from twistedcaldav.directory.principal import DirectoryPrincipalResource

    if (not hasattr(resource, "record") or
        resource.record.recordType != WikiDirectoryService.recordType_wikis):
        returnValue(None)

    if hasattr(request, 'wikiACL'):
        returnValue(request.wikiACL)

    userID = "unauthenticated"
    wikiID = resource.record.shortNames[0]

    try:
        url = str(request.authnUser.children[0])
        principal = (yield request.locateResource(url))
        if isinstance(principal, DirectoryPrincipalResource):
            userID = principal.record.guid
    except:
        # TODO: better error handling
        pass

    try:
        access = (yield getWikiAccess(userID, wikiID))

        # The ACL we returns has ACEs for the end-user and the wiki principal
        # in case authzUser is the wiki principal.
        if access == "read":
            request.wikiACL = davxml.ACL(
                davxml.ACE(
                    request.authnUser,
                    davxml.Grant(
                        davxml.Privilege(davxml.Read()),
                        davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),

                        # We allow write-properties so that direct sharees can change
                        # e.g. calendar color properties
                        davxml.Privilege(davxml.WriteProperties()),
                    ),
                    TwistedACLInheritable(),
                ),
                davxml.ACE(
                    davxml.Principal(
                        davxml.HRef.fromString("/principals/wikis/%s/" % (wikiID,))
                    ),
                    davxml.Grant(
                        davxml.Privilege(davxml.Read()),
                        davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
                    ),
                    TwistedACLInheritable(),
                )
            )
            returnValue(request.wikiACL)

        elif access in ("write", "admin"):
            request.wikiACL = davxml.ACL(
                davxml.ACE(
                    request.authnUser,
                    davxml.Grant(
                        davxml.Privilege(davxml.Read()),
                        davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
                        davxml.Privilege(davxml.Write()),
                    ),
                    TwistedACLInheritable(),
                ),
                davxml.ACE(
                    davxml.Principal(
                        davxml.HRef.fromString("/principals/wikis/%s/" % (wikiID,))
                    ),
                    davxml.Grant(
                        davxml.Privilege(davxml.Read()),
                        davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
                        davxml.Privilege(davxml.Write()),
                    ),
                    TwistedACLInheritable(),
                )
            )
            returnValue(request.wikiACL)

        else: # "no-access":

            if userID == "unauthenticated":
                # Return a 401 so they have an opportunity to log in
                response = (yield UnauthorizedResponse.makeResponse(
                    request.credentialFactories,
                    request.remoteAddr,
                ))
                raise HTTPError(response)

            raise HTTPError(
                StatusResponse(
                    responsecode.FORBIDDEN,
                    "You are not allowed to access this wiki"
                )
            )

    except HTTPError:
        # pass through the HTTPError we might have raised above
        raise

    except Exception, e:
        log.error("Wiki ACL lookup failed: %s" % (e,))
        raise HTTPError(StatusResponse(responsecode.SERVICE_UNAVAILABLE, "Wiki ACL lookup failed"))
示例#13
0
def getWikiACL(resource, request):
    """
    Ask the wiki server we're paired with what level of access the authnUser
    has.

    Returns an ACL.

    Wiki authentication is a bit tricky because the end-user accessing a group
    calendar may not actually be enabled for calendaring.  Therefore in that
    situation, the authzUser will have been replaced with the wiki principal
    in locateChild( ), so that any changes the user makes will have the wiki
    as the originator.  The authnUser will always be the end-user.
    """
    from twistedcaldav.directory.principal import DirectoryPrincipalResource

    if (not hasattr(resource, "record")
            or resource.record.recordType != RecordType.macOSXServerWiki):
        returnValue(None)

    if hasattr(request, 'wikiACL'):
        returnValue(request.wikiACL)

    wikiRecord = resource.record
    wikiID = wikiRecord.shortNames[0]
    userRecord = None

    try:
        url = request.authnUser.principalURL()
        principal = (yield request.locateResource(url))
        if isinstance(principal, DirectoryPrincipalResource):
            userRecord = principal.record
    except:
        # TODO: better error handling
        pass

    try:
        access = yield wikiRecord.accessForRecord(userRecord)

        # The ACL we returns has ACEs for the end-user and the wiki principal
        # in case authzUser is the wiki principal.
        if access == WikiAccessLevel.read:
            request.wikiACL = davxml.ACL(
                davxml.ACE(
                    (request.authnUser.principalElement()
                     if request.authnUser is not None else davxml.Principal(
                         davxml.Unauthenticated())),
                    davxml.Grant(
                        davxml.Privilege(davxml.Read()),
                        davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),

                        # We allow write-properties so that direct sharees can
                        # change e.g. calendar color properties
                        davxml.Privilege(davxml.WriteProperties()),
                    ),
                    TwistedACLInheritable(),
                ),
                davxml.ACE(
                    davxml.Principal(
                        davxml.HRef.fromString(
                            "/principals/wikis/{}/".format(wikiID))),
                    davxml.Grant(
                        davxml.Privilege(davxml.Read()),
                        davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
                    ),
                    TwistedACLInheritable(),
                ))
            returnValue(request.wikiACL)

        elif access == WikiAccessLevel.write:
            request.wikiACL = davxml.ACL(
                davxml.ACE(
                    (request.authnUser.principalElement()
                     if request.authnUser is not None else davxml.Principal(
                         davxml.Unauthenticated())),
                    davxml.Grant(
                        davxml.Privilege(davxml.Read()),
                        davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
                        davxml.Privilege(davxml.Write()),
                    ),
                    TwistedACLInheritable(),
                ),
                davxml.ACE(
                    davxml.Principal(
                        davxml.HRef.fromString(
                            "/principals/wikis/{}/".format(wikiID))),
                    davxml.Grant(
                        davxml.Privilege(davxml.Read()),
                        davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
                        davxml.Privilege(davxml.Write()),
                    ),
                    TwistedACLInheritable(),
                ))
            returnValue(request.wikiACL)

        else:  # "no-access":

            if userRecord is None:
                # Return a 401 so they have an opportunity to log in
                response = (yield UnauthorizedResponse.makeResponse(
                    request.credentialFactories,
                    request.remoteAddr,
                ))
                raise HTTPError(response)

            raise HTTPError(
                StatusResponse(responsecode.FORBIDDEN,
                               "You are not allowed to access this wiki"))

    except HTTPError:
        # pass through the HTTPError we might have raised above
        raise

    except Exception as e:
        log.error("Wiki ACL lookup failed: {error}", error=e)
        raise HTTPError(
            StatusResponse(responsecode.SERVICE_UNAVAILABLE,
                           "Wiki ACL lookup failed"))
示例#14
0
    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)
                        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}: "
                                "{message}",
                                token=token, message=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 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)
示例#15
0
                for reject in config.RejectClients:
                    if reject.search(agent) is not None:
                        log.info("Rejecting user-agent: %s" % (agent,))
                        raise HTTPError(StatusResponse(
                            responsecode.FORBIDDEN,
                            "Your client software (%s) is not allowed to access this service." % (agent,)
                        ))

        if config.EnableResponseCache and request.method == "PROPFIND" and not getattr(request, "notInCache", False) and len(segments) > 1:
            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)

            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