Example #1
0
 def test_ctor_empty(self):
     td = TransportDetails()
     data = td.marshal()
     self.assertEqual(
         data, {
             'channel_type': None,
             'channel_framing': None,
             'channel_serializer': None,
             'own': None,
             'peer': None,
             'is_server': None,
             'own_pid': None,
             'own_tid': None,
             'own_fd': None,
             'is_secure': None,
             'channel_id': None,
             'peer_cert': None,
             'websocket_protocol': None,
             'websocket_extensions_in_use': None,
             'http_headers_received': None,
             'http_headers_sent': None,
             'http_cbtid': None,
         })
     td2 = TransportDetails.parse(td.marshal())
     self.assertEqual(td2, td)
Example #2
0
    def test_publish_closed_session(self):
        """
        ensure a session doesn't get Events if it's closed
        (see also issue #431)
        """

        # we want to trigger a deeply-nested condition in
        # processPublish in class Broker -- lets try w/o refactoring
        # anything first...

        class TestSession(ApplicationSession):
            pass

        session0 = TestSession()
        session1 = TestSession()
        router = mock.MagicMock()
        router.new_correlation_id = lambda: 'fake correlation id'
        broker = Broker(router, reactor)

        # let's just "cheat" our way a little to the right state by
        # injecting our subscription "directly" (e.g. instead of
        # faking out an entire Subscribe etc. flow
        # ...so we need _subscriptions_map to have at least one
        # subscription (our test one) for the topic we'll publish to
        broker._subscription_map.add_observer(session0, 'test.topic')

        # simulate the session state we want, which is that a
        # transport is connected (._transport != None) but there
        # _session_id *is* None (not joined yet, or left already)
        self.assertIs(None, session0._session_id)
        session0._transport = mock.MagicMock()
        session0._transport.transport_details = TransportDetails(
            channel_id={'tls-unique': b'deadbeef'})
        session1._session_id = 1234  # "from" session should look connected + joined
        session1._transport = mock.MagicMock()
        session1._transport.transport_details = TransportDetails(
            channel_id={'tls-unique': b'aaaabeef'})

        # here's the main "cheat"; we're faking out the
        # router.authorize because we need it to callback immediately
        router.authorize = mock.MagicMock(
            return_value=txaio.create_future_success(
                dict(allow=True, cache=False, disclose=True)))

        # now we scan call "processPublish" such that we get to the
        # condition we're interested in (this "comes from" session1
        # beacuse by default publishes don't go to the same session)
        pubmsg = message.Publish(123, 'test.topic')
        broker.processPublish(session1, pubmsg)

        # neither session should have sent anything on its transport
        self.assertEqual(session0._transport.method_calls, [])
        self.assertEqual(session1._transport.method_calls, [])
Example #3
0
    def test_attributes(self):
        td = TransportDetails()
        for channel_type in TransportDetails.CHANNEL_TYPE_TO_STR:
            td.channel_type = channel_type
            self.assertEqual(td.channel_type, channel_type)

        for channel_framing in TransportDetails.CHANNEL_FRAMING_TO_STR:
            td.channel_framing = channel_framing
            self.assertEqual(td.channel_framing, channel_framing)

        for channel_serializer in TransportDetails.CHANNEL_SERIALIZER_TO_STR:
            td.channel_serializer = channel_serializer
            self.assertEqual(td.channel_serializer, channel_serializer)
Example #4
0
    def test_router_session_internal_error_onHello(self):
        """
        similar to above, but during _RouterSession's onMessage handling,
        where it calls self.onHello
        """

        # setup
        transport = mock.MagicMock()
        transport.transport_details = TransportDetails(
            channel_id={'tls-unique': b'deadbeef'})
        the_exception = RuntimeError("kerblam")

        def boom(*args, **kw):
            raise the_exception

        session = self.session_factory(
        )  # __call__ on the _RouterSessionFactory
        session.onHello = boom
        session.onOpen(transport)
        msg = message.Hello('realm1', dict(caller=role.RoleCallerFeatures()))

        with mock.patch.object(session, 'log') as logger:
            # do the test; should call onHello which is now "boom", above
            session.onMessage(msg)

            # check we got the right log.failure() call
            self.assertTrue(len(logger.method_calls) > 0)
            call = logger.method_calls[0]
            # for a MagicMock call-object, 0th thing is the method-name, 1st
            # thing is the arg-tuple, 2nd thing is the kwargs.
            self.assertEqual(call[0], 'warn')
            self.assertTrue('err' in call[2])
            self.assertEqual(call[2]['err'].value, the_exception)
