def _get_authenticator(self, config: Mapping[str, Any]): # Added to maintain backward compatibility with previous versions if "api_token" in config: return TokenAuthenticator(config["api_token"]) credentials = config.get("credentials") credentials_title = credentials.get("option_title") if credentials_title == "Default OAuth2.0 authorization": # We can get `refresh_token` only if the token rotation function is enabled for the Slack Oauth Application. # If it is disabled, then we use the generated `access_token`, which acts without expiration. # https://api.slack.com/authentication/rotation if credentials.get("refresh_token", "").strip(): return Oauth2Authenticator( token_refresh_endpoint= "https://slack.com/api/oauth.v2.access", client_id=credentials["client_id"], client_secret=credentials["client_secret"], refresh_token=credentials["refresh_token"], ) return TokenAuthenticator(credentials["access_token"]) elif credentials_title == "API Token Credentials": return TokenAuthenticator(credentials["api_token"]) else: raise Exception( f"No supported option_title: {credentials_title} specified. See spec.json for references" )
def check_connection(self, logger, config) -> Tuple[bool, any]: try: authenticator = TokenAuthenticator(config["api_token"]) url = f"{ZenloopStream.url_base}surveys" session = requests.get(url, headers=authenticator.get_auth_header()) session.raise_for_status() return True, None except Exception as error: return False, f"Unable to connect to Zenloop API with the provided credentials - {error}"
def get_access_token(self): credentials = self.config.get("credentials") if credentials: auth_type = credentials.get("auth_type") if auth_type == "OAuth2.0": return TokenAuthenticator(credentials.get("access_token")) return TokenAuthenticator(credentials.get("token")) # support the old config if "access_token" in self.config: return TokenAuthenticator(self.config.get("access_token"))
def test_token_authenticator(): """ Should match passed in token, no matter how many times token is retrieved. """ token_auth = TokenAuthenticator(token="test-token") header1 = token_auth.get_auth_header() header2 = token_auth.get_auth_header() prepared_request = requests.PreparedRequest() prepared_request.headers = {} token_auth(prepared_request) assert {"Authorization": "Bearer test-token"} == prepared_request.headers assert {"Authorization": "Bearer test-token"} == header1 assert {"Authorization": "Bearer test-token"} == header2
def login(self) -> TokenAuthenticator: data = {"grant_type": "client_credentials"} auth = requests.auth.HTTPBasicAuth(self.username, self.password) response = requests.post(self.url, auth=auth, data=data) response.raise_for_status() self.access_token = response.json()["access_token"] return TokenAuthenticator(self.access_token)
def streams(self, config: Mapping[str, Any]) -> List[Stream]: args = { "api_token": TokenAuthenticator(token=config["api_token"]), "date_from": config["date_from"], "survey_id": config.get("survey_id"), "survey_group_id": config.get("survey_group_id"), } return [Surveys(**args), Answers(**args), SurveyGroups(**args), AnswersSurveyGroup(**args)]
def check_connection(self, logger, config) -> Tuple[bool, any]: auth = TokenAuthenticator(token=config["api_key"]) for table in config["tables"]: try: Helpers.get_first_row(auth, config["base_id"], table) except Exception as e: return False, str(e) return True, None
def streams(self, config: Mapping[str, Any]) -> List[Stream]: stream_kwargs = { "authenticator": TokenAuthenticator(config["api_token"]), "workspace": config["workspace"], "start_date": config["start_date"], } return [Members(**stream_kwargs), Workspace(**stream_kwargs)]
def discover(self, logger: AirbyteLogger, config) -> AirbyteCatalog: streams = [] auth = TokenAuthenticator(token=config["api_key"]) for table in config["tables"]: record = Helpers.get_first_row(auth, config["base_id"], table) json_schema = Helpers.get_json_schema(record) airbyte_stream = Helpers.get_airbyte_stream(table, json_schema) streams.append(airbyte_stream) return AirbyteCatalog(streams=streams)
def check_connection(self, logger, config) -> Tuple[bool, any]: try: authenticator = TokenAuthenticator(config["user_auth_key"], "Basic") stream = Apps(authenticator=authenticator, config=config) records = stream.read_records(sync_mode=SyncMode.full_refresh) next(records) return True, None except requests.exceptions.RequestException as e: return False, e
def streams(self, config: Mapping[str, Any]) -> List[Stream]: auth = TokenAuthenticator(token=config["api_key"]) return [ Companies(authenticator=auth), Locations(authenticator=auth), Products(authenticator=auth), Invoices(authenticator=auth), Shipments(authenticator=auth, start_date=config["start_date"]), ]
def streams(self, config: Mapping[str, Any]) -> List[Stream]: AirbyteLogger().log("INFO", f"Using start_date: {config['start_date']}") authenticator = TokenAuthenticator(config["user_auth_key"], "Basic") args = {"authenticator": authenticator, "config": config} apps = Apps(**args) args = {"parent": apps, **args} return [apps, Devices(**args), Notifications(**args), Outcomes(**args)]
def get_auth(config: Mapping[str, Any]) -> AuthBase: credential = config.get("credentials", {}) auth_type = credential.get("auth_type") if auth_type == "Oauth": # scopes needed for all currently supported streams: scopes = [ "CUSTOMERS_READ", "EMPLOYEES_READ", "ITEMS_READ", "MERCHANT_PROFILE_READ", "ORDERS_READ", "PAYMENTS_READ", "TIMECARDS_READ", # OAuth Permissions: # https://developer.squareup.com/docs/oauth-api/square-permissions # https://developer.squareup.com/reference/square/enums/OAuthPermission # "DISPUTES_READ", # "GIFTCARDS_READ", # "INVENTORY_READ", # "INVOICES_READ", # "TIMECARDS_SETTINGS_READ", # "LOYALTY_READ", # "ONLINE_STORE_SITE_READ", # "ONLINE_STORE_SNIPPETS_READ", # "SUBSCRIPTIONS_READ", ] auth = Oauth2AuthenticatorSquare( token_refresh_endpoint="https://connect.squareup.com/oauth2/token", client_secret=credential.get("client_secret"), client_id=credential.get("client_id"), refresh_token=credential.get("refresh_token"), scopes=scopes, expires_in_name="expires_at", ) elif auth_type == "Apikey": auth = TokenAuthenticator(token=credential.get("api_key")) elif not auth_type and config.get("api_key"): auth = TokenAuthenticator(token=config.get("api_key")) else: raise Exception(f"Invalid auth type: {auth_type}") return auth
def _get_authenticator(api_key): """ Returns a TokenAuthenticator. The API token is concatenated with `:x` and the resulting string is base-64 encoded. See https://documentation.bamboohr.com/docs#authentication """ return TokenAuthenticator(token=base64.b64encode( f"{api_key}:x".encode("utf-8")).decode("utf-8"), auth_method="Basic")
def get_auth(self) -> TokenAuthenticator: """Return the TokenAuthenticator object with access_token.""" # the old config supports for backward capability access_token = self.config.get("access_token") if not access_token: # the new config supports `OAuth2.0` access_token = self.config["credentials"]["access_token"] return TokenAuthenticator(token=access_token)
def check_connection(self, logger, config) -> Tuple[bool, any]: try: authenticator = TokenAuthenticator(config["access_token"]) records = RoutingSettings( authenticator=authenticator).read_records( sync_mode=SyncMode.full_refresh) next(records) return True, None except Exception as error: return False, f"Unable to connect to Zendesk Chat API with the provided credentials - {error}"
def get_authenticator( cls, config: Mapping[str, Any]) -> BasicApiTokenAuthenticator: if config["auth_method"]["auth_method"] == "access_token": return TokenAuthenticator( token=config["auth_method"]["access_token"]) elif config["auth_method"]["auth_method"] == "api_token": return BasicApiTokenAuthenticator( config["auth_method"]["email"], config["auth_method"]["api_token"]) raise SourceZendeskException( f"Not implemented authorization method: {config['auth_method']}")
def check_connection(self, logger, config) -> Tuple[bool, any]: try: workspace_stream = Workspace( authenticator=TokenAuthenticator(token=config["api_token"]), workspace=config["workspace"], ) next( workspace_stream.read_records(sync_mode=SyncMode.full_refresh)) return True, None except Exception as e: return False, f"Please check that your API key and workspace name are entered correctly: {repr(e)}"
def streams(self, config: Mapping[str, Any]) -> List[Stream]: AirbyteLogger().log("INFO", f"Using start_date: {config['start_date']}") authenticator = TokenAuthenticator(config["access_token"]) args = {"authenticator": authenticator, "config": config} pages = Pages(**args) blocks = Blocks(parent=pages, **args) return [Users(**args), Databases(**args), pages, blocks]
def streams(self, config: Mapping[str, Any]) -> List[Stream]: auth = TokenAuthenticator(token=config["api_key"]) streams = [] for table in config["tables"]: record = Helpers.get_first_row(auth, config["base_id"], table) json_schema = Helpers.get_json_schema(record) stream = AirtableStream(base_id=config["base_id"], table_name=table, authenticator=auth, schema=json_schema) streams.append(stream) return streams
def check_connection(self, logger, config) -> Tuple[bool, any]: """ Makes a request to the /ping endpoint, which validates that the authentication credentials are appropriate. API Docs: https://docs.withorb.com/reference/ping """ auth_header = TokenAuthenticator(token=config["api_key"]).get_auth_header() ping_url = ORB_API_BASE_URL + "ping" ping_response = requests.get(ping_url, headers=auth_header) try: ping_response.raise_for_status() return True, None except Exception as e: return False, e
def get_first_row(auth: TokenAuthenticator, base_id: str, table: str) -> Dict[str, Any]: url = f"https://api.airtable.com/v0/{base_id}/{table}?pageSize=1" try: response = requests.get(url, headers=auth.get_auth_header()) response.raise_for_status() except requests.exceptions.HTTPError as e: if e.response.status_code == 401: raise Exception("Invalid API key") elif e.response.status_code == 404: raise Exception(f"Table '{table}' not found") else: raise Exception(f"Error getting first row from table {table}: {e}") json_response = response.json() record = json_response.get("records", [])[0] return record
def streams(self, config: Mapping[str, Any]) -> List[Stream]: authenticator = TokenAuthenticator(config["access_token"]) return [ Agents(authenticator=authenticator), AgentTimelines(authenticator=authenticator, start_date=config["start_date"]), Accounts(authenticator=authenticator), Chats(authenticator=authenticator), Shortcuts(authenticator=authenticator), Triggers(authenticator=authenticator), Bans(authenticator=authenticator), Departments(authenticator=authenticator), Goals(authenticator=authenticator), Skills(authenticator=authenticator), Roles(authenticator=authenticator), RoutingSettings(authenticator=authenticator), ]
def read_records( self, sync_mode: SyncMode, cursor_field: List[str] = None, stream_slice: Mapping[str, Any] = None, stream_state: Mapping[str, Any] = None, ) -> Iterable[Mapping[str, Any]]: # OneSignal needs different auth token for each app, so we inject it # right before read records request. # Note: The _session.auth can be replaced when HttpStream provided # some ways to get its request authenticator in the future token = self._auth_token if stream_slice: token = stream_slice.get("rest_api_key", token) self._session.auth = TokenAuthenticator(token, "Basic") return super().read_records(sync_mode, cursor_field, stream_slice, stream_state)
def streams(self, config: Mapping[str, Any]) -> List[Stream]: authenticator = TokenAuthenticator(token=config["api_key"]) lookback_window = config.get("lookback_window_days") string_event_properties_keys = config.get("string_event_properties_keys") numeric_event_properties_keys = config.get("numeric_event_properties_keys") if not self.input_keys_mutually_exclusive(string_event_properties_keys, numeric_event_properties_keys): raise ValueError("Supplied property keys for string and numeric valued property values must be mutually exclusive.") start_date_str = config.get("start_date") start_date = pendulum.parse(start_date_str) if start_date_str else None return [ Customers(authenticator=authenticator, lookback_window_days=lookback_window, start_date=start_date), Subscriptions(authenticator=authenticator, lookback_window_days=lookback_window, start_date=start_date), Plans(authenticator=authenticator, lookback_window_days=lookback_window, start_date=start_date), CreditsLedgerEntries( authenticator=authenticator, lookback_window_days=lookback_window, start_date=start_date, string_event_properties_keys=string_event_properties_keys, numeric_event_properties_keys=numeric_event_properties_keys, ), ]
def get_authenticator( cls, config: Mapping[str, Any]) -> BasicApiTokenAuthenticator: # old authentication flow support auth_old = config.get("auth_method") if auth_old: if auth_old.get("auth_method") == "api_token": return BasicApiTokenAuthenticator( config["auth_method"]["email"], config["auth_method"]["api_token"]) # new authentication flow auth = config.get("credentials") if auth: if auth.get("credentials") == "oauth2.0": return TokenAuthenticator( token=config["credentials"]["access_token"]) elif auth.get("credentials") == "api_token": return BasicApiTokenAuthenticator( config["credentials"]["email"], config["credentials"]["api_token"]) else: raise SourceZendeskException( f"Not implemented authorization method: {config['credentials']}" )
def test_get_first_row_invalid_api_key(base_id, table): with pytest.raises(Exception): auth = TokenAuthenticator("invalid_api_key") Helpers.get_first_row(auth, base_id, table)
def test_requests_native_token_authenticator(): stream = StubBasicReadHttpStream( authenticator=TokenAuthenticator("test-token")) assert isinstance(stream.authenticator, NoAuth) assert isinstance(stream._session.auth, TokenAuthenticator)