async def test_event_hubs_send_override_token_refresh_window( live_eventhub_config): uri = "sb://{}/{}".format(live_eventhub_config['hostname'], live_eventhub_config['event_hub']) target = "amqps://{}/{}/Partitions/0".format( live_eventhub_config['hostname'], live_eventhub_config['event_hub']) token = None async def get_token(): nonlocal token return _AccessToken(token, expiry) jwt_auth = authentication.JWTTokenAsync( uri, uri, get_token, refresh_window=300 # set refresh window to be 5 mins ) send_client = uamqp.SendClientAsync(target, auth=jwt_auth, debug=False) # use token of which the valid remaining time < refresh window expiry = int(time.time()) + (60 * 4 + 30) # 4.5 minutes token = utils.create_sas_token(live_eventhub_config['key_name'].encode(), live_eventhub_config['access_key'].encode(), uri.encode(), expiry=timedelta(minutes=4, seconds=30)) for _ in range(3): message = uamqp.message.Message(body='Hello World') await send_client.send_message_async(message) auth_status = constants.CBSAuthStatus(jwt_auth._cbs_auth.get_status()) assert auth_status == constants.CBSAuthStatus.RefreshRequired # update token, the valid remaining time > refresh window expiry = int(time.time()) + (60 * 5 + 30) # 5.5 minutes token = utils.create_sas_token(live_eventhub_config['key_name'].encode(), live_eventhub_config['access_key'].encode(), uri.encode(), expiry=timedelta(minutes=5, seconds=30)) for _ in range(3): message = uamqp.message.Message(body='Hello World') await send_client.send_message_async(message) auth_status = constants.CBSAuthStatus(jwt_auth._cbs_auth.get_status()) assert auth_status == constants.CBSAuthStatus.Ok await send_client.close_async()
def from_shared_access_key( cls, uri, key_name, shared_access_key, expiry=None, port=constants.DEFAULT_AMQPS_PORT, timeout=10, retry_policy=TokenRetryPolicy(), verify=None, encoding='UTF-8'): """Attempt to create a CBS token session using a Shared Access Key such as is used to connect to Azure services. :param uri: The AMQP endpoint URI. This must be provided as a decoded string. :type uri: str :param key_name: The SAS token username, also referred to as the key name or policy name. :type key_name: str :param shared_access_key: The SAS token password, also referred to as the key. :type shared_access_key: str :param expiry: The lifetime in seconds for the generated token. Default is 1 hour. :type expiry: int :param port: The TLS port - default for AMQP is 5671. :type port: int :param timeout: The timeout in seconds in which to negotiate the token. The default value is 10 seconds. :type timeout: int :param retry_policy: The retry policy for the PUT token request. The default retry policy has 3 retries. :type retry_policy: ~uamqp.authentication.TokenRetryPolicy :param verify: The path to a user-defined certificate. :type verify: str :param encoding: The encoding to use if hostname is provided as a str. Default is 'UTF-8'. :type encoding: str """ expires_in = datetime.timedelta(seconds=expiry or constants.AUTH_EXPIRATION_SECS) encoded_uri = urllib_parse.quote_plus(uri).encode(encoding) # pylint: disable=no-member encoded_key = urllib_parse.quote_plus(key_name).encode(encoding) # pylint: disable=no-member expires_at = time.time() + expires_in.seconds token = utils.create_sas_token( encoded_key, shared_access_key.encode(encoding), encoded_uri, expires_in) return cls( uri, uri, token, expires_in=expires_in, expires_at=expires_at, username=key_name, password=shared_access_key, port=port, timeout=timeout, retry_policy=retry_policy, verify=verify, encoding=encoding)
def update_token(self): """If a username and password are present - attempt to use them to request a fresh SAS token. """ if not self.username or not self.password: raise errors.TokenExpired( "Unable to refresh token - no username or password.") encoded_uri = compat.quote_plus(self.uri).encode(self._encoding) # pylint: disable=no-member encoded_key = compat.quote_plus(self.username).encode(self._encoding) # pylint: disable=no-member self.expires_at = time.time() + self.expires_in.seconds self.token = utils.create_sas_token( encoded_key, self.password.encode(self._encoding), encoded_uri, self.expires_in)
def _generate_sas_token(uri, policy, key, expiry=None): # type: (str, str, str, Optional[timedelta]) -> _AccessToken """Create a shared access signiture token as a string literal. :returns: SAS token as string literal. :rtype: str """ if not expiry: expiry = timedelta(hours=1) # Default to 1 hour. abs_expiry = int(time.time()) + expiry.seconds encoded_uri = quote_plus(uri).encode("utf-8") # pylint: disable=no-member encoded_policy = quote_plus(policy).encode("utf-8") # pylint: disable=no-member encoded_key = key.encode("utf-8") token = utils.create_sas_token(encoded_policy, encoded_key, encoded_uri, expiry) return _AccessToken(token=token, expires_on=abs_expiry)
def from_shared_access_key(cls, uri, key_name, shared_access_key, expiry=None, port=constants.DEFAULT_AMQPS_PORT, timeout=10, retry_policy=TokenRetryPolicy(), verify=None, http_proxy=None, transport_type=TransportType.Amqp, encoding='UTF-8'): """Attempt to create a CBS token session using a Shared Access Key such as is used to connect to Azure services. :param uri: The AMQP endpoint URI. This must be provided as a decoded string. :type uri: str :param key_name: The SAS token username, also referred to as the key name or policy name. :type key_name: str :param shared_access_key: The SAS token password, also referred to as the key. :type shared_access_key: str :param expiry: The lifetime in seconds for the generated token. Default is 1 hour. :type expiry: int :param port: The TLS port - default for AMQP is 5671. :type port: int :param timeout: The timeout in seconds in which to negotiate the token. The default value is 10 seconds. :type timeout: float :param retry_policy: The retry policy for the PUT token request. The default retry policy has 3 retries. :type retry_policy: ~uamqp.authentication.cbs_auth.TokenRetryPolicy :param verify: The path to a user-defined certificate. :type verify: str :param http_proxy: HTTP proxy configuration. This should be a dictionary with the following keys present: 'proxy_hostname' and 'proxy_port'. Additional optional keys are 'username' and 'password'. :type http_proxy: dict :param transport_type: The transport protocol type - default is ~uamqp.TransportType.Amqp. ~uamqp.TransportType.AmqpOverWebsocket is applied when http_proxy is set or the transport type is explicitly requested. :type transport_type: ~uamqp.TransportType :param encoding: The encoding to use if hostname is provided as a str. Default is 'UTF-8'. :type encoding: str """ expires_in = datetime.timedelta( seconds=expiry or constants.AUTH_EXPIRATION_SECS) encoded_uri = compat.quote_plus(uri).encode(encoding) # pylint: disable=no-member encoded_key = compat.quote_plus(key_name).encode(encoding) # pylint: disable=no-member expires_at = time.time() + expires_in.seconds token = utils.create_sas_token(encoded_key, shared_access_key.encode(encoding), encoded_uri, expires_in) return cls(uri, uri, token, expires_in=expires_in, expires_at=expires_at, username=key_name, password=shared_access_key, port=port, timeout=timeout, retry_policy=retry_policy, verify=verify, http_proxy=http_proxy, transport_type=transport_type, encoding=encoding)