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)
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, [])
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)
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)
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)
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])
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)
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()
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)
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
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
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
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)
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)
def test_parse(self): td = TransportDetails.parse(TRANSPORT_DETAILS_1) data2 = td.marshal() self.maxDiff = None self.assertEqual(data2, TRANSPORT_DETAILS_1)
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()
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}
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)