Example #5
0
def create_transport_details(transport, is_server: bool) -> TransportDetails:
    # Internal helper. Base class calls this to create a TransportDetails
    peer = peer2str(transport)

    # https://docs.python.org/3.9/library/asyncio-protocol.html?highlight=get_extra_info#asyncio.BaseTransport.get_extra_info
    is_secure = transport.get_extra_info('peercert', None) is not None
    if is_secure:
        channel_id = {
            'tls-unique': transport_channel_id(transport, is_server,
                                               'tls-unique'),
        }
        channel_type = TransportDetails.CHANNEL_TYPE_TLS
        peer_cert = None
    else:
        channel_id = {}
        channel_type = TransportDetails.CHANNEL_TYPE_TCP
        peer_cert = None
    channel_framing = TransportDetails.CHANNEL_FRAMING_WEBSOCKET

    return TransportDetails(channel_type=channel_type,
                            channel_framing=channel_framing,
                            peer=peer,
                            is_server=is_server,
                            is_secure=is_secure,
                            channel_id=channel_id,
                            peer_cert=peer_cert)
 def test_attributes(self):
     sd1 = SessionDetails()
     td1 = TransportDetails.parse(TRANSPORT_DETAILS_1)
     sd1.realm = 'realm1'
     sd1.session = 666
     sd1.authid = 'homer'
     sd1.authrole = 'user'
     sd1.authmethod = 'wampcra'
     sd1.authprovider = 'static'
     sd1.authextra = {'foo': 'bar', 'baz': [1, 2, 3]}
     sd1.serializer = 'json'
     sd1.transport = td1
     sd1.resumed = False
     sd1.resumable = True
     sd1.resume_token = '8713e25a-d4f5-48b7-9d6d-eda66603a1ab'
     data = sd1.marshal()
     self.assertEqual(
         data, {
             'realm': sd1.realm,
             'session': sd1.session,
             'authid': sd1.authid,
             'authrole': sd1.authrole,
             'authmethod': sd1.authmethod,
             'authprovider': sd1.authprovider,
             'authextra': sd1.authextra,
             'serializer': sd1.serializer,
             'transport': sd1.transport.marshal(),
             'resumed': sd1.resumed,
             'resumable': sd1.resumable,
             'resume_token': sd1.resume_token,
         })
     sd2 = SessionDetails.parse(data)
     self.assertEqual(sd2, sd1)
Example #7
0
    def test_router_session_internal_error_onAuthenticate(self):
        """
        similar to above, but during _RouterSession's onMessage handling,
        where it calls self.onAuthenticate)
        """
        raise unittest.SkipTest('FIXME: Adjust unit test mocks #1567')

        # setup
        transport = mock.MagicMock()
        transport.transport_details = TransportDetails(
            channel_id={'tls-unique': b'deadbeef'})
        the_exception = RuntimeError("kerblam")

        def boom(*args, **kw):
            raise the_exception

        session = self.session_factory(
        )  # __call__ on the _RouterSessionFactory
        session.onAuthenticate = boom
        session.onOpen(transport)
        msg = message.Authenticate('bogus signature')

        # do the test; should call onHello which is now "boom", above
        session.onMessage(msg)

        errors = self.flushLoggedErrors()
        self.assertEqual(1, len(errors),
                         "Expected just one error: {}".format(errors))
        self.assertTrue(the_exception in [fail.value for fail in errors])
Example #8
0
 def test_str_transport(self):
     details = TransportDetails(
         channel_type=TransportDetails.CHANNEL_TYPE_FUNCTION,
         peer="example.com",
         is_secure=False,
         channel_id={},
     )
     # we can str() this and it doesn't fail
     str(details)
