Pyro4: вызов метода на удаленном подобъекте без попытки возврата подобъекта - PullRequest
2 голосов
/ 06 марта 2019

Допустим, у меня есть объект, который является свойством другого объекта.

import Pyro4

@Pyro4.expose
class ClassB:
    def foo(self):
        return 'Hello from ClassB::foo()'

@Pyro4.expose
class ClassA:
    def __init__(self):
        self._b = ClassB()

    def foo(self):
        return 'Hello from ClassA::foo()'

    @property
    def b(self):
        return self._b

if __name__ == '__main__':
    daemon = Pyro4.Daemon(host='localhost')

    ns = Pyro4.locateNS(host='localhost', port=9090)
    ns.register('ClassA', daemon.register(ClassA))

    daemon.requestLoop()

И на локальном компьютере я запускаю это

import Pyro4

if __name__ == '__main__':
    ns = Pyro4.locateNS(host='localhost', port=9090)
    uri = ns.lookup('ClassA')
    a = Pyro4.Proxy(uri)

    print(a.foo())
    print(a.b.foo()) # Fails here

Когда я пытаюсь вызвать abfooон пытается сериализовать ab и локально вызвать foo для него, но я хочу вызвать foo для экземпляра ClassB, который уже существует на удаленном экземпляре ClassA.

Конечно, я мог бы добавить метод к ClassA, которыйделегат b.foo (), например,

def classA:
    # ...
    def foo_on_b(self):
        return self._b.foo()

Но я бы предпочел этого не делать.

Ответы [ 2 ]

1 голос
/ 06 марта 2019

По замыслу это невозможно.Это уязвимость безопасности.https://pyro4.readthedocs.io/en/stable/security.html#dotted-names-object-traversal

0 голосов
/ 07 марта 2019

Удалось вот так взломать.

remote.py

import Pyro4

class C:
    def __init__(self):
        self.val = 123

class B:
    def __init__(self):
        self.c = C()

class A:
    def __init__(self):
        self.b = B()

def getattr_r(obj, *attrs):
    return getattr_r(getattr(obj, attrs[0]), *attrs[1:]) if attrs else obj

class ClassC:
    def foo(self, x, y):
        return 'Hello from ClassC::foo()! {} + {} = {}'.format(x, y, x + y)

class ClassB:
    def __init__(self):
        self.c = ClassC()

    def foo(self, x, y):
        return 'Hello from ClassB::foo()! {} + {} = {}'.format(x, y, x + y)

@Pyro4.expose
class ClassA:
    def __init__(self):
        self.b = ClassB()

    def foo(self, x, y):
        return 'Hello from ClassA::foo()! {} + {} = {}'.format(x, y, x + y)

    def invoke_on_subobj(self, obj_chain, *args, **kwargs):   
        return getattr_r(self, *obj_chain)(*args, **kwargs)

if __name__ == '__main__':
    daemon = Pyro4.Daemon(host='localhost')

    ns = Pyro4.locateNS(host='localhost', port=9090)
    ns.register('ClassA', daemon.register(ClassA))

    daemon.requestLoop()

local.py

import Pyro4

class ObjTraversableProxy:
    def __init__(self, proxy, bound_attrs=[]):
        self._proxy = proxy
        self._bound_attrs = bound_attrs

    def __getattr__(self, attr):
        return ObjTraversableProxy(self._proxy, self._bound_attrs + [attr])

    def __call__(self, *args, **kwargs):
        if len(self._bound_attrs) > 1:
            return self._proxy.invoke_on_subobj(self._bound_attrs, *args, **kwargs)
        else:
            return getattr(self._proxy, self._bound_attrs[0])(*args, **kwargs)

if __name__ == '__main__':
    ns = Pyro4.locateNS(host='localhost', port=9090)
    uri = ns.lookup('ClassA')
    a = ObjTraversableProxy(Pyro4.Proxy(uri))

    print(a.foo(3, 4))
    print(a.b.foo(5, 6))
    print(a.b.c.foo(6, 7))

Результат

Hello from ClassA::foo()! 3 + 4 = 7
Hello from ClassB::foo()! 5 + 6 = 11
Hello from ClassC::foo()! 6 + 7 = 13
...