1# Copyright (c) 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved 2# 3# Permission is hereby granted, free of charge, to any person obtaining a 4# copy of this software and associated documentation files (the 5# "Software"), to deal in the Software without restriction, including 6# without limitation the rights to use, copy, modify, merge, publish, dis- 7# tribute, sublicense, and/or sell copies of the Software, and to permit 8# persons to whom the Software is furnished to do so, subject to the fol- 9# lowing conditions: 10# 11# The above copyright notice and this permission notice shall be included 12# in all copies or substantial portions of the Software. 13# 14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- 16# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 17# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 18# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20# IN THE SOFTWARE. 21# 22from boto.compat import json 23from boto.exception import JSONResponseError 24from boto.connection import AWSAuthConnection 25from boto.regioninfo import RegionInfo 26from boto.cognito.sync import exceptions 27 28 29class CognitoSyncConnection(AWSAuthConnection): 30 """ 31 Amazon Cognito Sync 32 Amazon Cognito Sync provides an AWS service and client library 33 that enable cross-device syncing of application-related user data. 34 High-level client libraries are available for both iOS and 35 Android. You can use these libraries to persist data locally so 36 that it's available even if the device is offline. Developer 37 credentials don't need to be stored on the mobile device to access 38 the service. You can use Amazon Cognito to obtain a normalized 39 user ID and credentials. User data is persisted in a dataset that 40 can store up to 1 MB of key-value pairs, and you can have up to 20 41 datasets per user identity. 42 43 With Amazon Cognito Sync, the data stored for each identity is 44 accessible only to credentials assigned to that identity. In order 45 to use the Cognito Sync service, you need to make API calls using 46 credentials retrieved with `Amazon Cognito Identity service`_. 47 """ 48 APIVersion = "2014-06-30" 49 DefaultRegionName = "us-east-1" 50 DefaultRegionEndpoint = "cognito-sync.us-east-1.amazonaws.com" 51 ResponseError = JSONResponseError 52 53 _faults = { 54 "LimitExceededException": exceptions.LimitExceededException, 55 "ResourceConflictException": exceptions.ResourceConflictException, 56 "InvalidConfigurationException": exceptions.InvalidConfigurationException, 57 "TooManyRequestsException": exceptions.TooManyRequestsException, 58 "InvalidParameterException": exceptions.InvalidParameterException, 59 "ResourceNotFoundException": exceptions.ResourceNotFoundException, 60 "InternalErrorException": exceptions.InternalErrorException, 61 "NotAuthorizedException": exceptions.NotAuthorizedException, 62 } 63 64 65 def __init__(self, **kwargs): 66 region = kwargs.get('region') 67 if not region: 68 region = RegionInfo(self, self.DefaultRegionName, 69 self.DefaultRegionEndpoint) 70 else: 71 del kwargs['region'] 72 kwargs['host'] = region.endpoint 73 super(CognitoSyncConnection, self).__init__(**kwargs) 74 self.region = region 75 76 def _required_auth_capability(self): 77 return ['hmac-v4'] 78 79 def delete_dataset(self, identity_pool_id, identity_id, dataset_name): 80 """ 81 Deletes the specific dataset. The dataset will be deleted 82 permanently, and the action can't be undone. Datasets that 83 this dataset was merged with will no longer report the merge. 84 Any consequent operation on this dataset will result in a 85 ResourceNotFoundException. 86 87 :type identity_pool_id: string 88 :param identity_pool_id: A name-spaced GUID (for example, us- 89 east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon 90 Cognito. GUID generation is unique within a region. 91 92 :type identity_id: string 93 :param identity_id: A name-spaced GUID (for example, us- 94 east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon 95 Cognito. GUID generation is unique within a region. 96 97 :type dataset_name: string 98 :param dataset_name: A string of up to 128 characters. Allowed 99 characters are a-z, A-Z, 0-9, '_' (underscore), '-' (dash), and '.' 100 (dot). 101 102 """ 103 104 uri = '/identitypools/{0}/identities/{1}/datasets/{2}'.format( 105 identity_pool_id, identity_id, dataset_name) 106 return self.make_request('DELETE', uri, expected_status=200) 107 108 def describe_dataset(self, identity_pool_id, identity_id, dataset_name): 109 """ 110 Gets metadata about a dataset by identity and dataset name. 111 The credentials used to make this API call need to have access 112 to the identity data. With Amazon Cognito Sync, each identity 113 has access only to its own data. You should use Amazon Cognito 114 Identity service to retrieve the credentials necessary to make 115 this API call. 116 117 :type identity_pool_id: string 118 :param identity_pool_id: A name-spaced GUID (for example, us- 119 east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon 120 Cognito. GUID generation is unique within a region. 121 122 :type identity_id: string 123 :param identity_id: A name-spaced GUID (for example, us- 124 east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon 125 Cognito. GUID generation is unique within a region. 126 127 :type dataset_name: string 128 :param dataset_name: A string of up to 128 characters. Allowed 129 characters are a-z, A-Z, 0-9, '_' (underscore), '-' (dash), and '.' 130 (dot). 131 132 """ 133 134 uri = '/identitypools/{0}/identities/{1}/datasets/{2}'.format( 135 identity_pool_id, identity_id, dataset_name) 136 return self.make_request('GET', uri, expected_status=200) 137 138 def describe_identity_pool_usage(self, identity_pool_id): 139 """ 140 Gets usage details (for example, data storage) about a 141 particular identity pool. 142 143 :type identity_pool_id: string 144 :param identity_pool_id: A name-spaced GUID (for example, us- 145 east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon 146 Cognito. GUID generation is unique within a region. 147 148 """ 149 150 uri = '/identitypools/{0}'.format(identity_pool_id) 151 return self.make_request('GET', uri, expected_status=200) 152 153 def describe_identity_usage(self, identity_pool_id, identity_id): 154 """ 155 Gets usage information for an identity, including number of 156 datasets and data usage. 157 158 :type identity_pool_id: string 159 :param identity_pool_id: A name-spaced GUID (for example, us- 160 east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon 161 Cognito. GUID generation is unique within a region. 162 163 :type identity_id: string 164 :param identity_id: A name-spaced GUID (for example, us- 165 east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon 166 Cognito. GUID generation is unique within a region. 167 168 """ 169 170 uri = '/identitypools/{0}/identities/{1}'.format( 171 identity_pool_id, identity_id) 172 return self.make_request('GET', uri, expected_status=200) 173 174 def get_identity_pool_configuration(self, identity_pool_id): 175 """ 176 Gets the configuration settings of an identity pool. 177 178 :type identity_pool_id: string 179 :param identity_pool_id: A name-spaced GUID (for example, us- 180 east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon 181 Cognito. This is the ID of the pool for which to return a 182 configuration. 183 184 """ 185 186 uri = '/identitypools/{0}/configuration'.format(identity_pool_id) 187 return self.make_request('GET', uri, expected_status=200) 188 189 def list_datasets(self, identity_pool_id, identity_id, next_token=None, 190 max_results=None): 191 """ 192 Lists datasets for an identity. The credentials used to make 193 this API call need to have access to the identity data. With 194 Amazon Cognito Sync, each identity has access only to its own 195 data. You should use Amazon Cognito Identity service to 196 retrieve the credentials necessary to make this API call. 197 198 :type identity_pool_id: string 199 :param identity_pool_id: A name-spaced GUID (for example, us- 200 east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon 201 Cognito. GUID generation is unique within a region. 202 203 :type identity_id: string 204 :param identity_id: A name-spaced GUID (for example, us- 205 east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon 206 Cognito. GUID generation is unique within a region. 207 208 :type next_token: string 209 :param next_token: A pagination token for obtaining the next page of 210 results. 211 212 :type max_results: integer 213 :param max_results: The maximum number of results to be returned. 214 215 """ 216 217 uri = '/identitypools/{0}/identities/{1}/datasets'.format( 218 identity_pool_id, identity_id) 219 params = {} 220 headers = {} 221 query_params = {} 222 if next_token is not None: 223 query_params['nextToken'] = next_token 224 if max_results is not None: 225 query_params['maxResults'] = max_results 226 return self.make_request('GET', uri, expected_status=200, 227 data=json.dumps(params), headers=headers, 228 params=query_params) 229 230 def list_identity_pool_usage(self, next_token=None, max_results=None): 231 """ 232 Gets a list of identity pools registered with Cognito. 233 234 :type next_token: string 235 :param next_token: A pagination token for obtaining the next page of 236 results. 237 238 :type max_results: integer 239 :param max_results: The maximum number of results to be returned. 240 241 """ 242 243 uri = '/identitypools' 244 params = {} 245 headers = {} 246 query_params = {} 247 if next_token is not None: 248 query_params['nextToken'] = next_token 249 if max_results is not None: 250 query_params['maxResults'] = max_results 251 return self.make_request('GET', uri, expected_status=200, 252 data=json.dumps(params), headers=headers, 253 params=query_params) 254 255 def list_records(self, identity_pool_id, identity_id, dataset_name, 256 last_sync_count=None, next_token=None, max_results=None, 257 sync_session_token=None): 258 """ 259 Gets paginated records, optionally changed after a particular 260 sync count for a dataset and identity. The credentials used to 261 make this API call need to have access to the identity data. 262 With Amazon Cognito Sync, each identity has access only to its 263 own data. You should use Amazon Cognito Identity service to 264 retrieve the credentials necessary to make this API call. 265 266 :type identity_pool_id: string 267 :param identity_pool_id: A name-spaced GUID (for example, us- 268 east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon 269 Cognito. GUID generation is unique within a region. 270 271 :type identity_id: string 272 :param identity_id: A name-spaced GUID (for example, us- 273 east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon 274 Cognito. GUID generation is unique within a region. 275 276 :type dataset_name: string 277 :param dataset_name: A string of up to 128 characters. Allowed 278 characters are a-z, A-Z, 0-9, '_' (underscore), '-' (dash), and '.' 279 (dot). 280 281 :type last_sync_count: long 282 :param last_sync_count: The last server sync count for this record. 283 284 :type next_token: string 285 :param next_token: A pagination token for obtaining the next page of 286 results. 287 288 :type max_results: integer 289 :param max_results: The maximum number of results to be returned. 290 291 :type sync_session_token: string 292 :param sync_session_token: A token containing a session ID, identity 293 ID, and expiration. 294 295 """ 296 297 uri = '/identitypools/{0}/identities/{1}/datasets/{2}/records'.format( 298 identity_pool_id, identity_id, dataset_name) 299 params = {} 300 headers = {} 301 query_params = {} 302 if last_sync_count is not None: 303 query_params['lastSyncCount'] = last_sync_count 304 if next_token is not None: 305 query_params['nextToken'] = next_token 306 if max_results is not None: 307 query_params['maxResults'] = max_results 308 if sync_session_token is not None: 309 query_params['syncSessionToken'] = sync_session_token 310 return self.make_request('GET', uri, expected_status=200, 311 data=json.dumps(params), headers=headers, 312 params=query_params) 313 314 def register_device(self, identity_pool_id, identity_id, platform, token): 315 """ 316 Registers a device to receive push sync notifications. 317 318 :type identity_pool_id: string 319 :param identity_pool_id: A name-spaced GUID (for example, us- 320 east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon 321 Cognito. Here, the ID of the pool that the identity belongs to. 322 323 :type identity_id: string 324 :param identity_id: The unique ID for this identity. 325 326 :type platform: string 327 :param platform: The SNS platform type (e.g. GCM, SDM, APNS, 328 APNS_SANDBOX). 329 330 :type token: string 331 :param token: The push token. 332 333 """ 334 335 uri = '/identitypools/{0}/identity/{1}/device'.format( 336 identity_pool_id, identity_id) 337 params = {'Platform': platform, 'Token': token, } 338 headers = {} 339 query_params = {} 340 return self.make_request('POST', uri, expected_status=200, 341 data=json.dumps(params), headers=headers, 342 params=query_params) 343 344 def set_identity_pool_configuration(self, identity_pool_id, 345 push_sync=None): 346 """ 347 Sets the necessary configuration for push sync. 348 349 :type identity_pool_id: string 350 :param identity_pool_id: A name-spaced GUID (for example, us- 351 east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon 352 Cognito. This is the ID of the pool to modify. 353 354 :type push_sync: dict 355 :param push_sync: Configuration options to be applied to the identity 356 pool. 357 358 """ 359 360 uri = '/identitypools/{0}/configuration'.format(identity_pool_id) 361 params = {} 362 headers = {} 363 query_params = {} 364 if push_sync is not None: 365 params['PushSync'] = push_sync 366 return self.make_request('POST', uri, expected_status=200, 367 data=json.dumps(params), headers=headers, 368 params=query_params) 369 370 def subscribe_to_dataset(self, identity_pool_id, identity_id, 371 dataset_name, device_id): 372 """ 373 Subscribes to receive notifications when a dataset is modified 374 by another device. 375 376 :type identity_pool_id: string 377 :param identity_pool_id: A name-spaced GUID (for example, us- 378 east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon 379 Cognito. The ID of the pool to which the identity belongs. 380 381 :type identity_id: string 382 :param identity_id: Unique ID for this identity. 383 384 :type dataset_name: string 385 :param dataset_name: The name of the dataset to subcribe to. 386 387 :type device_id: string 388 :param device_id: The unique ID generated for this device by Cognito. 389 390 """ 391 392 uri = '/identitypools/{0}/identities/{1}/datasets/{2}/subscriptions/{3}'.format( 393 identity_pool_id, identity_id, dataset_name, device_id) 394 return self.make_request('POST', uri, expected_status=200) 395 396 def unsubscribe_from_dataset(self, identity_pool_id, identity_id, 397 dataset_name, device_id): 398 """ 399 Unsubscribe from receiving notifications when a dataset is 400 modified by another device. 401 402 :type identity_pool_id: string 403 :param identity_pool_id: A name-spaced GUID (for example, us- 404 east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon 405 Cognito. The ID of the pool to which this identity belongs. 406 407 :type identity_id: string 408 :param identity_id: Unique ID for this identity. 409 410 :type dataset_name: string 411 :param dataset_name: The name of the dataset from which to unsubcribe. 412 413 :type device_id: string 414 :param device_id: The unique ID generated for this device by Cognito. 415 416 """ 417 418 uri = '/identitypools/{0}/identities/{1}/datasets/{2}/subscriptions/{3}'.format( 419 identity_pool_id, identity_id, dataset_name, device_id) 420 return self.make_request('DELETE', uri, expected_status=200) 421 422 def update_records(self, identity_pool_id, identity_id, dataset_name, 423 sync_session_token, device_id=None, 424 record_patches=None, client_context=None): 425 """ 426 Posts updates to records and add and delete records for a 427 dataset and user. The credentials used to make this API call 428 need to have access to the identity data. With Amazon Cognito 429 Sync, each identity has access only to its own data. You 430 should use Amazon Cognito Identity service to retrieve the 431 credentials necessary to make this API call. 432 433 :type identity_pool_id: string 434 :param identity_pool_id: A name-spaced GUID (for example, us- 435 east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon 436 Cognito. GUID generation is unique within a region. 437 438 :type identity_id: string 439 :param identity_id: A name-spaced GUID (for example, us- 440 east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon 441 Cognito. GUID generation is unique within a region. 442 443 :type dataset_name: string 444 :param dataset_name: A string of up to 128 characters. Allowed 445 characters are a-z, A-Z, 0-9, '_' (underscore), '-' (dash), and '.' 446 (dot). 447 448 :type device_id: string 449 :param device_id: The unique ID generated for this device by Cognito. 450 451 :type record_patches: list 452 :param record_patches: A list of patch operations. 453 454 :type sync_session_token: string 455 :param sync_session_token: The SyncSessionToken returned by a previous 456 call to ListRecords for this dataset and identity. 457 458 :type client_context: string 459 :param client_context: Intended to supply a device ID that will 460 populate the `lastModifiedBy` field referenced in other methods. 461 The `ClientContext` field is not yet implemented. 462 463 """ 464 465 uri = '/identitypools/{0}/identities/{1}/datasets/{2}'.format( 466 identity_pool_id, identity_id, dataset_name) 467 params = {'SyncSessionToken': sync_session_token, } 468 headers = {} 469 query_params = {} 470 if device_id is not None: 471 params['DeviceId'] = device_id 472 if record_patches is not None: 473 params['RecordPatches'] = record_patches 474 if client_context is not None: 475 headers['x-amz-Client-Context'] = client_context 476 if client_context is not None: 477 headers['x-amz-Client-Context'] = client_context 478 return self.make_request('POST', uri, expected_status=200, 479 data=json.dumps(params), headers=headers, 480 params=query_params) 481 482 def make_request(self, verb, resource, headers=None, data='', 483 expected_status=None, params=None): 484 if headers is None: 485 headers = {} 486 response = AWSAuthConnection.make_request( 487 self, verb, resource, headers=headers, data=data, params=params) 488 body = json.loads(response.read().decode('utf-8')) 489 if response.status == expected_status: 490 return body 491 else: 492 error_type = response.getheader('x-amzn-ErrorType').split(':')[0] 493 error_class = self._faults.get(error_type, self.ResponseError) 494 raise error_class(response.status, response.reason, body) 495