def test_versioned(self, time): time.return_value = 1000 message = "hello!" max_age = datetime.timedelta(seconds=30) versioned = VersionedSecret(previous=b"one", current=b"two", next=b"three") previous = VersionedSecret.from_simple_secret(versioned.previous) current = VersionedSecret.from_simple_secret(versioned.current) next = VersionedSecret.from_simple_secret(versioned.next) self.assertEqual(crypto.make_signature(versioned, message, max_age), crypto.make_signature(current, message, max_age)) signature = crypto.make_signature(previous, message, max_age) info = crypto.validate_signature(versioned, message, signature) self.assertEqual(info.expiration, 1030) signature = crypto.make_signature(current, message, max_age) info = crypto.validate_signature(versioned, message, signature) self.assertEqual(info.expiration, 1030) signature = crypto.make_signature(next, message, max_age) info = crypto.validate_signature(versioned, message, signature) self.assertEqual(info.expiration, 1030)
def test_new_csrf_token(self, time_mock): time_mock.time.return_value = 1000.0 request = mock.Mock() request.authenticated_userid = "t2_1" token = self.policy.new_csrf_token(request) self.assertTrue(token.startswith("1.")) self.assertEqual(token, "1.AQAA-BEAAF-br-ovnk0q8Wd0kA98-jsak9elbMqo0WbjT0GuyRTD") signature = token.split('.')[-1] validate_signature(self.policy._get_secret(), "1.t2_1", signature)
def upgrade_connection(self): """Validate authorization to attach to a namespace before connecting. We hook into the geventwebsocket stuff here to ensure that the authorization is valid before we return a `101 Switching Protocols` rather than connecting then disconnecting the user. """ if not self.client_address: # we can't do websockets if we don't have a valid client_address # because geventwebsocket uses that to keep connection objects. # if this error is happening, you probably need to update your proxy raise Exception("no client address. check x-forwarded-{for,port}") app = self.application # Check if compression is supported. The RFC explanation for the # variations on what is accepted here is convoluted, so we'll just # stick with the happy case: # # https://tools.ietf.org/html/rfc6455#page-48 # # Worst case scenario, we fall back to not compressing. extensions = self.environ.get('HTTP_SEC_WEBSOCKET_EXTENSIONS', '') extensions = { extension.split(";")[0].strip() for extension in extensions.split(",") } self.environ["supports_compression"] = \ "permessage-deflate" in extensions try: namespace = self.environ["PATH_INFO"] query_string = self.environ["QUERY_STRING"] params = urlparse.parse_qs(query_string, strict_parsing=True) signature = params["m"][0] secret = app.secrets.get_versioned( "secret/websockets/authorization_key") validate_signature(secret, namespace, signature) except (KeyError, IndexError, ValueError, SignatureError): app.metrics.counter("conn.rejected.bad_namespace").increment() self.start_response("403 Forbidden", []) return ["Forbidden"] self.environ["signature_validated"] = True return super(WebSocketHandler, self).upgrade_connection()
def check_csrf_token(self, request, supplied_token): """Return True if the supplied_token is valid. This is called automatically by Pyramid if you have configured it to require CSRF. """ try: token_version, sep, signature = supplied_token.partition(".") except Exception: return False if sep != ".": return False _, payload = _make_csrf_token_payload( version=token_version, account_id=request.authenticated_userid) try: validate_signature(self._get_secret(), payload, signature) except SignatureError: return False return True