Improved iCloud 2FA support. (#5984)
parent
fdd1957750
commit
3d3a0a7a4f
|
@ -197,13 +197,22 @@ class Icloud(DeviceScanner):
|
||||||
|
|
||||||
def icloud_trusted_device_callback(self, callback_data):
|
def icloud_trusted_device_callback(self, callback_data):
|
||||||
"""The trusted device is chosen."""
|
"""The trusted device is chosen."""
|
||||||
self._trusted_device = int(callback_data.get('0', '0'))
|
self._trusted_device = int(callback_data.get('trusted_device'))
|
||||||
self._trusted_device = self.api.trusted_devices[self._trusted_device]
|
self._trusted_device = self.api.trusted_devices[self._trusted_device]
|
||||||
|
|
||||||
|
if not self.api.send_verification_code(self._trusted_device):
|
||||||
|
_LOGGER.error('Failed to send verification code')
|
||||||
|
self._trusted_device = None
|
||||||
|
return
|
||||||
|
|
||||||
if self.accountname in _CONFIGURING:
|
if self.accountname in _CONFIGURING:
|
||||||
request_id = _CONFIGURING.pop(self.accountname)
|
request_id = _CONFIGURING.pop(self.accountname)
|
||||||
configurator = get_component('configurator')
|
configurator = get_component('configurator')
|
||||||
configurator.request_done(request_id)
|
configurator.request_done(request_id)
|
||||||
|
|
||||||
|
# Trigger the next step immediately
|
||||||
|
self.icloud_need_verification_code()
|
||||||
|
|
||||||
def icloud_need_trusted_device(self):
|
def icloud_need_trusted_device(self):
|
||||||
"""We need a trusted device."""
|
"""We need a trusted device."""
|
||||||
configurator = get_component('configurator')
|
configurator = get_component('configurator')
|
||||||
|
@ -213,7 +222,10 @@ class Icloud(DeviceScanner):
|
||||||
devicesstring = ''
|
devicesstring = ''
|
||||||
devices = self.api.trusted_devices
|
devices = self.api.trusted_devices
|
||||||
for i, device in enumerate(devices):
|
for i, device in enumerate(devices):
|
||||||
devicesstring += "{}: {};".format(i, device.get('deviceName'))
|
devicename = device.get(
|
||||||
|
'deviceName',
|
||||||
|
'SMS to %s' % device.get('phoneNumber'))
|
||||||
|
devicesstring += "{}: {};".format(i, devicename)
|
||||||
|
|
||||||
_CONFIGURING[self.accountname] = configurator.request_config(
|
_CONFIGURING[self.accountname] = configurator.request_config(
|
||||||
self.hass, 'iCloud {}'.format(self.accountname),
|
self.hass, 'iCloud {}'.format(self.accountname),
|
||||||
|
@ -223,12 +235,27 @@ class Icloud(DeviceScanner):
|
||||||
' the index from this list: ' + devicesstring),
|
' the index from this list: ' + devicesstring),
|
||||||
entity_picture="/static/images/config_icloud.png",
|
entity_picture="/static/images/config_icloud.png",
|
||||||
submit_caption='Confirm',
|
submit_caption='Confirm',
|
||||||
fields=[{'id': '0'}]
|
fields=[{'id': 'trusted_device', 'name': 'Trusted Device'}]
|
||||||
)
|
)
|
||||||
|
|
||||||
def icloud_verification_callback(self, callback_data):
|
def icloud_verification_callback(self, callback_data):
|
||||||
"""The trusted device is chosen."""
|
"""The trusted device is chosen."""
|
||||||
self._verification_code = callback_data.get('0')
|
from pyicloud.exceptions import PyiCloudException
|
||||||
|
self._verification_code = callback_data.get('code')
|
||||||
|
|
||||||
|
try:
|
||||||
|
if not self.api.validate_verification_code(
|
||||||
|
self._trusted_device, self._verification_code):
|
||||||
|
raise PyiCloudException('Unknown failure')
|
||||||
|
except PyiCloudException as error:
|
||||||
|
# Reset to the inital 2FA state to allow the user to retry
|
||||||
|
_LOGGER.error('Failed to verify verification code: %s', error)
|
||||||
|
self._trusted_device = None
|
||||||
|
self._verification_code = None
|
||||||
|
|
||||||
|
# Trigger the next step immediately
|
||||||
|
self.icloud_need_trusted_device()
|
||||||
|
|
||||||
if self.accountname in _CONFIGURING:
|
if self.accountname in _CONFIGURING:
|
||||||
request_id = _CONFIGURING.pop(self.accountname)
|
request_id = _CONFIGURING.pop(self.accountname)
|
||||||
configurator = get_component('configurator')
|
configurator = get_component('configurator')
|
||||||
|
@ -240,22 +267,17 @@ class Icloud(DeviceScanner):
|
||||||
if self.accountname in _CONFIGURING:
|
if self.accountname in _CONFIGURING:
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.api.send_verification_code(self._trusted_device):
|
|
||||||
self._verification_code = 'waiting'
|
|
||||||
|
|
||||||
_CONFIGURING[self.accountname] = configurator.request_config(
|
_CONFIGURING[self.accountname] = configurator.request_config(
|
||||||
self.hass, 'iCloud {}'.format(self.accountname),
|
self.hass, 'iCloud {}'.format(self.accountname),
|
||||||
self.icloud_verification_callback,
|
self.icloud_verification_callback,
|
||||||
description=('Please enter the validation code:'),
|
description=('Please enter the validation code:'),
|
||||||
entity_picture="/static/images/config_icloud.png",
|
entity_picture="/static/images/config_icloud.png",
|
||||||
submit_caption='Confirm',
|
submit_caption='Confirm',
|
||||||
fields=[{'code': '0'}]
|
fields=[{'id': 'code', 'name': 'code'}]
|
||||||
)
|
)
|
||||||
|
|
||||||
def keep_alive(self, now):
|
def keep_alive(self, now):
|
||||||
"""Keep the api alive."""
|
"""Keep the api alive."""
|
||||||
from pyicloud.exceptions import PyiCloud2FARequiredError
|
|
||||||
|
|
||||||
if self.api is None:
|
if self.api is None:
|
||||||
self.reset_account_icloud()
|
self.reset_account_icloud()
|
||||||
|
|
||||||
|
@ -263,9 +285,8 @@ class Icloud(DeviceScanner):
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.api.requires_2fa:
|
if self.api.requires_2fa:
|
||||||
|
from pyicloud.exceptions import PyiCloudException
|
||||||
try:
|
try:
|
||||||
self.api.authenticate()
|
|
||||||
except PyiCloud2FARequiredError:
|
|
||||||
if self._trusted_device is None:
|
if self._trusted_device is None:
|
||||||
self.icloud_need_trusted_device()
|
self.icloud_need_trusted_device()
|
||||||
return
|
return
|
||||||
|
@ -274,12 +295,14 @@ class Icloud(DeviceScanner):
|
||||||
self.icloud_need_verification_code()
|
self.icloud_need_verification_code()
|
||||||
return
|
return
|
||||||
|
|
||||||
if self._verification_code == 'waiting':
|
self.api.authenticate()
|
||||||
return
|
if self.api.requires_2fa:
|
||||||
|
raise Exception('Unknown failure')
|
||||||
|
|
||||||
if self.api.validate_verification_code(
|
self._trusted_device = None
|
||||||
self._trusted_device, self._verification_code):
|
self._verification_code = None
|
||||||
self._verification_code = None
|
except PyiCloudException as error:
|
||||||
|
_LOGGER.error("Error setting up 2fa: %s", error)
|
||||||
else:
|
else:
|
||||||
self.api.authenticate()
|
self.api.authenticate()
|
||||||
|
|
||||||
|
@ -397,13 +420,13 @@ class Icloud(DeviceScanner):
|
||||||
try:
|
try:
|
||||||
if devicename is not None:
|
if devicename is not None:
|
||||||
if devicename in self.devices:
|
if devicename in self.devices:
|
||||||
self.devices[devicename].update_icloud()
|
self.devices[devicename].location()
|
||||||
else:
|
else:
|
||||||
_LOGGER.error("devicename %s unknown for account %s",
|
_LOGGER.error("devicename %s unknown for account %s",
|
||||||
devicename, self._attrs[ATTR_ACCOUNTNAME])
|
devicename, self._attrs[ATTR_ACCOUNTNAME])
|
||||||
else:
|
else:
|
||||||
for device in self.devices:
|
for device in self.devices:
|
||||||
self.devices[device].update_icloud()
|
self.devices[device].location()
|
||||||
except PyiCloudNoDevicesException:
|
except PyiCloudNoDevicesException:
|
||||||
_LOGGER.error('No iCloud Devices found!')
|
_LOGGER.error('No iCloud Devices found!')
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue