[Checkins] SVN: grok/branches/faassen-rest/src/grok/ Add security
tests and massage things until they work. This required
Martijn Faassen
faassen at infrae.com
Mon Oct 15 16:38:49 EDT 2007
Log message for revision 80884:
Add security tests and massage things until they work. This required
the creation of a new GrokHTTPPublication that is along the lines of
GrokBrowserPublication and GrokXMLRPCPublication.
Incidentally this also allows the right MethodNotAllowedError to be
raised for weird verbs like FROG.
Changed:
U grok/branches/faassen-rest/src/grok/configure.zcml
U grok/branches/faassen-rest/src/grok/ftests/rest/rest.py
U grok/branches/faassen-rest/src/grok/meta.py
U grok/branches/faassen-rest/src/grok/publication.py
-=-
Modified: grok/branches/faassen-rest/src/grok/configure.zcml
===================================================================
--- grok/branches/faassen-rest/src/grok/configure.zcml 2007-10-15 18:33:03 UTC (rev 80883)
+++ grok/branches/faassen-rest/src/grok/configure.zcml 2007-10-15 20:38:49 UTC (rev 80884)
@@ -76,6 +76,14 @@
priority="11"
/>
+ <publisher
+ name="HTTP"
+ factory=".publication.GrokHTTPFactory"
+ methods="*"
+ mimetypes="*"
+ priority="1"
+ />
+
<!-- need to grok this for some basic REST support -->
<grok:grok package=".rest" />
Modified: grok/branches/faassen-rest/src/grok/ftests/rest/rest.py
===================================================================
--- grok/branches/faassen-rest/src/grok/ftests/rest/rest.py 2007-10-15 18:33:03 UTC (rev 80883)
+++ grok/branches/faassen-rest/src/grok/ftests/rest/rest.py 2007-10-15 20:38:49 UTC (rev 80884)
@@ -141,18 +141,52 @@
Content-Type: text/plain
<BLANKLINE>
Method Not Allowed
-
+
+We can also try this with a completely made-up request method, like FROG::
+
+ >>> print http('FROG /++rest++b/app HTTP/1.1')
+ HTTP/1. 405 Method Not Allowed
+ Allow: GET, PUT
+ Content-Length: 18
+ <BLANKLINE>
+ Method Not Allowed
+
+Let's now see whether security works properly with REST. GET should
+be public::
+
+ >>> print http('GET /++rest++e/app/alpha HTTP/1.1')
+ HTTP/1. 200 Ok
+ Content-Length: 4
+ Content-Type: text/plain
+ <BLANKLINE>
+ GET3
+
+POST, PUT and DELETE however are not public::
+
+ >>> print http('POST /++rest++e/app/alpha HTTP/1.1')
+ HTTP/1. 401 Unauthorized
+ Content-Length: 0
+ Content-Type: text/plain
+ WWW-Authenticate: basic realm="Zope"
+ <BLANKLINE>
+
+ >>> print http('PUT /++rest++e/app/alpha HTTP/1.1')
+ HTTP/1. 401 Unauthorized
+ Content-Length: 0
+ WWW-Authenticate: basic realm="Zope"
+ <BLANKLINE>
+
+ >>> print http('DELETE /++rest++e/app/alpha HTTP/1.1')
+ HTTP/1. 401 Unauthorized
+ Content-Length: 0
+ WWW-Authenticate: basic realm="Zope"
+ <BLANKLINE>
+
Todo:
* Support for OPTIONS, HEAD, other methods?
* Content-Type header is there for GET/POST, but not for PUT/DELETE...
-
-* MethodNotAllowed doesn't work correctly if the method is completely
- unrecognized, such as FROG instead of POST. It falls back on the default
- MethodNotAllowed then, instead of GrokMethodNotAllowed.
-
-* Security tests.
"""
import grok
@@ -172,6 +206,9 @@
class LayerC(grok.IRESTLayer):
pass
+class LayerSecurity(grok.IRESTLayer):
+ pass
+
class A(grok.RESTProtocol):
grok.layer(LayerA)
@@ -184,6 +221,9 @@
class D(grok.RESTProtocol):
grok.layer(grok.IRESTLayer)
+class E(grok.RESTProtocol):
+ grok.layer(LayerSecurity)
+
class ARest(grok.REST):
grok.layer(LayerA)
grok.context(MyApp)
@@ -219,3 +259,24 @@
def GET(self):
return "GET2"
+
+class SecurityRest(grok.REST):
+ grok.context(MyContent)
+ grok.layer(LayerSecurity)
+
+ @grok.require('zope.Public')
+ def GET(self):
+ return "GET3"
+
+ @grok.require('zope.ManageContent')
+ def POST(self):
+ return "POST3"
+
+ @grok.require('zope.ManageContent')
+ def PUT(self):
+ return "PUT3"
+
+ @grok.require('zope.ManageContent')
+ def DELETE(self):
+ return "DELETE3"
+
Modified: grok/branches/faassen-rest/src/grok/meta.py
===================================================================
--- grok/branches/faassen-rest/src/grok/meta.py 2007-10-15 18:33:03 UTC (rev 80883)
+++ grok/branches/faassen-rest/src/grok/meta.py 2007-10-15 20:38:49 UTC (rev 80884)
@@ -128,9 +128,10 @@
self.context = context
self.request = request
self.__parent__ = self.context
-
- def browserDefault(self, request):
- return self, ()
+
+ # XXX evidently not necessary?
+ #def browserDefault(self, request):
+ # return self, ()
class RESTGrokker(martian.ClassGrokker):
component_class = grok.REST
Modified: grok/branches/faassen-rest/src/grok/publication.py
===================================================================
--- grok/branches/faassen-rest/src/grok/publication.py 2007-10-15 18:33:03 UTC (rev 80883)
+++ grok/branches/faassen-rest/src/grok/publication.py 2007-10-15 20:38:49 UTC (rev 80884)
@@ -14,15 +14,19 @@
"""Grok publication objects
"""
+from grok.components import GrokMethodNotAllowed
+
+from zope import component
from zope.security.proxy import removeSecurityProxy
from zope.security.checker import selectChecker
+from zope.publisher.publish import mapply
-from zope.app.publication.http import BaseHTTPPublication
+from zope.app.publication.http import BaseHTTPPublication, HTTPPublication
from zope.app.publication.browser import BrowserPublication
from zope.app.publication.requestpublicationfactories import \
- BrowserFactory, XMLRPCFactory
+ BrowserFactory, XMLRPCFactory, HTTPFactory
+from zope.app.http.interfaces import IHTTPException
-
class ZopePublicationSansProxy(object):
def getApplication(self, request):
@@ -59,9 +63,28 @@
class GrokXMLRPCPublication(ZopePublicationSansProxy, BaseHTTPPublication):
pass
-
class GrokXMLRPCFactory(XMLRPCFactory):
def __call__(self):
request, publication = super(GrokXMLRPCFactory, self).__call__()
return request, GrokXMLRPCPublication
+
+
+class GrokHTTPPublication(ZopePublicationSansProxy, HTTPPublication):
+ def callObject(self, request, ob):
+ orig = ob
+ if not IHTTPException.providedBy(ob):
+ ob = component.queryMultiAdapter((ob, request),
+ name=request.method)
+ checker = selectChecker(ob)
+ if checker is not None:
+ checker.check(ob, '__call__')
+ ob = getattr(ob, request.method, None)
+ if ob is None:
+ raise GrokMethodNotAllowed(orig, request)
+ return mapply(ob, request.getPositionalArguments(), request)
+
+class GrokHTTPFactory(HTTPFactory):
+ def __call__(self):
+ request, publication = super(GrokHTTPFactory, self).__call__()
+ return request, GrokHTTPPublication
More information about the Checkins
mailing list