async def test_toke_details_from_json(self): token_details = await self.ably.auth.request_token() token_details_dict = token_details.to_dict() token_details_str = json.dumps(token_details_dict) assert token_details == TokenDetails.from_json(token_details_dict) assert token_details == TokenDetails.from_json(token_details_str)
def test_toke_details_from_json(self): token_details = self.ably.auth.request_token() token_details_dict = token_details.to_dict() token_details_str = json.dumps(token_details_dict) assert token_details == TokenDetails.from_json(token_details_dict) assert token_details == TokenDetails.from_json(token_details_str)
def __init__(self, ably, options): self.__ably = ably self.__auth_options = options if options.token_details: self.__client_id = options.token_details.client_id else: self.__client_id = options.client_id self.__client_id_validated = False self.__basic_credentials = None self.__auth_params = None self.__token_details = None must_use_token_auth = options.use_token_auth is True must_not_use_token_auth = options.use_token_auth is False can_use_basic_auth = options.key_secret is not None and options.client_id is None if not must_use_token_auth and can_use_basic_auth: # We have the key, no need to authenticate the client # default to using basic auth log.debug("anonymous, using basic auth") self.__auth_mechanism = Auth.Method.BASIC basic_key = "%s:%s" % (options.key_name, options.key_secret) basic_key = base64.b64encode(basic_key.encode('utf-8')) self.__basic_credentials = basic_key.decode('ascii') return elif must_not_use_token_auth and not can_use_basic_auth: raise ValueError( 'If use_token_auth is False you must provide a key') # Using token auth self.__auth_mechanism = Auth.Method.TOKEN if options.token_details: self.__token_details = options.token_details elif options.auth_token: self.__token_details = TokenDetails(token=options.auth_token) else: self.__token_details = None if options.auth_callback: log.debug("using token auth with auth_callback") elif options.auth_url: log.debug("using token auth with auth_url") elif options.key_secret: log.debug("using token auth with client-side signing") elif options.auth_token: log.debug("using token auth with supplied token only") elif options.token_details: log.debug("using token auth with supplied token_details") else: raise ValueError( "Can't authenticate via token, must provide " "auth_callback, auth_url, key, token or a TokenDetail")
def test_toke_details_from_json(self): token_details = self.ably.auth.request_token() token_details_dict = token_details.to_dict() token_details_str = json.dumps(token_details_dict) self.assertEqual( token_details, TokenDetails.from_json(token_details_dict), ) self.assertEqual( token_details, TokenDetails.from_json(token_details_str), )
def __init__(self, ably, options): self.__ably = ably self.__auth_options = options if options.token_details: self.__client_id = options.token_details.client_id else: self.__client_id = options.client_id self.__client_id_validated = False self.__basic_credentials = None self.__auth_params = None self.__token_details = None must_use_token_auth = options.use_token_auth is True must_not_use_token_auth = options.use_token_auth is False can_use_basic_auth = options.key_secret is not None and options.client_id is None if not must_use_token_auth and can_use_basic_auth: # We have the key, no need to authenticate the client # default to using basic auth log.debug("anonymous, using basic auth") self.__auth_mechanism = Auth.Method.BASIC basic_key = "%s:%s" % (options.key_name, options.key_secret) basic_key = base64.b64encode(basic_key.encode('utf-8')) self.__basic_credentials = basic_key.decode('ascii') return elif must_not_use_token_auth and not can_use_basic_auth: raise ValueError('If use_token_auth is False you must provide a key') # Using token auth self.__auth_mechanism = Auth.Method.TOKEN if options.token_details: self.__token_details = options.token_details elif options.auth_token: self.__token_details = TokenDetails(token=options.auth_token) else: self.__token_details = None if options.auth_callback: log.debug("using token auth with auth_callback") elif options.auth_url: log.debug("using token auth with auth_url") elif options.key_secret: log.debug("using token auth with client-side signing") elif options.auth_token: log.debug("using token auth with supplied token only") elif options.token_details: log.debug("using token auth with supplied token_details") else: raise ValueError("Can't authenticate via token, must provide " "auth_callback, auth_url, key, token or a TokenDetail")
def test_when_not_renewable_with_token_details(self): token_details = TokenDetails(token='a_dummy_token') self.ably = RestSetup.get_ably_rest(key=None, token_details=token_details, use_binary_protocol=False) self.ably.channels[self.channel].publish('evt', 'msg') assert 1 == self.publish_attempts publish = self.ably.channels[self.channel].publish match = "The provided token is not renewable and there is no means to generate a new token" with pytest.raises(AblyAuthException, match=match): publish('evt', 'msg') assert 0 == self.token_requests
async def test_when_not_renewable_with_token_details(self): token_details = TokenDetails(token='a_dummy_token') self.ably = await RestSetup.get_ably_rest(key=None, token_details=token_details, use_binary_protocol=False) await self.ably.channels[self.channel].publish('evt', 'msg') assert self.mocked_api["publish_attempt_route"].call_count == 1 publish = self.ably.channels[self.channel].publish match = "The provided token is not renewable and there is no means to generate a new token" with pytest.raises(AblyAuthException, match=match): await publish('evt', 'msg') assert not self.mocked_api["request_token_route"].called
def test_when_not_renewable_with_token_details(self): token_details = TokenDetails(token='a_dummy_token') self.ably = AblyRest(token_details=token_details, rest_host=test_vars["host"], port=test_vars["port"], tls_port=test_vars["tls_port"], tls=test_vars["tls"], use_binary_protocol=False) self.ably.channels[self.channel].publish('evt', 'msg') self.assertEquals(1, self.publish_attempts) publish = self.ably.channels[self.channel].publish self.assertRaisesRegexp( AblyAuthException, "The provided token is not renewable and there is" " no means to generate a new token", publish, 'evt', 'msg') self.assertEquals(0, self.token_requests)
def request_token( self, token_params=None, # auth_options key_name=None, key_secret=None, auth_callback=None, auth_url=None, auth_method=None, auth_headers=None, auth_params=None, query_time=None): token_params = token_params or {} token_params = dict(self.auth_options.default_token_params, **token_params) key_name = key_name or self.auth_options.key_name key_secret = key_secret or self.auth_options.key_secret log.debug("Auth callback: %s" % auth_callback) log.debug("Auth options: %s" % six.text_type(self.auth_options)) if query_time is None: query_time = self.auth_options.query_time query_time = bool(query_time) auth_callback = auth_callback or self.auth_options.auth_callback auth_url = auth_url or self.auth_options.auth_url auth_params = auth_params or self.auth_options.auth_params or {} auth_method = (auth_method or self.auth_options.auth_method).upper() auth_headers = auth_headers or self.auth_options.auth_headers or {} log.debug("Token Params: %s" % token_params) if auth_callback: log.debug("using token auth with authCallback") token_request = auth_callback(token_params) elif auth_url: log.debug("using token auth with authUrl") token_request = self.token_request_from_auth_url( auth_method, auth_url, token_params, auth_headers, auth_params) else: token_request = self.create_token_request(token_params, key_name=key_name, key_secret=key_secret, query_time=query_time) if isinstance(token_request, TokenDetails): return token_request elif isinstance(token_request, dict) and 'issued' in token_request: return TokenDetails.from_dict(token_request) elif isinstance(token_request, dict): token_request = TokenRequest(**token_request) elif isinstance(token_request, six.text_type): return TokenDetails(token=token_request) # python2 elif isinstance(token_request, six.binary_type) and six.binary_type == str: return TokenDetails(token=token_request) token_path = "/keys/%s/requestToken" % token_request.key_name response = self.ably.http.post(token_path, headers=auth_headers, native_data=token_request.to_dict(), skip_auth=True) AblyException.raise_for_response(response) response_dict = response.to_native() log.debug("Token: %s" % str(response_dict.get("token"))) return TokenDetails.from_dict(response_dict)
def test_auth_token_details(self): td = TokenDetails() ably = AblyRest(token_details=td) assert Auth.Method.TOKEN == ably.auth.auth_mechanism assert ably.auth.token_details is td
def callback(token_params): assert token_params == called_token_params return TokenDetails(token='another_token_string')
def test_auth_token_details(self): td = TokenDetails() ably = AblyRest(token_details=td) self.assertEqual(Auth.Method.TOKEN, ably.auth.auth_mechanism) self.assertIs(ably.auth.token_details, td)
def test_with_token_details(self): td = TokenDetails() ably = AblyRest(token_details=td) self.assertIs(ably.options.token_details, td)
def test_with_token_details(self): td = TokenDetails() ably = AblyRest(token_details=td) assert ably.options.token_details is td
class Auth(object): class Method: BASIC = "BASIC" TOKEN = "TOKEN" def __init__(self, ably, options): self.__ably = ably self.__auth_options = options self.__client_id = options.client_id self.__client_id_validated = False self.__basic_credentials = None self.__auth_params = None self.__token_details = None must_use_token_auth = options.use_token_auth is True must_not_use_token_auth = options.use_token_auth is False can_use_basic_auth = options.key_secret is not None and options.client_id is None if not must_use_token_auth and can_use_basic_auth: # We have the key, no need to authenticate the client # default to using basic auth log.debug("anonymous, using basic auth") self.__auth_mechanism = Auth.Method.BASIC basic_key = "%s:%s" % (options.key_name, options.key_secret) basic_key = base64.b64encode(basic_key.encode('utf-8')) self.__basic_credentials = basic_key.decode('ascii') return elif must_not_use_token_auth and not can_use_basic_auth: raise ValueError( 'If use_token_auth is False you must provide a key') # Using token auth self.__auth_mechanism = Auth.Method.TOKEN if options.token_details: self.__token_details = options.token_details elif options.auth_token: self.__token_details = TokenDetails(token=options.auth_token) else: self.__token_details = None if options.auth_callback: log.debug("using token auth with auth_callback") elif options.auth_url: log.debug("using token auth with auth_url") elif options.key_secret: log.debug("using token auth with client-side signing") elif options.auth_token: log.debug("using token auth with supplied token only") elif options.token_details: log.debug("using token auth with supplied token_details") else: raise ValueError( "Can't authenticate via token, must provide " "auth_callback, auth_url, key, token or a TokenDetail") def authorise(self, token_params=None, auth_options=None, force=False): self.__auth_mechanism = Auth.Method.TOKEN if token_params is None: token_params = dict(self.auth_options.default_token_params) else: token_params = dict(self.auth_options.default_token_params, **token_params) self.auth_options.default_token_params = dict(token_params) self.auth_options.default_token_params.pop('timestamp', None) if auth_options is not None: force = auth_options.pop('force', None) or force self.auth_options.merge(auth_options) auth_options = dict(self.auth_options.auth_options) token_params.setdefault('client_id', self.client_id) if self.__token_details: if not self.__token_details.is_expired(self._timestamp()): if not force: log.debug("using cached token; expires = %d", self.__token_details.expires) return self.__token_details else: # token has expired self.__token_details = None self.__token_details = self.request_token(token_params, **auth_options) self._configure_client_id(self.__token_details.client_id) return self.__token_details def request_token( self, token_params=None, # auth_options key_name=None, key_secret=None, auth_callback=None, auth_url=None, auth_method=None, auth_headers=None, auth_params=None, query_time=None): token_params = token_params or {} token_params = dict(self.auth_options.default_token_params, **token_params) key_name = key_name or self.auth_options.key_name key_secret = key_secret or self.auth_options.key_secret log.debug("Auth callback: %s" % auth_callback) log.debug("Auth options: %s" % six.text_type(self.auth_options)) if query_time is None: query_time = self.auth_options.query_time query_time = bool(query_time) auth_callback = auth_callback or self.auth_options.auth_callback auth_url = auth_url or self.auth_options.auth_url auth_params = auth_params or self.auth_options.auth_params or {} auth_method = (auth_method or self.auth_options.auth_method).upper() auth_headers = auth_headers or self.auth_options.auth_headers or {} log.debug("Token Params: %s" % token_params) if auth_callback: log.debug("using token auth with authCallback") token_request = auth_callback(token_params) elif auth_url: log.debug("using token auth with authUrl") token_request = self.token_request_from_auth_url( auth_method, auth_url, token_params, auth_headers, auth_params) else: token_request = self.create_token_request(token_params, key_name=key_name, key_secret=key_secret, query_time=query_time) if isinstance(token_request, TokenDetails): return token_request elif isinstance(token_request, dict) and 'issued' in token_request: return TokenDetails.from_dict(token_request) elif isinstance(token_request, dict): token_request = TokenRequest(**token_request) elif isinstance(token_request, six.text_type): return TokenDetails(token=token_request) # python2 elif isinstance(token_request, six.binary_type) and six.binary_type == str: return TokenDetails(token=token_request) token_path = "/keys/%s/requestToken" % token_request.key_name response = self.ably.http.post(token_path, headers=auth_headers, native_data=token_request.to_dict(), skip_auth=True) AblyException.raise_for_response(response) response_dict = response.to_native() log.debug("Token: %s" % str(response_dict.get("token"))) return TokenDetails.from_dict(response_dict) def create_token_request(self, token_params=None, key_name=None, key_secret=None, query_time=None): token_params = token_params or {} token_request = {} key_name = key_name or self.auth_options.key_name key_secret = key_secret or self.auth_options.key_secret if not key_name or not key_secret: log.debug('key_name or key_secret blank') raise AblyException( "No key specified: no means to generate a token", 401, 40101) token_request['key_name'] = key_name if token_params.get('timestamp'): token_request['timestamp'] = token_params['timestamp'] else: if query_time is None: query_time = self.auth_options.query_time if query_time: token_request['timestamp'] = self.ably.time() else: token_request['timestamp'] = self._timestamp() token_request['timestamp'] = int(token_request['timestamp']) token_request['ttl'] = token_params.get( 'ttl') or TokenDetails.DEFAULTS['ttl'] if token_params.get('capability') is None: token_request["capability"] = "" else: token_request['capability'] = six.text_type( Capability(token_params['capability'])) token_request["client_id"] = (token_params.get('client_id') or self.client_id) # Note: There is no expectation that the client # specifies the nonce; this is done by the library # However, this can be overridden by the client # simply for testing purposes token_request["nonce"] = token_params.get( 'nonce') or self._random_nonce() token_request = TokenRequest(**token_request) if token_params.get('mac') is None: # Note: There is no expectation that the client # specifies the mac; this is done by the library # However, this can be overridden by the client # simply for testing purposes. token_request.sign_request(key_secret.encode('utf8')) else: token_request.mac = token_params['mac'] return token_request @property def ably(self): return self.__ably @property def auth_mechanism(self): return self.__auth_mechanism @property def auth_options(self): return self.__auth_options @property def auth_params(self): return self.__auth_params @property def basic_credentials(self): return self.__basic_credentials @property def token_credentials(self): if self.__token_details: token = self.__token_details.token token_key = base64.b64encode(token.encode('utf-8')) return token_key.decode('ascii') @property def token_details(self): return self.__token_details @property def client_id(self): return self.__client_id def _configure_client_id(self, new_client_id): # If new client ID from Ably is a wildcard, but preconfigured clientId is set, # then keep the existing clientId if self.client_id != '*' and new_client_id == '*': self.__client_id_validated = True return # If client_id is defined and not a wildcard, prevent it changing, this is not supported if self.client_id is not None and self.client_id != '*' and new_client_id != self.client_id: raise IncompatibleClientIdException( "Client ID is immutable once configured for a client. " "Client ID cannot be changed to '{}'".format(new_client_id), 400, 40012) self.__client_id_validated = True self.__client_id = new_client_id def can_assume_client_id(self, assumed_client_id): if self.__client_id_validated: return self.client_id == '*' or self.client_id == assumed_client_id elif self.client_id is None or self.client_id == '*': return True # client ID is unknown else: return self.client_id == assumed_client_id def _get_auth_headers(self): if self.__auth_mechanism == Auth.Method.BASIC: return { 'Authorization': 'Basic %s' % self.basic_credentials, } else: self.authorise() return { 'Authorization': 'Bearer %s' % self.token_credentials, } def _timestamp(self): """Returns the local time in milliseconds since the unix epoch""" return int(time.time() * 1000) def _random_nonce(self): return uuid.uuid4().hex[:16] def token_request_from_auth_url(self, method, url, token_params, headers, auth_params): if method == 'GET': body = {} params = dict(auth_params, **token_params) elif method == 'POST': params = {} body = dict(auth_params, **token_params) from ably.http.http import Response response = Response( requests.request(method, url, headers=headers, params=params, data=body)) AblyException.raise_for_response(response) try: token_request = response.to_native() except ValueError: token_request = response.text return token_request
def callback(token_params): self.assertEquals(token_params, called_token_params) return TokenDetails(token='another_token_string')
def request_token(self, token_params=None, # auth_options key_name=None, key_secret=None, auth_callback=None, auth_url=None, auth_method=None, auth_headers=None, auth_params=None, query_time=None): token_params = token_params or {} token_params = dict(self.auth_options.default_token_params, **token_params) key_name = key_name or self.auth_options.key_name key_secret = key_secret or self.auth_options.key_secret log.debug("Auth callback: %s" % auth_callback) log.debug("Auth options: %s" % six.text_type(self.auth_options)) if query_time is None: query_time = self.auth_options.query_time query_time = bool(query_time) auth_callback = auth_callback or self.auth_options.auth_callback auth_url = auth_url or self.auth_options.auth_url auth_params = auth_params or self.auth_options.auth_params or {} auth_method = (auth_method or self.auth_options.auth_method).upper() auth_headers = auth_headers or self.auth_options.auth_headers or {} log.debug("Token Params: %s" % token_params) if auth_callback: log.debug("using token auth with authCallback") token_request = auth_callback(token_params) elif auth_url: log.debug("using token auth with authUrl") token_request = self.token_request_from_auth_url( auth_method, auth_url, token_params, auth_headers, auth_params) else: token_request = self.create_token_request( token_params, key_name=key_name, key_secret=key_secret, query_time=query_time) if isinstance(token_request, TokenDetails): return token_request elif isinstance(token_request, dict) and 'issued' in token_request: return TokenDetails.from_dict(token_request) elif isinstance(token_request, dict): token_request = TokenRequest(**token_request) elif isinstance(token_request, six.text_type): return TokenDetails(token=token_request) # python2 elif isinstance(token_request, six.binary_type) and six.binary_type == str: return TokenDetails(token=token_request) token_path = "/keys/%s/requestToken" % token_request.key_name response = self.ably.http.post( token_path, headers=auth_headers, native_data=token_request.to_dict(), skip_auth=True ) AblyException.raise_for_response(response) response_dict = response.to_native() log.debug("Token: %s" % str(response_dict.get("token"))) return TokenDetails.from_dict(response_dict)
def request_token(self, key_id=None, key_value=None, query_time=None, auth_token=None, auth_callback=None, auth_url=None, auth_headers=None, auth_params=None, token_params=None): key_id = key_id or self.auth_options.key_id key_value = key_value or self.auth_options.key_value log.debug("Auth callback: %s" % auth_callback) log.debug("Auth options: %s" % six.text_type(self.auth_options)) query_time = bool(query_time) auth_token = auth_token or self.auth_options.auth_token auth_callback = auth_callback or self.auth_options.auth_callback auth_url = auth_url or self.auth_options.auth_url auth_headers = auth_headers or { "Content-Encoding": "utf-8", "Content-Type": "application/json", } auth_params = auth_params or self.auth_params token_params = token_params or {} token_params.setdefault("client_id", self.ably.client_id) signed_token_request = "" log.debug("Token Params: %s" % token_params) if auth_callback: log.debug("using token auth with authCallback") signed_token_request = auth_callback(**token_params) elif auth_url: log.debug("using token auth with authUrl") response = self.ably.http.post( auth_url, headers=auth_headers, body=json.dumps(token_params), skip_auth=True ) AblyException.raise_for_response(response) signed_token_request = response.text elif key_value: log.debug("using token auth with client-side signing") signed_token_request = self.create_token_request( key_id=key_id, key_value=key_value, query_time=query_time, token_params=token_params) else: log.debug('No auth_callback, auth_url or key_value specified') raise AblyException( "Auth.request_token() must include valid auth parameters", 400, 40000) token_path = "/keys/%s/requestToken" % key_id response = self.ably.http.post( token_path, headers=auth_headers, body=signed_token_request, skip_auth=True ) AblyException.raise_for_response(response) access_token = response.json()["access_token"] log.debug("Token: %s" % str(access_token)) return TokenDetails.from_dict(access_token)
class Auth(object): class Method: BASIC = "BASIC" TOKEN = "TOKEN" def __init__(self, ably, options): self.__ably = ably self.__auth_options = options self.__client_id = options.client_id self.__client_id_validated = False self.__basic_credentials = None self.__auth_params = None self.__token_details = None must_use_token_auth = options.use_token_auth is True must_not_use_token_auth = options.use_token_auth is False can_use_basic_auth = options.key_secret is not None and options.client_id is None if not must_use_token_auth and can_use_basic_auth: # We have the key, no need to authenticate the client # default to using basic auth log.debug("anonymous, using basic auth") self.__auth_mechanism = Auth.Method.BASIC basic_key = "%s:%s" % (options.key_name, options.key_secret) basic_key = base64.b64encode(basic_key.encode('utf-8')) self.__basic_credentials = basic_key.decode('ascii') return elif must_not_use_token_auth and not can_use_basic_auth: raise ValueError('If use_token_auth is False you must provide a key') # Using token auth self.__auth_mechanism = Auth.Method.TOKEN if options.token_details: self.__token_details = options.token_details elif options.auth_token: self.__token_details = TokenDetails(token=options.auth_token) else: self.__token_details = None if options.auth_callback: log.debug("using token auth with auth_callback") elif options.auth_url: log.debug("using token auth with auth_url") elif options.key_secret: log.debug("using token auth with client-side signing") elif options.auth_token: log.debug("using token auth with supplied token only") elif options.token_details: log.debug("using token auth with supplied token_details") else: raise ValueError("Can't authenticate via token, must provide " "auth_callback, auth_url, key, token or a TokenDetail") def authorise(self, token_params=None, auth_options=None, force=False): self.__auth_mechanism = Auth.Method.TOKEN if token_params is None: token_params = dict(self.auth_options.default_token_params) else: token_params = dict(self.auth_options.default_token_params, **token_params) self.auth_options.default_token_params = dict(token_params) self.auth_options.default_token_params.pop('timestamp', None) if auth_options is not None: force = auth_options.pop('force', None) or force self.auth_options.merge(auth_options) auth_options = dict(self.auth_options.auth_options) token_params.setdefault('client_id', self.client_id) if self.__token_details: if not self.__token_details.is_expired(self._timestamp()): if not force: log.debug( "using cached token; expires = %d", self.__token_details.expires ) return self.__token_details else: # token has expired self.__token_details = None self.__token_details = self.request_token(token_params, **auth_options) self._configure_client_id(self.__token_details.client_id) return self.__token_details def request_token(self, token_params=None, # auth_options key_name=None, key_secret=None, auth_callback=None, auth_url=None, auth_method=None, auth_headers=None, auth_params=None, query_time=None): token_params = token_params or {} token_params = dict(self.auth_options.default_token_params, **token_params) key_name = key_name or self.auth_options.key_name key_secret = key_secret or self.auth_options.key_secret log.debug("Auth callback: %s" % auth_callback) log.debug("Auth options: %s" % six.text_type(self.auth_options)) if query_time is None: query_time = self.auth_options.query_time query_time = bool(query_time) auth_callback = auth_callback or self.auth_options.auth_callback auth_url = auth_url or self.auth_options.auth_url auth_params = auth_params or self.auth_options.auth_params or {} auth_method = (auth_method or self.auth_options.auth_method).upper() auth_headers = auth_headers or self.auth_options.auth_headers or {} log.debug("Token Params: %s" % token_params) if auth_callback: log.debug("using token auth with authCallback") token_request = auth_callback(token_params) elif auth_url: log.debug("using token auth with authUrl") token_request = self.token_request_from_auth_url( auth_method, auth_url, token_params, auth_headers, auth_params) else: token_request = self.create_token_request( token_params, key_name=key_name, key_secret=key_secret, query_time=query_time) if isinstance(token_request, TokenDetails): return token_request elif isinstance(token_request, dict) and 'issued' in token_request: return TokenDetails.from_dict(token_request) elif isinstance(token_request, dict): token_request = TokenRequest(**token_request) elif isinstance(token_request, six.text_type): return TokenDetails(token=token_request) # python2 elif isinstance(token_request, six.binary_type) and six.binary_type == str: return TokenDetails(token=token_request) token_path = "/keys/%s/requestToken" % token_request.key_name response = self.ably.http.post( token_path, headers=auth_headers, native_data=token_request.to_dict(), skip_auth=True ) AblyException.raise_for_response(response) response_dict = response.to_native() log.debug("Token: %s" % str(response_dict.get("token"))) return TokenDetails.from_dict(response_dict) def create_token_request(self, token_params=None, key_name=None, key_secret=None, query_time=None): token_params = token_params or {} token_request = {} key_name = key_name or self.auth_options.key_name key_secret = key_secret or self.auth_options.key_secret if not key_name or not key_secret: log.debug('key_name or key_secret blank') raise AblyException("No key specified: no means to generate a token", 401, 40101) token_request['key_name'] = key_name if token_params.get('timestamp'): token_request['timestamp'] = token_params['timestamp'] else: if query_time is None: query_time = self.auth_options.query_time if query_time: token_request['timestamp'] = self.ably.time() else: token_request['timestamp'] = self._timestamp() token_request['timestamp'] = int(token_request['timestamp']) token_request['ttl'] = token_params.get('ttl') or TokenDetails.DEFAULTS['ttl'] if token_params.get('capability') is None: token_request["capability"] = "" else: token_request['capability'] = six.text_type( Capability(token_params['capability']) ) token_request["client_id"] = ( token_params.get('client_id') or self.client_id) # Note: There is no expectation that the client # specifies the nonce; this is done by the library # However, this can be overridden by the client # simply for testing purposes token_request["nonce"] = token_params.get('nonce') or self._random_nonce() token_request = TokenRequest(**token_request) if token_params.get('mac') is None: # Note: There is no expectation that the client # specifies the mac; this is done by the library # However, this can be overridden by the client # simply for testing purposes. token_request.sign_request(key_secret.encode('utf8')) else: token_request.mac = token_params['mac'] return token_request @property def ably(self): return self.__ably @property def auth_mechanism(self): return self.__auth_mechanism @property def auth_options(self): return self.__auth_options @property def auth_params(self): return self.__auth_params @property def basic_credentials(self): return self.__basic_credentials @property def token_credentials(self): if self.__token_details: token = self.__token_details.token token_key = base64.b64encode(token.encode('utf-8')) return token_key.decode('ascii') @property def token_details(self): return self.__token_details @property def client_id(self): return self.__client_id def _configure_client_id(self, new_client_id): # If new client ID from Ably is a wildcard, but preconfigured clientId is set, # then keep the existing clientId if self.client_id != '*' and new_client_id == '*': self.__client_id_validated = True return # If client_id is defined and not a wildcard, prevent it changing, this is not supported if self.client_id is not None and self.client_id != '*' and new_client_id != self.client_id: raise IncompatibleClientIdException( "Client ID is immutable once configured for a client. " "Client ID cannot be changed to '{}'".format(new_client_id), 400, 40012) self.__client_id_validated = True self.__client_id = new_client_id def can_assume_client_id(self, assumed_client_id): if self.__client_id_validated: return self.client_id == '*' or self.client_id == assumed_client_id elif self.client_id is None or self.client_id == '*': return True # client ID is unknown else: return self.client_id == assumed_client_id def _get_auth_headers(self): if self.__auth_mechanism == Auth.Method.BASIC: return { 'Authorization': 'Basic %s' % self.basic_credentials, } else: self.authorise() return { 'Authorization': 'Bearer %s' % self.token_credentials, } def _timestamp(self): """Returns the local time in milliseconds since the unix epoch""" return int(time.time() * 1000) def _random_nonce(self): return uuid.uuid4().hex[:16] def token_request_from_auth_url(self, method, url, token_params, headers, auth_params): if method == 'GET': body = {} params = dict(auth_params, **token_params) elif method == 'POST': params = {} body = dict(auth_params, **token_params) from ably.http.http import Response response = Response(requests.request( method, url, headers=headers, params=params, data=body)) AblyException.raise_for_response(response) try: token_request = response.to_native() except ValueError: token_request = response.text return token_request