Example #9
0
 def __init__(self, *args, **kw):
     super(TestSession, self).__init__(*args, **kw)
     # for this test, pretend we're connected (without
     # going through sending a Hello etc.)
     self._transport = mock.MagicMock()
     self._transport.transport_details = TransportDetails(
         channel_id={'tls-unique': b'deadbeef'})
     self._session_id = 1234
     self._router = router  # normally done in Hello processing
     self._service_session = mock.MagicMock()
Example #10
0
    def test_channel_typeid(self):
        # test empty
        td = TransportDetails()
        self.assertEqual(td.channel_typeid, 'null-null-null')

        # test all combinations
        for channel_type in TransportDetails.CHANNEL_TYPE_TO_STR:
            for channel_framing in TransportDetails.CHANNEL_FRAMING_TO_STR:
                for channel_serializer in TransportDetails.CHANNEL_SERIALIZER_TO_STR:
                    td = TransportDetails(
                        channel_type=channel_type,
                        channel_framing=channel_framing,
                        channel_serializer=channel_serializer)
                    channel_typeid = '{}-{}-{}'.format(
                        TransportDetails.CHANNEL_TYPE_TO_STR[channel_type],
                        TransportDetails.
                        CHANNEL_FRAMING_TO_STR[channel_framing],
                        TransportDetails.
                        CHANNEL_SERIALIZER_TO_STR[channel_serializer])
                    self.assertEqual(td.channel_typeid, channel_typeid)
Example #11
0
    def get_session_by_session_id(self,
                                  session_id: int,
                                  joined_before: Optional[int] = None) -> Optional[Dict[str, Any]]:
        """
        Implements :meth:`crossbar._interfaces.IRealmStore.get_session_by_session_id`
        """
        if joined_before:
            _joined_before = np.datetime64(joined_before, 'ns')
        else:
            _joined_before = np.datetime64(time_ns(), 'ns')
        _from_key = (session_id, np.datetime64(0, 'ns'))
        _to_key = (session_id, _joined_before)

        # check if we have a record store for the session
        session: Optional[cfxdb.realmstore.Session] = None
        with self._db.begin() as txn:
            # lookup session by WAMP session ID and find the most recent session
            # according to joined_at timestamp
            for session_oid in self._schema.idx_sessions_by_session_id.select(txn,
                                                                              from_key=_from_key,
                                                                              to_key=_to_key,
                                                                              reverse=True,
                                                                              return_keys=False,
                                                                              return_values=True):
                session = self._schema.sessions[txn, session_oid]

                # if we have an index, that index must always resolve to an indexed record
                assert session

                # we only want the most recent session
                break

        if session:
            # extract info from database table to construct session details and return
            td = TransportDetails.parse(session.transport)
            sd = SessionDetails(
                realm=session.realm,
                session=session.session,
                authid=session.authid,
                authrole=session.authrole,
                authmethod=session.authmethod,
                authprovider=session.authprovider,
                authextra=session.authextra,
                # FIXME
                serializer=None,
                resumed=False,
                resumable=False,
                resume_token=None,
                transport=td)
            res = sd.marshal()
            return res
        else:
            return None
Example #12
0
    def __init__(self, pending_session_id: int,
                 transport_details: TransportDetails,
                 realm_container: IRealmContainer, config: Dict[str, Any]):
        """

        :param pending_session_id: The WAMP session ID if this authentication succeeds.
        :param transport_details: Transport details of the authenticating session.
        :param realm_container: Realm container (router or proxy) for access to configured realms and roles.
        :param config: Authentication configuration to apply for the pending auth.
        """
        # Details about the authenticating session
        self._session_details = {
            'transport': transport_details.marshal(),
            'session': pending_session_id,
            'authmethod': None,
            'authextra': None
        }

        # The router factory we are working for
        self._realm_container: IRealmContainer = realm_container

        # WAMP-Ticket configuration to apply for the pending auth
        self._config: Dict[str, Any] = config

        # The realm of the authenticating principal.
        self._realm: Optional[str] = None

        # The authentication ID of the authenticating principal.
        self._authid: Optional[str] = None

        # The role under which the principal will be authenticated when
        # the authentication succeeds.
        self._authrole: Optional[str] = None

        # Optional authentication provider (URI of procedure to call).
        self._authprovider: Optional[str] = None

        # The authentication method
        self._authmethod: str = self.AUTHMETHOD

        # Application-specific extra data forwarded to the authenticating client in WELCOME
        self._authextra: Optional[Dict[str, Any]] = None

        # The URI of the authenticator procedure to call (filled only in dynamic mode).
        self._authenticator: Optional[str] = None

        # The realm the (dynamic) authenticator itself is joined to
        self._authenticator_realm: Optional[str] = None

        # The session over which to issue the call to the authenticator (filled only in dynamic mode).
        self._authenticator_session: Optional[ISession] = None
