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