[Zope3-checkins] SVN: Zope3/branches/ZopeX3-3.0/src/zope/ Changed so that removeAllProxies is no-longer needed.

Jim Fulton jim at zope.com
Fri Aug 27 20:43:51 EDT 2004


Log message for revision 27320:
  Changed so that removeAllProxies is no-longer needed.
  
  Updated README.txt to test handling of faults.
  


Changed:
  U   Zope3/branches/ZopeX3-3.0/src/zope/app/publication/xmlrpc.py
  U   Zope3/branches/ZopeX3-3.0/src/zope/app/publisher/xmlrpc/README.txt
  U   Zope3/branches/ZopeX3-3.0/src/zope/app/publisher/xmlrpc/configure.zcml
  A   Zope3/branches/ZopeX3-3.0/src/zope/publisher/tests/test_xmlrpc.py
  U   Zope3/branches/ZopeX3-3.0/src/zope/publisher/xmlrpc.py


-=-
Modified: Zope3/branches/ZopeX3-3.0/src/zope/app/publication/xmlrpc.py
===================================================================
--- Zope3/branches/ZopeX3-3.0/src/zope/app/publication/xmlrpc.py	2004-08-28 00:43:49 UTC (rev 27319)
+++ Zope3/branches/ZopeX3-3.0/src/zope/app/publication/xmlrpc.py	2004-08-28 00:43:51 UTC (rev 27320)
@@ -18,11 +18,6 @@
 $Id$
 """
 
-from zope.app import zapi
-from zope.component import queryView, queryDefaultViewName
-from zope.proxy import removeAllProxies
-from zope.app.publisher.interfaces.xmlrpc import IXMLRPCPresentation
-from zope.security.checker import ProxyFactory
 from zope.app.publication.http import BaseHTTPPublication
 
 # Don't need any special handling for xml-rpc

Modified: Zope3/branches/ZopeX3-3.0/src/zope/app/publisher/xmlrpc/README.txt
===================================================================
--- Zope3/branches/ZopeX3-3.0/src/zope/app/publisher/xmlrpc/README.txt	2004-08-28 00:43:49 UTC (rev 27319)
+++ Zope3/branches/ZopeX3-3.0/src/zope/app/publisher/xmlrpc/README.txt	2004-08-28 00:43:51 UTC (rev 27320)
@@ -1,6 +1,9 @@
 XML-RPC views
 =============
 
+XML-RPC Methods
+---------------
+
 There are two ways to write XML-RPV views. You can write views that
 provide "methods" for other objects, and you can write views that have
 their own methods.  Let's look at the former case first, since it's a
@@ -45,8 +48,7 @@
   ... Content-Length: 73
   ... Content-Type: application/x-www-form-urlencoded
   ... 
-  ... type_name=zope.app.browser.add.zope.app.folder.folder.Folder&new_value=f1"""
-  ... , handle_errors=False)
+  ... type_name=zope.app.browser.add.zope.app.folder.folder.Folder&new_value=f1""")
   HTTP/1.1 303 See Other
   ...
 
@@ -123,6 +125,8 @@
   </methodResponse>
   <BLANKLINE>
 
+Named XM-RPC Views
+------------------
 
 Now let's look at views that have their own methods or other
 subobjects.  Views that have their own methods have names that appear
@@ -194,7 +198,7 @@
   ... <params>
   ... </params>
   ... </methodCall>
-  ... """, handle_errors=False)
+  ... """)
   HTTP/1.0 200 Ok
   Content-Length: 208
   Content-Type: text/xml;charset=utf-8
@@ -240,3 +244,78 @@
   </params>
   </methodResponse>
   <BLANKLINE>
+
+Faults
+------
+
+If you need to raise an error, the prefered way to do it is via an
+`xmlrpclib.Fault`:
+
+  >>> import xmlrpclib
+
+  >>> class FaultDemo:
+  ...     def __init__(self, context, request):
+  ...         self.context = context
+  ...         self.request = request
+  ...
+  ...     def your_fault(self):
+  ...         return xmlrpclib.Fault(42, "It's your fault!")
+
+Now we'll register it as a view:
+
+  >>> from zope.configuration import xmlconfig
+  >>> ignored = xmlconfig.string("""
+  ... <configure 
+  ...     xmlns="http://namespaces.zope.org/zope"
+  ...     xmlns:xmlrpc="http://namespaces.zope.org/xmlrpc"
+  ...     >
+  ...   <!-- We only need to do this include in this example, 
+  ...        Normally the include has already been done for us. -->
+  ...   <include package="zope.app.publisher.xmlrpc" file="meta.zcml" />
+  ...
+  ...   <xmlrpc:view
+  ...       for="zope.app.folder.folder.IFolder"
+  ...       methods="your_fault"
+  ...       class="zope.app.publisher.xmlrpc.README.FaultDemo"
+  ...       permission="zope.ManageContent"
+  ...       />
+  ... </configure>
+  ... """)
+
+Now, when we call it, we get a proper XML-RPC fault:
+
+  >>> print http(r"""
+  ... POST / HTTP/1.0
+  ... Authorization: Basic bWdyOm1ncnB3
+  ... Content-Length: 104
+  ... Content-Type: text/xml
+  ... 
+  ... <?xml version='1.0'?>
+  ... <methodCall>
+  ... <methodName>your_fault</methodName>
+  ... <params>
+  ... </params>
+  ... </methodCall>
+  ... """, handle_errors=False)
+  HTTP/1.0 200 Ok
+  Content-Length: 272
+  Content-Type: text/xml;charset=utf-8
+  <BLANKLINE>
+  <?xml version='1.0'?>
+  <methodResponse>
+  <fault>
+  <value><struct>
+  <member>
+  <name>faultCode</name>
+  <value><int>42</int></value>
+  </member>
+  <member>
+  <name>faultString</name>
+  <value><string>It's your fault!</string></value>
+  </member>
+  </struct></value>
+  </fault>
+  </methodResponse>
+  <BLANKLINE>
+
+

Modified: Zope3/branches/ZopeX3-3.0/src/zope/app/publisher/xmlrpc/configure.zcml
===================================================================
--- Zope3/branches/ZopeX3-3.0/src/zope/app/publisher/xmlrpc/configure.zcml	2004-08-28 00:43:49 UTC (rev 27319)
+++ Zope3/branches/ZopeX3-3.0/src/zope/app/publisher/xmlrpc/configure.zcml	2004-08-28 00:43:51 UTC (rev 27320)
@@ -3,6 +3,10 @@
     xmlns:xmlrpc="http://namespaces.zope.org/xmlrpc"
     >
 
+  <class class="xmlrpclib.Fault">
+    <require permission="zope.Public" attributes="faultCode faultString" />
+  </class>
+
   <view 
       for="zope.interface.Interface"
       type="zope.publisher.interfaces.xmlrpc.IXMLRPCRequest"

Added: Zope3/branches/ZopeX3-3.0/src/zope/publisher/tests/test_xmlrpc.py
===================================================================
--- Zope3/branches/ZopeX3-3.0/src/zope/publisher/tests/test_xmlrpc.py	2004-08-28 00:43:49 UTC (rev 27319)
+++ Zope3/branches/ZopeX3-3.0/src/zope/publisher/tests/test_xmlrpc.py	2004-08-28 00:43:51 UTC (rev 27320)
@@ -0,0 +1,31 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""XXX short summary goes here.
+
+$Id$
+"""
+import unittest
+from zope.testing.doctest import DocTestSuite
+from zope.testing.cleanup import CleanUp
+
+
+def test_suite():
+    return unittest.TestSuite((
+        DocTestSuite('zope.publisher.xmlrpc',
+                     tearDown=lambda ignored=None: CleanUp().cleanUp()),
+        ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
+


Property changes on: Zope3/branches/ZopeX3-3.0/src/zope/publisher/tests/test_xmlrpc.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Modified: Zope3/branches/ZopeX3-3.0/src/zope/publisher/xmlrpc.py
===================================================================
--- Zope3/branches/ZopeX3-3.0/src/zope/publisher/xmlrpc.py	2004-08-28 00:43:49 UTC (rev 27319)
+++ Zope3/branches/ZopeX3-3.0/src/zope/publisher/xmlrpc.py	2004-08-28 00:43:51 UTC (rev 27320)
@@ -26,8 +26,9 @@
 from zope.publisher.interfaces.xmlrpc import IXMLRPCRequest
 
 from zope.publisher.http import HTTPRequest, HTTPResponse
-from zope.proxy import removeAllProxies
 
+from zope.security.proxy import isinstance
+
 class XMLRPCRequest(HTTPRequest):
     implements(IXMLRPCRequest)
 
@@ -93,21 +94,17 @@
         If is_error is true then the HTML will be formatted as a Zope error
         message instead of a generic HTML page.
         """
-        # We have to call removeAllProxies() to allow the type
-        # introspection to work, and allow the serialization provided
-        # by the xmlrpclib module to operate on the actual object.
-        body = removeAllProxies(body)
+        body = premarshal(body)
         if isinstance(body, xmlrpclib.Fault):
             # Convert Fault object to XML-RPC response.
-            body = xmlrpclib.dumps(body, methodresponse=1)
+            body = xmlrpclib.dumps(body, methodresponse=True)
         else:
             # Marshall our body as an XML-RPC response. Strings will be sent
             # strings, integers as integers, etc. We do *not* convert
             # everything to a string first.
-            if body is None:
-                body = xmlrpclib.False # Argh, XML-RPC doesn't handle null
             try:
-                body = xmlrpclib.dumps((body,), methodresponse=1)
+                body = xmlrpclib.dumps((body,), methodresponse=True,
+                                       allow_none=True)
             except:
                 # We really want to catch all exceptions at this point!
                 self.handleException(sys.exc_info())
@@ -146,3 +143,83 @@
         # Do the damage.
         self.setBody(fault_text)
         self.setStatus(200)
+
+
+def premarshal_dict(data):
+    return dict([(premarshal(k), premarshal(v))
+                 for (k, v) in data.items()])
+
+def premarshal_list(data):
+    return map(premarshal, data)
+
+def premarshal_fault(data):
+    return xmlrpclib.Fault(
+        premarshal(data.faultCode),
+        premarshal(data.faultString),
+        )
+
+premarshal_dispatch_table = {
+    dict: premarshal_dict,
+    list: premarshal_list,
+    tuple: premarshal_list,
+    xmlrpclib.Fault: premarshal_fault,
+    }
+premarshal_dispatch = premarshal_dispatch_table.get
+
+def premarshal(data):
+    """Premarshal data before handing it to xmlrpclib for marhalling
+
+    The initial putpuse of this function is to remove security proxies
+    without resorting to removeSecurityProxy.   This way, we can avoid
+    inadvertently providing access to data that should be protected.
+
+    Suppose we have a sample data structure:
+
+      >>> sample = {'foo': (1, ['x', 'y', 1.2])}
+
+    if we put the sample in a security procy:
+
+      >>> from zope.security.checker import ProxyFactory
+      >>> proxied_sample = ProxyFactory(sample)
+
+    We can still get to the data, but the non-rock data is proxied:
+
+      >>> from zope.security.proxy import Proxy
+      >>> proxied_sample['foo']
+      (1, ['x', 'y', 1.2])
+      
+      >>> type(proxied_sample['foo']) is Proxy
+      True
+      >>> type(proxied_sample['foo'][1]) is Proxy
+      True
+
+    But we can strip the proxies using premarshal:
+
+      >>> stripped = premarshal(proxied_sample)
+      >>> stripped
+      {'foo': [1, ['x', 'y', 1.2]]}
+
+      >>> type(stripped['foo']) is Proxy
+      False
+      >>> type(stripped['foo'][1]) is Proxy
+      False
+
+    So xmlrpclib will be happy. :)
+
+    We can also use premarshal to strip proxies off of Fault objects.
+    We have to make a security declaration first though:
+
+      >>> from zope.security.checker import NamesChecker, defineChecker
+      >>> defineChecker(xmlrpclib.Fault,
+      ...               NamesChecker(['faultCode', 'faultString']))
+    
+      >>> fault = xmlrpclib.Fault(1, 'waaa')
+      >>> proxied_fault = ProxyFactory(fault)
+      >>> stripped_fault = premarshal(proxied_fault)
+      >>> type(stripped_fault) is Proxy
+      False
+    """
+    premarshaller = premarshal_dispatch(data.__class__)
+    if premarshaller is not None:
+        return premarshaller(data)
+    return data



More information about the Zope3-Checkins mailing list