Example #13
0
    def setUp(self):
        t = FakeTransport()
        f = WebSocketClientFactory()
        p = WebSocketClientProtocol()
        p.factory = f
        p.transport = t
        p._transport_details = TransportDetails()

        p._connectionMade()
        p.state = p.STATE_OPEN
        p.websocket_version = 18

        self.protocol = p
        self.transport = t
Example #14
0
    def __init__(self, handler):
        self._log = False
        self._handler = handler
        self._serializer = serializer.JsonSerializer()
        self._registrations = {}
        self._invocations = {}
        self._subscription_topics = {}
        self._my_session_id = util.id()
        self._transport_details = TransportDetails()

        self._handler.onOpen(self)

        roles = {'broker': role.RoleBrokerFeatures(), 'dealer': role.RoleDealerFeatures()}

        msg = message.Welcome(self._my_session_id, roles)
        self._handler.onMessage(msg)
Example #15
0
    def test_router_session_internal_error_onAuthenticate(self):
        """
        similar to above, but during _RouterSession's onMessage handling,
        where it calls self.onAuthenticate)
        """
        raise unittest.SkipTest('FIXME: Adjust unit test mocks #1567')

        # setup
        transport = mock.MagicMock()
        transport.transport_details = TransportDetails(
            channel_id={'tls-unique': b'deadbeef'})
        the_exception = RuntimeError("kerblam")

        def boom(*args, **kw):
            raise the_exception

        session = self.session_factory(
        )  # __call__ on the _RouterSessionFactory
        session.onAuthenticate = boom
        session.onOpen(transport)
        msg = message.Authenticate('bogus signature')

        # XXX think: why isn't this using _RouterSession.log?
        from crossbar.router.session import RouterSession
        with mock.patch.object(RouterSession, 'log') as logger:
            # do the test; should call onHello which is now "boom", above
            session.onMessage(msg)

            # check we got the right log.failure() call
            self.assertTrue(len(logger.method_calls) > 0)
            call = logger.method_calls[0]
            # for a MagicMock call-object, 0th thing is the method-name, 1st
            # thing is the arg-tuple, 2nd thing is the kwargs.
            self.assertEqual(call[0], 'failure')
            self.assertTrue('failure' in call[2])
            self.assertEqual(call[2]['failure'].value, the_exception)
Example #16
0
 def test_parse(self):
     td = TransportDetails.parse(TRANSPORT_DETAILS_1)
     data2 = td.marshal()
     self.maxDiff = None
     self.assertEqual(data2, TRANSPORT_DETAILS_1)
