1 # Copyright 2015 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
11 logger
= logging
.getLogger('proximity_auth.%s' % __name__
)
13 _GOOGLE_APIS_URL
= 'www.googleapis.com'
14 _REQUEST_PATH
= '/cryptauth/v1/%s?alt=JSON'
16 class CryptAuthClient(object):
17 """ A client for making blocking CryptAuth API calls. """
19 def __init__(self
, access_token
, google_apis_url
=_GOOGLE_APIS_URL
):
20 self
._access
_token
= access_token
21 self
._google
_apis
_url
= google_apis_url
23 def GetMyDevices(self
):
24 """ Invokes the GetMyDevices API.
27 A list of devices or None if the API call fails.
28 Each device is a dictionary of the deserialized JSON returned by
32 'approvedForUnlockRequired': False,
33 'allowStaleRead': False,
34 'invocationReason': 13 # REASON_MANUAL
36 response
= self
._SendRequest
('deviceSync/getmydevices', request_data
)
37 return response
['devices'] if response
is not None else None
39 def GetUnlockKey(self
):
42 The unlock key registered with CryptAuth if it exists or None.
43 The device is a dictionary of the deserialized JSON returned by CryptAuth.
45 devices
= self
.GetMyDevices()
49 for device
in devices
:
50 if device
['unlockKey']:
54 def ToggleEasyUnlock(self
, enable
, public_key
=''):
55 """ Calls the ToggleEasyUnlock API.
57 enable: True to designate the device specified by |public_key| as an
59 public_key: The public key of the device to toggle. Ignored if |enable| is
60 False, which toggles all unlock keys off.
62 True upon success, else False.
64 request_data
= { 'enable': enable
, }
66 request_data
['applyToAll'] = True
68 request_data
['publicKey'] = public_key
69 response
= self
._SendRequest
('deviceSync/toggleeasyunlock', request_data
)
70 return response
is not None
72 def FindEligibleUnlockDevices(self
, time_delta_millis
=None):
73 """ Finds devices eligible to be an unlock key.
75 time_delta_millis: If specified, then only return eligible devices that
76 have contacted CryptAuth in the last time delta.
78 A tuple containing two lists, one of eligible devices and the other of
80 Each device is a dictionary of the deserialized JSON returned by
84 if time_delta_millis
is not None:
85 request_data
['maxLastUpdateTimeDeltaMillis'] = time_delta_millis
* 1000;
87 response
= self
._SendRequest
(
88 'deviceSync/findeligibleunlockdevices', request_data
)
92 response
['eligibleDevices'] if 'eligibleDevices' in response
else [])
94 response
['ineligibleDevices'] if (
95 'ineligibleDevices' in response
) else [])
96 return eligibleDevices
, ineligibleDevices
98 def PingPhones(self
, timeout_secs
=10):
99 """ Asks CryptAuth to ping registered phones and determine their status.
101 timeout_secs: The number of seconds to wait for phones to respond.
103 A tuple containing two lists, one of eligible devices and the other of
105 Each device is a dictionary of the deserialized JSON returned by
108 response
= self
._SendRequest
(
109 'deviceSync/senddevicesynctickle',
110 { 'tickleType': 'updateEnrollment' })
113 # We wait for phones to update their status with CryptAuth.
114 logger
.info('Waiting for %s seconds for phone status...' % timeout_secs
)
115 time
.sleep(timeout_secs
)
116 return self
.FindEligibleUnlockDevices(time_delta_millis
=timeout_secs
)
118 def _SendRequest(self
, function_path
, request_data
):
119 """ Sends an HTTP request to CryptAuth and returns the deserialized
122 conn
= httplib
.HTTPSConnection(self
._google
_apis
_url
)
123 path
= _REQUEST_PATH
% function_path
126 'authorization': 'Bearer ' + self
._access
_token
,
127 'Content-Type': 'application/json'
129 body
= json
.dumps(request_data
)
130 logger
.info('Making request to %s with body:\n%s' % (
131 path
, pprint
.pformat(request_data
)))
132 conn
.request('POST', path
, body
, headers
)
134 response
= conn
.getresponse()
135 if response
.status
== 204:
137 if response
.status
!= 200:
138 logger
.warning('Request to %s failed: %s' % (path
, response
.status
))
140 return json
.loads(response
.read())