def test_jwt_token_uses_utc_time(self) -> None: # django server generates token using UTC timezone token = generate_jwt_token("foo", expiration=0) with patch.object(dt, "datetime", Mock(wraps=dt.datetime)) as patched_dt: # mock bokeh server localtime to be UTC + 10 patched_dt.now.return_value = dt.datetime.utcnow() + dt.timedelta(hours=10) payload = get_token_payload(token) utcnow = calendar.timegm(dt.datetime.utcnow().utctimetuple()) assert utcnow -1 <= payload['session_expiry'] <= utcnow + 1
def test_payload_with_zlib_key(self): token = generate_jwt_token(generate_session_id(), signed=False, extra_payload=dict([(_TOKEN_ZLIB_KEY, 10)])) assert '.' not in token assert _TOKEN_ZLIB_KEY in json.loads(_b64_to_utf8(token)) payload = get_token_payload(token) assert _TOKEN_ZLIB_KEY in payload assert payload[_TOKEN_ZLIB_KEY] == 10
def test_payload_unsigned(self): token = generate_jwt_token(generate_session_id(), signed=False, extra_payload=dict(foo=10)) assert '.' not in token assert _TOKEN_ZLIB_KEY in json.loads(_b64_to_utf8(token)) payload = get_token_payload(token) assert _TOKEN_ZLIB_KEY not in payload assert payload['foo'] == 10
async def test__exclude_cookies(ManagedServerLoop) -> None: application = Application() with ManagedServerLoop(application, exclude_cookies=['custom']) as server: sessions = server.get_sessions('/') assert 0 == len(sessions) response = await http_get(server.io_loop, url(server), headers={'Cookie': 'custom = test ; custom2 = test2'}) html = response.body token = extract_token_from_json(html) payload = get_token_payload(token) assert 'cookies' in payload assert payload['cookies'] == {'custom2': 'test2'}
async def test__exclude_headers(ManagedServerLoop) -> None: application = Application() with ManagedServerLoop(application, exclude_headers=['Connection', 'Host']) as server: sessions = server.get_sessions('/') assert 0 == len(sessions) response = await http_get(server.io_loop, url(server)) html = response.body token = extract_token_from_json(html) payload = get_token_payload(token) assert 'headers' in payload assert payload["headers"].get("Accept-Encoding") == "gzip"
async def test__include_headers(ManagedServerLoop) -> None: application = Application() with ManagedServerLoop(application, include_headers=['Custom']) as server: sessions = server.get_sessions('/') assert 0 == len(sessions) response = await http_get(server.io_loop, url(server), headers={'Custom': 'Test'}) html = response.body token = extract_token_from_json(html) payload = get_token_payload(token) assert 'headers' in payload assert payload['headers'] == {'Custom': 'Test'}
def test_payload_signed(self): session_id = generate_session_id(signed=True, secret_key="abc") token = generate_jwt_token(session_id, signed=True, secret_key="abc", extra_payload=dict(foo=10)) assert '.' in token decoded = json.loads(_b64_to_utf8(token.split('.')[0])) assert _TOKEN_ZLIB_KEY in decoded assert 'session_id' in decoded session_id = get_session_id(token) assert check_token_signature(token, secret_key="abc", signed=True) assert not check_token_signature(token, secret_key="qrs", signed=True) payload = get_token_payload(token) assert _TOKEN_ZLIB_KEY not in payload assert payload['foo'] == 10
async def connect(self): log.info('WebSocket connection opened') subprotocols = self.scope["subprotocols"] if len(subprotocols) != 2 or subprotocols[0] != 'bokeh': self.close() raise RuntimeError("Subprotocol header is not 'bokeh'") token = subprotocols[1] if token is None: self.close() raise RuntimeError("No token received in subprotocol header") now = calendar.timegm(dt.datetime.now().utctimetuple()) payload = get_token_payload(token) if 'session_expiry' not in payload: self.close() raise RuntimeError("Session expiry has not been provided") elif now >= payload['session_expiry']: self.close() raise RuntimeError("Token is expired.") elif not check_token_signature(token, signed=False, secret_key=None): session_id = get_session_id(token) log.error("Token for session %r had invalid signature", session_id) raise RuntimeError("Invalid token signature") def on_fully_opened(future): e = future.exception() if e is not None: # this isn't really an error (unless we have a # bug), it just means a client disconnected # immediately, most likely. log.debug("Failed to fully open connlocksection %r", e) future = self._async_open(token) # rewrite above line using asyncio # this task is scheduled to run soon once context is back to event loop task = asyncio.ensure_future(future) task.add_done_callback(on_fully_opened) await self.accept("bokeh")
def open(self): ''' Initialize a connection to a client. Returns: None ''' log.info('WebSocket connection opened') token = self._token if self.selected_subprotocol != 'bokeh': self.close() raise ProtocolError("Subprotocol header is not 'bokeh'") elif token is None: self.close() raise ProtocolError("No token received in subprotocol header") now = calendar.timegm(dt.datetime.utcnow().utctimetuple()) payload = get_token_payload(token) if 'session_expiry' not in payload: self.close() raise ProtocolError("Session expiry has not been provided") elif now >= payload['session_expiry']: self.close() raise ProtocolError("Token is expired.") elif not check_token_signature(token, signed=self.application.sign_sessions, secret_key=self.application.secret_key): session_id = get_session_id(token) log.error("Token for session %r had invalid signature", session_id) raise ProtocolError("Invalid token signature") try: self.application.io_loop.spawn_callback(self._async_open, self._token) except Exception as e: # this isn't really an error (unless we have a # bug), it just means a client disconnected # immediately, most likely. log.debug("Failed to fully open connection %r", e)