Merge branch 'simplify-login-sequence'

This commit is contained in:
Adam Coddington 2016-01-22 22:23:19 -08:00
commit 13f71ddbcc

View file

@ -32,7 +32,7 @@ logger = logging.getLogger(__name__)
class PyiCloudService(object): class PyiCloudService(object):
""" """
A base authentication class for the iCloud service. Handles the A base authentication class for the iCloud service. Handles the
validation and authentication required to access iCloud services. authentication required to access iCloud services.
Usage: Usage:
from pyicloud import PyiCloudService from pyicloud import PyiCloudService
@ -48,7 +48,6 @@ class PyiCloudService(object):
self._setup_endpoint = 'https://setup.icloud.com/setup/ws/1' self._setup_endpoint = 'https://setup.icloud.com/setup/ws/1'
self._base_login_url = '%s/login' % self._setup_endpoint self._base_login_url = '%s/login' % self._setup_endpoint
self._base_validate_url = '%s/validate' % self._setup_endpoint
if cookie_directory: if cookie_directory:
self._cookie_directory = os.path.expanduser( self._cookie_directory = os.path.expanduser(
@ -77,64 +76,42 @@ class PyiCloudService(object):
# Most likely a pickled cookiejar from earlier versions # Most likely a pickled cookiejar from earlier versions
pass pass
self.params = {} self.params = {
'clientBuildNumber': '14E45',
'clientId': self.client_id,
}
self.authenticate() self.authenticate()
def refresh_validate(self):
"""
Queries the /validate endpoint and fetches two key values we need:
1. "dsInfo" is a nested object which contains the "dsid" integer.
This object doesn't exist until *after* the login has taken place,
the first request will compain about a X-APPLE-WEBAUTH-TOKEN cookie
2. "instance" is an int which is used to build the "id" query string.
This is, pseudo: sha1(email + "instance") to uppercase.
"""
req = self.session.get(self._base_validate_url, params=self.params)
resp = req.json()
if 'dsInfo' in resp:
dsid = resp['dsInfo']['dsid']
self.params.update({'dsid': dsid})
instance = resp.get(
'instance',
uuid.uuid4().hex.encode('utf-8')
)
sha = hashlib.sha1(
self.user.get('apple_id').encode('utf-8') + instance
)
self.params.update({'id': sha.hexdigest().upper()})
self.params.update({
'clientBuildNumber': '14E45',
'clientId': self.client_id,
})
def authenticate(self): def authenticate(self):
""" """
Handles the full authentication steps, validating, Handles authentication, and persists the X-APPLE-WEB-KB cookie so that
authenticating and then validating again. subsequent logins will not cause additional e-mails from Apple.
""" """
self.refresh_validate()
data = dict(self.user) data = dict(self.user)
data.update({'id': self.params['id'], 'extended_login': False})
# We authenticate every time, so "remember me" is not needed
data.update({'extended_login': False})
req = self.session.post( req = self.session.post(
self._base_login_url, self._base_login_url,
params=self.params, params=self.params,
data=json.dumps(data) data=json.dumps(data)
) )
if not req.ok: resp = req.json() if req.ok else {}
if 'dsInfo' not in resp:
msg = 'Invalid email/password combination.' msg = 'Invalid email/password combination.'
raise PyiCloudFailedLoginException(msg) raise PyiCloudFailedLoginException(msg)
self.params.update({'dsid': resp['dsInfo']['dsid']})
if not os.path.exists(self._cookie_directory): if not os.path.exists(self._cookie_directory):
os.mkdir(self._cookie_directory) os.mkdir(self._cookie_directory)
self.session.cookies.save() self.session.cookies.save()
self.refresh_validate() self.discovery = resp
self.discovery = req.json()
self.webservices = self.discovery['webservices'] self.webservices = self.discovery['webservices']
def _get_cookiejar_path(self): def _get_cookiejar_path(self):