async def test_oauth_session_refresh_failure(opp, flow_handler, local_impl, aioclient_mock): """Test the OAuth2 session helper when no refresh is needed.""" flow_handler.async_register_implementation(opp, local_impl) aioclient_mock.post(TOKEN_URL, status=400) config_entry = MockConfigEntry( domain=TEST_DOMAIN, data={ "auth_implementation": TEST_DOMAIN, "token": { "refresh_token": REFRESH_TOKEN, "access_token": ACCESS_TOKEN_1, # Already expired, requires a refresh "expires_in": -500, "expires_at": time.time() - 500, "token_type": "bearer", "random_other_data": "should_stay", }, }, ) session = config_entry_oauth2_flow.OAuth2Session(opp, config_entry, local_impl) with pytest.raises(aiohttp.client_exceptions.ClientResponseError): await session.async_request("post", "https://example.com")
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry): """Set up xbox from a config entry.""" implementation = ( await config_entry_oauth2_flow.async_get_config_entry_implementation( opp, entry)) session = config_entry_oauth2_flow.OAuth2Session(opp, entry, implementation) auth = api.AsyncConfigEntryAuth( aiohttp_client.async_get_clientsession(opp), session) client = XboxLiveClient(auth) consoles: SmartglassConsoleList = await client.smartglass.get_console_list( ) _LOGGER.debug( "Found %d consoles: %s", len(consoles.result), consoles.dict(), ) coordinator = XboxUpdateCoordinator(opp, client, consoles) await coordinator.async_config_entry_first_refresh() opp.data[DOMAIN][entry.entry_id] = { "client": XboxLiveClient(auth), "consoles": consoles, "coordinator": coordinator, } opp.config_entries.async_setup_platforms(entry, PLATFORMS) return True
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry) -> bool: """Set up config entry.""" if CONF_TOKEN not in entry.data: raise ConfigEntryAuthFailed implementation = ( await config_entry_oauth2_flow.async_get_config_entry_implementation( opp, entry)) session = config_entry_oauth2_flow.OAuth2Session(opp, entry, implementation) neato_session = api.ConfigEntryAuth(opp, entry, session) opp.data[NEATO_DOMAIN][entry.entry_id] = neato_session hub = NeatoHub(opp, Account(neato_session)) try: await opp.async_add_executor_job(hub.update_robots) except NeatoException as ex: _LOGGER.debug("Failed to connect to Neato API") raise ConfigEntryNotReady from ex opp.data[NEATO_LOGIN] = hub opp.config_entries.async_setup_platforms(entry, PLATFORMS) return True
def __init__( self, opp: core.OpenPeerPower, config_entry: config_entries.ConfigEntry, implementation: config_entry_oauth2_flow.AbstractOAuth2Implementation, ) -> None: """Initialize Neato Botvac Auth.""" self.opp = opp self.session = config_entry_oauth2_flow.OAuth2Session( opp, config_entry, implementation) super().__init__(self.session.token, vendor=pybotvac.Neato())
def __init__( self, opp: core.OpenPeerPower, config_entry: config_entries.ConfigEntry, implementation: config_entry_oauth2_flow.AbstractOAuth2Implementation, ) -> None: """Initialize the Config Entry Somfy API.""" self.opp = opp self.config_entry = config_entry self.session = config_entry_oauth2_flow.OAuth2Session( opp, config_entry, implementation) super().__init__(None, None, token=self.session.token)
def __init__( self, opp: core.OpenPeerPower, config_entry: config_entries.ConfigEntry, implementation: config_entry_oauth2_flow.AbstractOAuth2Implementation, ) -> None: """Initialize Home Connect Auth.""" self.opp = opp self.config_entry = config_entry self.session = config_entry_oauth2_flow.OAuth2Session( opp, config_entry, implementation) super().__init__(self.session.token) self.devices = []
async def async_setup_entry(opp: OpenPeerPower, entry: config_entries.ConfigEntry): """Set up Almond config entry.""" websession = aiohttp_client.async_get_clientsession(opp) if entry.data["type"] == TYPE_LOCAL: auth = AlmondLocalAuth(entry.data["host"], websession) else: # OAuth2 implementation = ( await config_entry_oauth2_flow.async_get_config_entry_implementation( opp, entry)) oauth_session = config_entry_oauth2_flow.OAuth2Session( opp, entry, implementation) auth = AlmondOAuth(entry.data["host"], websession, oauth_session) api = WebAlmondAPI(auth) agent = AlmondAgent(opp, api, entry) # Opp.io does its own configuration. if not entry.data.get("is_oppio"): # If we're not starting or local, set up Almond right away if opp.state != CoreState.not_running or entry.data[ "type"] == TYPE_LOCAL: await _configure_almond_for_ha(opp, entry, api) else: # OAuth2 implementations can potentially rely on the HA Cloud url. # This url is not be available until 30 seconds after boot. async def configure_almond(_now): try: await _configure_almond_for_ha(opp, entry, api) except ConfigEntryNotReady: _LOGGER.warning( "Unable to configure Almond to connect to Open Peer Power" ) async def almond_opp_start(_event): event.async_call_later(opp, ALMOND_SETUP_DELAY, configure_almond) opp.bus.async_listen_once(EVENT_OPENPEERPOWER_START, almond_opp_start) conversation.async_set_agent(opp, agent) return True
async def test_oauth_session_with_clock_slightly_out_of_sync( opp, flow_handler, local_impl, aioclient_mock): """Test the OAuth2 session helper when the remote clock is slightly out of sync.""" flow_handler.async_register_implementation(opp, local_impl) aioclient_mock.post(TOKEN_URL, json={ "access_token": ACCESS_TOKEN_2, "expires_in": 19 }) aioclient_mock.post("https://example.com", status=201) config_entry = MockConfigEntry( domain=TEST_DOMAIN, data={ "auth_implementation": TEST_DOMAIN, "token": { "refresh_token": REFRESH_TOKEN, "access_token": ACCESS_TOKEN_1, "expires_in": 19, "expires_at": time.time() + 19, # Forces a refresh, "token_type": "bearer", "random_other_data": "should_stay", }, }, ) now = time.time() session = config_entry_oauth2_flow.OAuth2Session(opp, config_entry, local_impl) resp = await session.async_request("post", "https://example.com") assert resp.status == 201 # Refresh token, make request assert len(aioclient_mock.mock_calls) == 2 assert (aioclient_mock.mock_calls[1][3]["authorization"] == f"Bearer {ACCESS_TOKEN_2}") assert config_entry.data["token"]["refresh_token"] == REFRESH_TOKEN assert config_entry.data["token"]["access_token"] == ACCESS_TOKEN_2 assert config_entry.data["token"]["expires_in"] == 19 assert config_entry.data["token"]["random_other_data"] == "should_stay" assert round(config_entry.data["token"]["expires_at"] - now) == 19
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry) -> bool: """Set up Honeywell Lyric from a config entry.""" implementation = ( await config_entry_oauth2_flow.async_get_config_entry_implementation( opp, entry)) session = aiohttp_client.async_get_clientsession(opp) oauth_session = config_entry_oauth2_flow.OAuth2Session( opp, entry, implementation) client = ConfigEntryLyricClient(session, oauth_session) client_id = opp.data[DOMAIN][CONF_CLIENT_ID] lyric = Lyric(client, client_id) async def async_update_data() -> Lyric: """Fetch data from Lyric.""" try: async with async_timeout.timeout(60): await lyric.get_locations() return lyric except LyricAuthenticationException as exception: raise ConfigEntryAuthFailed from exception except (LyricException, ClientResponseError) as exception: raise UpdateFailed(exception) from exception coordinator = DataUpdateCoordinator( opp, _LOGGER, # Name of the data. For logging purposes. name="lyric_coordinator", update_method=async_update_data, # Polling interval. Will only be polled if there are subscribers. update_interval=timedelta(seconds=120), ) opp.data[DOMAIN][entry.entry_id] = coordinator # Fetch initial data so we have data when entities subscribe await coordinator.async_config_entry_first_refresh() opp.config_entries.async_setup_platforms(entry, PLATFORMS) return True
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry) -> bool: """Set up NEW_NAME from a config entry.""" implementation = ( await config_entry_oauth2_flow.async_get_config_entry_implementation( opp, entry)) session = config_entry_oauth2_flow.OAuth2Session(opp, entry, implementation) # If using a requests-based API lib opp.data[DOMAIN][entry.entry_id] = api.ConfigEntryAuth(opp, session) # If using an aiohttp-based API lib opp.data[DOMAIN][entry.entry_id] = api.AsyncConfigEntryAuth( aiohttp_client.async_get_clientsession(opp), session) opp.config_entries.async_setup_platforms(entry, PLATFORMS) return True
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry): """Set up NEW_NAME from a config entry.""" implementation = ( await config_entry_oauth2_flow.async_get_config_entry_implementation( opp, entry)) session = config_entry_oauth2_flow.OAuth2Session(opp, entry, implementation) # If using a requests-based API lib opp.data[DOMAIN][entry.entry_id] = api.ConfigEntryAuth(opp, entry, session) # If using an aiohttp-based API lib opp.data[DOMAIN][entry.entry_id] = api.AsyncConfigEntryAuth( aiohttp_client.async_get_clientsession(opp), session) for component in PLATFORMS: opp.async_create_task( opp.config_entries.async_forward_entry_setup(entry, component)) return True
async def test_oauth_session_no_token_refresh_needed(opp, flow_handler, local_impl, aioclient_mock): """Test the OAuth2 session helper when no refresh is needed.""" flow_handler.async_register_implementation(opp, local_impl) aioclient_mock.post("https://example.com", status=201) config_entry = MockConfigEntry( domain=TEST_DOMAIN, data={ "auth_implementation": TEST_DOMAIN, "token": { "refresh_token": REFRESH_TOKEN, "access_token": ACCESS_TOKEN_1, "expires_in": 500, "expires_at": time.time() + 500, # Should NOT refresh "token_type": "bearer", "random_other_data": "should_stay", }, }, ) now = time.time() session = config_entry_oauth2_flow.OAuth2Session(opp, config_entry, local_impl) resp = await session.async_request("post", "https://example.com") assert resp.status == 201 # make request (no refresh) assert len(aioclient_mock.mock_calls) == 1 assert (aioclient_mock.mock_calls[0][3]["authorization"] == f"Bearer {ACCESS_TOKEN_1}") assert config_entry.data["token"]["refresh_token"] == REFRESH_TOKEN assert config_entry.data["token"]["access_token"] == ACCESS_TOKEN_1 assert config_entry.data["token"]["expires_in"] == 500 assert config_entry.data["token"]["random_other_data"] == "should_stay" assert round(config_entry.data["token"]["expires_at"] - now) == 500
def __init__( self, opp: core.OpenPeerPower, config_entry: config_entries.ConfigEntry, implementation: config_entry_oauth2_flow.AbstractOAuth2Implementation, ) -> None: """Initialize Smappee Auth.""" self.opp = opp self.config_entry = config_entry self.session = config_entry_oauth2_flow.OAuth2Session( opp, config_entry, implementation) platform_to_farm = { "PRODUCTION": 1, "ACCEPTANCE": 2, "DEVELOPMENT": 3, } super().__init__( None, None, token=self.session.token, farm=platform_to_farm[opp.data[DOMAIN][CONF_PLATFORM]], )
async def async_setup_entry(opp: OpenPeerPower, entry: ConfigEntry): """Set up Netatmo from a config entry.""" implementation = ( await config_entry_oauth2_flow.async_get_config_entry_implementation( opp, entry)) # Set unique id if non was set (migration) if not entry.unique_id: opp.config_entries.async_update_entry(entry, unique_id=DOMAIN) session = config_entry_oauth2_flow.OAuth2Session(opp, entry, implementation) opp.data[DOMAIN][entry.entry_id] = { AUTH: api.AsyncConfigEntryNetatmoAuth( aiohttp_client.async_get_clientsession(opp), session) } data_handler = NetatmoDataHandler(opp, entry) await data_handler.async_setup() opp.data[DOMAIN][entry.entry_id][DATA_HANDLER] = data_handler opp.config_entries.async_setup_platforms(entry, PLATFORMS) async def unregister_webhook(_): if CONF_WEBHOOK_ID not in entry.data: return _LOGGER.debug("Unregister Netatmo webhook (%s)", entry.data[CONF_WEBHOOK_ID]) async_dispatcher_send( opp, f"signal-{DOMAIN}-webhook-None", { "type": "None", "data": { WEBHOOK_PUSH_TYPE: WEBHOOK_DEACTIVATION } }, ) webhook_unregister(opp, entry.data[CONF_WEBHOOK_ID]) await opp.data[DOMAIN][entry.entry_id][AUTH].async_dropwebhook() async def register_webhook(event): if CONF_WEBHOOK_ID not in entry.data: data = {**entry.data, CONF_WEBHOOK_ID: secrets.token_hex()} opp.config_entries.async_update_entry(entry, data=data) if opp.components.cloud.async_active_subscription(): if CONF_CLOUDHOOK_URL not in entry.data: webhook_url = await opp.components.cloud.async_create_cloudhook( entry.data[CONF_WEBHOOK_ID]) data = {**entry.data, CONF_CLOUDHOOK_URL: webhook_url} opp.config_entries.async_update_entry(entry, data=data) else: webhook_url = entry.data[CONF_CLOUDHOOK_URL] else: webhook_url = opp.components.webhook.async_generate_url( entry.data[CONF_WEBHOOK_ID]) if entry.data[ "auth_implementation"] == cloud.DOMAIN and not webhook_url.startswith( "https://"): _LOGGER.warning( "Webhook not registered - " "https and port 443 is required to register the webhook") return try: webhook_register( opp, DOMAIN, "Netatmo", entry.data[CONF_WEBHOOK_ID], async_handle_webhook, ) async def handle_event(event): """Handle webhook events.""" if event["data"][WEBHOOK_PUSH_TYPE] == WEBHOOK_ACTIVATION: if activation_listener is not None: activation_listener() if activation_timeout is not None: activation_timeout() activation_listener = async_dispatcher_connect( opp, f"signal-{DOMAIN}-webhook-None", handle_event, ) activation_timeout = async_call_later(opp, 30, unregister_webhook) await opp.data[DOMAIN][entry.entry_id ][AUTH].async_addwebhook(webhook_url) _LOGGER.info("Register Netatmo webhook: %s", webhook_url) except pyatmo.ApiError as err: _LOGGER.error("Error during webhook registration - %s", err) entry.async_on_unload( opp.bus.async_listen_once(EVENT_OPENPEERPOWER_STOP, unregister_webhook)) if opp.state == CoreState.running: await register_webhook(None) else: opp.bus.async_listen_once(EVENT_OPENPEERPOWER_START, register_webhook) opp.services.async_register(DOMAIN, "register_webhook", register_webhook) opp.services.async_register(DOMAIN, "unregister_webhook", unregister_webhook) entry.add_update_listener(async_config_entry_updated) return True