Example #17
0
    def test_publish_traced_events(self):
        """
        with two subscribers and message tracing the last event should
        have a magic flag
        """

        # we want to trigger a deeply-nested condition in
        # processPublish in class Broker -- lets try w/o refactoring
        # anything first...

        class TestSession(ApplicationSession):
            pass

        session0 = TestSession()
        session1 = TestSession()
        session2 = TestSession()
        router = mock.MagicMock()
        router.send = mock.Mock()
        router.new_correlation_id = lambda: 'fake correlation id'
        router.is_traced = True
        broker = Broker(router, reactor)

        # let's just "cheat" our way a little to the right state by
        # injecting our subscription "directly" (e.g. instead of
        # faking out an entire Subscribe etc. flow
        # ...so we need _subscriptions_map to have at least one
        # subscription (our test one) for the topic we'll publish to
        broker._subscription_map.add_observer(session0, 'test.topic')
        broker._subscription_map.add_observer(session1, 'test.topic')

        session0._session_id = 1000
        session0._transport = mock.MagicMock()
        session0._transport.transport_details = TransportDetails(
            channel_id={'tls-unique': b'deadbeef'})

        session1._session_id = 1001
        session1._transport = mock.MagicMock()
        session1._transport.transport_details = TransportDetails(
            channel_id={'tls-unique': b'deadbeef'})

        session2._session_id = 1002
        session2._transport = mock.MagicMock()
        session2._transport.transport_details = TransportDetails(
            channel_id={'tls-unique': b'deadbeef'})

        # here's the main "cheat"; we're faking out the
        # router.authorize because we need it to callback immediately
        router.authorize = mock.MagicMock(
            return_value=txaio.create_future_success(
                dict(allow=True, cache=False, disclose=True)))

        # now we scan call "processPublish" such that we get to the
        # condition we're interested in (this "comes from" session1
        # beacuse by default publishes don't go to the same session)
        pubmsg = message.Publish(123, 'test.topic')
        broker.processPublish(session2, pubmsg)

        # extract all the event calls
        events = [
            call[1][1] for call in router.send.mock_calls
            if call[1][0] in [session0, session1, session2]
        ]

        self.assertEqual(2, len(events))
        self.assertFalse(events[0].correlation_is_last)
        self.assertTrue(events[1].correlation_is_last)
 def __init__(self):
     self.messages = []
     self._transport_details = TransportDetails()
Example #19
0
 def __init__(self, factory, on_message, real_transport):
     self.factory = factory
     self.on_message = on_message
     self.transport = real_transport
     self.transport_details = TransportDetails()
     real_transport._transport_config = {'foo': 32}
Example #20
0
    def test_publish_traced_events_batched(self):
        """
        with two subscribers and message tracing the last event should
        have a magic flag
        """

        # we want to trigger a deeply-nested condition in
        # processPublish in class Broker -- lets try w/o refactoring
        # anything first...

        class TestSession(ApplicationSession):
            pass

        session0 = TestSession()
        session1 = TestSession()
        session2 = TestSession()
        session3 = TestSession()
        session4 = TestSession()
        # NOTE! We ensure that "session0" (the publishing session) is
        # *last* in the observation-list to trigger a (now fixed)
        # edge-case)
        sessions = [session1, session2, session3, session4, session0]
        router = mock.MagicMock()
        router.send = mock.Mock()
        router.new_correlation_id = lambda: 'fake correlation id'
        router.is_traced = True
        clock = Clock()
        with replace_loop(clock):
            broker = Broker(router, clock)
            broker._options.event_dispatching_chunk_size = 2

            # to ensure we get "session0" last, we turn on ordering in
            # the observations
            broker._subscription_map._ordered = 1

            # let's just "cheat" our way a little to the right state by
            # injecting our subscription "directly" (e.g. instead of
            # faking out an entire Subscribe etc. flow
            # ...so we need _subscriptions_map to have at least one
            # subscription (our test one) for the topic we'll publish to
            for session in sessions:
                broker._subscription_map.add_observer(session, 'test.topic')

            for i, sess in enumerate(sessions):
                sess._session_id = 1000 + i
                sess._transport = mock.MagicMock()
                sess._transport.transport_details = TransportDetails(
                    channel_id={'tls-unique': b'deadbeef'})

            # here's the main "cheat"; we're faking out the
            # router.authorize because we need it to callback immediately
            router.authorize = mock.MagicMock(
                return_value=txaio.create_future_success(
                    dict(allow=True, cache=False, disclose=True)))

            # now we scan call "processPublish" such that we get to the
            # condition we're interested in; should go to all sessions
            # except session0
            pubmsg = message.Publish(123, 'test.topic')
            broker.processPublish(session0, pubmsg)
            clock.advance(1)
            clock.advance(1)

            # extract all the event calls
            events = [
                call[1][1] for call in router.send.mock_calls if call[1][0] in
                [session0, session1, session2, session3, session4]
            ]

            # all except session0 should have gotten an event, and
            # session4's should have the "last" flag set
            self.assertEqual(4, len(events))
            self.assertFalse(events[0].correlation_is_last)
            self.assertFalse(events[1].correlation_is_last)
            self.assertFalse(events[2].correlation_is_last)
            self.assertTrue(events[3].correlation_is_last)