| 
									
										
										
										
											1996-06-11 20:12:49 +00:00
										 |  |  | """Bastionification utility.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | A bastion (for another object -- the 'original') is an object that has | 
					
						
							|  |  |  | the same methods as the original but does not give access to its | 
					
						
							|  |  |  | instance variables.  Bastions have a number of uses, but the most | 
					
						
							|  |  |  | obvious one is to provide code executing in restricted mode with a | 
					
						
							|  |  |  | safe interface to an object implemented in unrestricted mode. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The bastionification routine has an optional second argument which is | 
					
						
							|  |  |  | a filter function.  Only those methods for which the filter method | 
					
						
							|  |  |  | (called with the method name as argument) returns true are accessible. | 
					
						
							|  |  |  | The default filter method returns true unless the method name begins | 
					
						
							|  |  |  | with an underscore. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | There are a number of possible implementations of bastions.  We use a | 
					
						
							|  |  |  | 'lazy' approach where the bastion's __getattr__() discipline does all | 
					
						
							|  |  |  | the work for a particular method the first time it is used.  This is | 
					
						
							|  |  |  | usually fastest, especially if the user doesn't call all available | 
					
						
							|  |  |  | methods.  The retrieved methods are stored as instance variables of | 
					
						
							|  |  |  | the bastion, so the overhead is only occurred on the first use of each | 
					
						
							|  |  |  | method. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Detail: the bastion class has a __repr__() discipline which includes | 
					
						
							|  |  |  | the repr() of the original object.  This is precomputed when the | 
					
						
							|  |  |  | bastion is created. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-20 19:54:20 +00:00
										 |  |  | __all__ = ["BastionClass", "Bastion"] | 
					
						
							| 
									
										
										
										
											1996-06-11 20:12:49 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | from types import MethodType | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class BastionClass: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """Helper class used by the Bastion() function.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     You could subclass this and pass the subclass as the bastionclass | 
					
						
							|  |  |  |     argument to the Bastion() function, as long as the constructor has | 
					
						
							|  |  |  |     the same signature (a get() function and a name for the object). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, get, name): | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         """Constructor.
 | 
					
						
							| 
									
										
										
										
											1996-06-11 20:12:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         Arguments: | 
					
						
							| 
									
										
										
										
											1996-06-11 20:12:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         get - a function that gets the attribute value (by name) | 
					
						
							|  |  |  |         name - a human-readable name for the original object | 
					
						
							|  |  |  |                (suggestion: use repr(object)) | 
					
						
							| 
									
										
										
										
											1996-06-11 20:12:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         self._get_ = get | 
					
						
							|  |  |  |         self._name_ = name | 
					
						
							| 
									
										
										
										
											1996-06-11 20:12:49 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __repr__(self): | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         """Return a representation string.
 | 
					
						
							| 
									
										
										
										
											1996-06-11 20:12:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         This includes the name passed in to the constructor, so that | 
					
						
							|  |  |  |         if you print the bastion during debugging, at least you have | 
					
						
							|  |  |  |         some idea of what it is. | 
					
						
							| 
									
										
										
										
											1996-06-11 20:12:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         return "<Bastion for %s>" % self._name_ | 
					
						
							| 
									
										
										
										
											1996-06-11 20:12:49 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __getattr__(self, name): | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         """Get an as-yet undefined attribute value.
 | 
					
						
							| 
									
										
										
										
											1996-06-11 20:12:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         This calls the get() function that was passed to the | 
					
						
							|  |  |  |         constructor.  The result is stored as an instance variable so | 
					
						
							|  |  |  |         that the next time the same attribute is requested, | 
					
						
							|  |  |  |         __getattr__() won't be invoked. | 
					
						
							| 
									
										
										
										
											1996-06-11 20:12:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         If the get() function raises an exception, this is simply | 
					
						
							|  |  |  |         passed on -- exceptions are not cached. | 
					
						
							| 
									
										
										
										
											1996-06-11 20:12:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         attribute = self._get_(name) | 
					
						
							|  |  |  |         self.__dict__[name] = attribute | 
					
						
							|  |  |  |         return attribute | 
					
						
							| 
									
										
										
										
											1996-06-11 20:12:49 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def Bastion(object, filter = lambda name: name[:1] != '_', | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |             name=None, bastionclass=BastionClass): | 
					
						
							| 
									
										
										
										
											1996-06-11 20:12:49 +00:00
										 |  |  |     """Create a bastion for an object, using an optional filter.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     See the Bastion module's documentation for background. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Arguments: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     object - the original object | 
					
						
							|  |  |  |     filter - a predicate that decides whether a function name is OK; | 
					
						
							|  |  |  |              by default all names are OK that don't start with '_' | 
					
						
							|  |  |  |     name - the name of the object; default repr(object) | 
					
						
							|  |  |  |     bastionclass - class used to create the bastion; default BastionClass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-04-12 07:01:19 +00:00
										 |  |  |     raise RuntimeError, "This code is not secure in Python 2.2 and later" | 
					
						
							| 
									
										
										
										
											2003-01-06 15:43:34 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1996-06-11 20:12:49 +00:00
										 |  |  |     # Note: we define *two* ad-hoc functions here, get1 and get2. | 
					
						
							|  |  |  |     # Both are intended to be called in the same way: get(name). | 
					
						
							|  |  |  |     # It is clear that the real work (getting the attribute | 
					
						
							|  |  |  |     # from the object and calling the filter) is done in get1. | 
					
						
							|  |  |  |     # Why can't we pass get1 to the bastion?  Because the user | 
					
						
							|  |  |  |     # would be able to override the filter argument!  With get2, | 
					
						
							|  |  |  |     # overriding the default argument is no security loophole: | 
					
						
							|  |  |  |     # all it does is call it. | 
					
						
							|  |  |  |     # Also notice that we can't place the object and filter as | 
					
						
							|  |  |  |     # instance variables on the bastion object itself, since | 
					
						
							|  |  |  |     # the user has full access to all instance variables! | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get1(name, object=object, filter=filter): | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         """Internal function for Bastion().  See source comments.""" | 
					
						
							|  |  |  |         if filter(name): | 
					
						
							|  |  |  |             attribute = getattr(object, name) | 
					
						
							|  |  |  |             if type(attribute) == MethodType: | 
					
						
							|  |  |  |                 return attribute | 
					
						
							|  |  |  |         raise AttributeError, name | 
					
						
							| 
									
										
										
										
											1996-06-11 20:12:49 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def get2(name, get1=get1): | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         """Internal function for Bastion().  See source comments.""" | 
					
						
							|  |  |  |         return get1(name) | 
					
						
							| 
									
										
										
										
											1996-06-11 20:12:49 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if name is None: | 
					
						
							| 
									
										
										
										
											2004-02-12 17:35:32 +00:00
										 |  |  |         name = repr(object) | 
					
						
							| 
									
										
										
										
											1996-06-11 20:12:49 +00:00
										 |  |  |     return bastionclass(get2, name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _test(): | 
					
						
							|  |  |  |     """Test the Bastion() function.""" | 
					
						
							|  |  |  |     class Original: | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         def __init__(self): | 
					
						
							|  |  |  |             self.sum = 0 | 
					
						
							|  |  |  |         def add(self, n): | 
					
						
							|  |  |  |             self._add(n) | 
					
						
							|  |  |  |         def _add(self, n): | 
					
						
							|  |  |  |             self.sum = self.sum + n | 
					
						
							|  |  |  |         def total(self): | 
					
						
							|  |  |  |             return self.sum | 
					
						
							| 
									
										
										
										
											1996-06-11 20:12:49 +00:00
										 |  |  |     o = Original() | 
					
						
							|  |  |  |     b = Bastion(o) | 
					
						
							| 
									
										
										
										
											1996-08-20 20:21:52 +00:00
										 |  |  |     testcode = """if 1:
 | 
					
						
							| 
									
										
										
										
											1996-06-11 20:12:49 +00:00
										 |  |  |     b.add(81) | 
					
						
							|  |  |  |     b.add(18) | 
					
						
							|  |  |  |     print "b.total() =", b.total() | 
					
						
							|  |  |  |     try: | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         print "b.sum =", b.sum, | 
					
						
							| 
									
										
										
										
											1996-06-11 20:12:49 +00:00
										 |  |  |     except: | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         print "inaccessible" | 
					
						
							| 
									
										
										
										
											1996-06-11 20:12:49 +00:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         print "accessible" | 
					
						
							| 
									
										
										
										
											1996-06-11 20:12:49 +00:00
										 |  |  |     try: | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         print "b._add =", b._add, | 
					
						
							| 
									
										
										
										
											1996-06-11 20:12:49 +00:00
										 |  |  |     except: | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         print "inaccessible" | 
					
						
							| 
									
										
										
										
											1996-06-11 20:12:49 +00:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         print "accessible" | 
					
						
							| 
									
										
										
										
											1996-08-20 20:21:52 +00:00
										 |  |  |     try: | 
					
						
							| 
									
										
										
										
											2001-01-19 03:30:22 +00:00
										 |  |  |         print "b._get_.func_defaults =", map(type, b._get_.func_defaults), | 
					
						
							| 
									
										
										
										
											1996-08-20 20:21:52 +00:00
										 |  |  |     except: | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         print "inaccessible" | 
					
						
							| 
									
										
										
										
											1996-08-20 20:21:52 +00:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         print "accessible" | 
					
						
							| 
									
										
										
										
											1996-08-20 20:21:52 +00:00
										 |  |  |     \n"""
 | 
					
						
							|  |  |  |     exec testcode | 
					
						
							|  |  |  |     print '='*20, "Using rexec:", '='*20 | 
					
						
							|  |  |  |     import rexec | 
					
						
							|  |  |  |     r = rexec.RExec() | 
					
						
							|  |  |  |     m = r.add_module('__main__') | 
					
						
							|  |  |  |     m.b = b | 
					
						
							|  |  |  |     r.r_exec(testcode) | 
					
						
							| 
									
										
										
										
											1996-06-11 20:12:49 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == '__main__': | 
					
						
							|  |  |  |     _test() |