Skip to content

Commit

Permalink
Merge anonymous-guard-3924
Browse files Browse the repository at this point in the history
Author: esteve
Reviewer: exarkun
Fixes: #3924

Add support for anonymous sessions to `twisted.web.guard` by having the
wrapper attempt to log in to the portal using anonymous credentials
when a request without an authorization header is received.
  • Loading branch information
exarkun committed Jul 26, 2009
1 parent e26c924 commit c6c9270
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 5 deletions.
11 changes: 7 additions & 4 deletions twisted/web/_auth/wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
A guard implementation which supports HTTP header-based authentication
schemes.
If either no www-authenticate header is present in the request or the
supplied response is invalid a status code of 401 will be sent in the
response along with all accepted authentication schemes.
If no I{Authorization} header is supplied, an anonymous login will be
attempted by using a L{Anonymous} credentials object. If such a header is
supplied and does not contain allowed credentials, or if anonymous login is
denied, a 401 will be sent in the response along with I{WWW-Authenticate}
headers for each of the allowed authentication schemes.
"""

from zope.interface import implements
Expand All @@ -18,6 +20,7 @@
from twisted.web.resource import IResource, ErrorPage
from twisted.web import util
from twisted.cred import error
from twisted.cred.credentials import Anonymous


class UnauthorizedResource(object):
Expand Down Expand Up @@ -105,7 +108,7 @@ def getChildWithDefault(self, path, request):
"""
authheader = request.getHeader('authorization')
if not authheader:
return UnauthorizedResource(self._credentialFactories)
return util.DeferredResource(self._login(Anonymous()))

factory, respString = self._selectParseHeader(authheader)
if factory is None:
Expand Down
33 changes: 32 additions & 1 deletion twisted/web/test/test_httpauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

from twisted.cred import error, portal
from twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse
from twisted.cred.checkers import ANONYMOUS, AllowAnonymousAccess
from twisted.cred.credentials import IUsernamePassword

from twisted.web.iweb import ICredentialFactory
Expand Down Expand Up @@ -350,7 +351,12 @@ def test_getChildWithDefault(self):
"""
request = self.makeRequest([self.childName])
child = self.wrapper.getChildWithDefault(self.childName, request)
self.assertIsInstance(child, UnauthorizedResource)
d = request.notifyFinish()
def cbFinished(result):
self.assertEquals(request.responseCode, 401)
d.addCallback(cbFinished)
render(child, request)
return d


def _invalidAuthorizationTest(self, response):
Expand Down Expand Up @@ -545,3 +551,28 @@ def requestAvatarId(self, credentials):
render(child, request)
self.assertEqual(request.responseCode, 500)
self.assertEqual(len(self.flushLoggedErrors(UnexpectedException)), 1)


def test_anonymousAccess(self):
"""
Anonymous requests are allowed if a L{Portal} has an anonymous checker
registered.
"""
unprotectedContents = "contents of the unprotected child resource"

class UnprotectedResource(Resource):

def render_GET(self, request):
return unprotectedContents

self.avatars[ANONYMOUS] = UnprotectedResource()
self.portal.registerChecker(AllowAnonymousAccess())
self.credentialFactories.append(BasicCredentialFactory('example.com'))
request = self.makeRequest([self.childName])
child = getChildForRequest(self.wrapper, request)
d = request.notifyFinish()
def cbFinished(ignored):
self.assertEquals(request.written, [unprotectedContents])
d.addCallback(cbFinished)
render(child, request)
return d

0 comments on commit c6c9270

Please sign in to comment.