| OLD | NEW |
| 1 # Copyright 2014 Google Inc. All rights reserved. | 1 # Copyright 2014 Google Inc. All rights reserved. |
| 2 # | 2 # |
| 3 # Licensed under the Apache License, Version 2.0 (the "License"); | 3 # Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 # you may not use this file except in compliance with the License. | 4 # you may not use this file except in compliance with the License. |
| 5 # You may obtain a copy of the License at | 5 # You may obtain a copy of the License at |
| 6 # | 6 # |
| 7 # http://www.apache.org/licenses/LICENSE-2.0 | 7 # http://www.apache.org/licenses/LICENSE-2.0 |
| 8 # | 8 # |
| 9 # Unless required by applicable law or agreed to in writing, software | 9 # Unless required by applicable law or agreed to in writing, software |
| 10 # distributed under the License is distributed on an "AS IS" BASIS, | 10 # distributed under the License is distributed on an "AS IS" BASIS, |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 46 | 46 |
| 47 import errno | 47 import errno |
| 48 import json | 48 import json |
| 49 import logging | 49 import logging |
| 50 import os | 50 import os |
| 51 import threading | 51 import threading |
| 52 | 52 |
| 53 from oauth2client.client import Credentials | 53 from oauth2client.client import Credentials |
| 54 from oauth2client.client import Storage as BaseStorage | 54 from oauth2client.client import Storage as BaseStorage |
| 55 from oauth2client import util | 55 from oauth2client import util |
| 56 from oauth2client.locked_file import LockedFile | 56 from oauth2client.contrib.locked_file import LockedFile |
| 57 | 57 |
| 58 | 58 |
| 59 __author__ = 'jbeda@google.com (Joe Beda)' | 59 __author__ = 'jbeda@google.com (Joe Beda)' |
| 60 | 60 |
| 61 logger = logging.getLogger(__name__) | 61 logger = logging.getLogger(__name__) |
| 62 | 62 |
| 63 # A dict from 'filename'->_MultiStore instances | 63 # A dict from 'filename'->_MultiStore instances |
| 64 _multistores = {} | 64 _multistores = {} |
| 65 _multistores_lock = threading.Lock() | 65 _multistores_lock = threading.Lock() |
| 66 | 66 |
| 67 | 67 |
| 68 class Error(Exception): | 68 class Error(Exception): |
| 69 """Base error for this module.""" | 69 """Base error for this module.""" |
| 70 | 70 |
| 71 | 71 |
| 72 class NewerCredentialStoreError(Error): | 72 class NewerCredentialStoreError(Error): |
| 73 """The credential store is a newer version than supported.""" | 73 """The credential store is a newer version than supported.""" |
| 74 | 74 |
| 75 | 75 |
| 76 def _dict_to_tuple_key(dictionary): |
| 77 """Converts a dictionary to a tuple that can be used as an immutable key. |
| 78 |
| 79 The resulting key is always sorted so that logically equivalent |
| 80 dictionaries always produce an identical tuple for a key. |
| 81 |
| 82 Args: |
| 83 dictionary: the dictionary to use as the key. |
| 84 |
| 85 Returns: |
| 86 A tuple representing the dictionary in it's naturally sorted ordering. |
| 87 """ |
| 88 return tuple(sorted(dictionary.items())) |
| 89 |
| 90 |
| 76 @util.positional(4) | 91 @util.positional(4) |
| 77 def get_credential_storage(filename, client_id, user_agent, scope, | 92 def get_credential_storage(filename, client_id, user_agent, scope, |
| 78 warn_on_readonly=True): | 93 warn_on_readonly=True): |
| 79 """Get a Storage instance for a credential. | 94 """Get a Storage instance for a credential. |
| 80 | 95 |
| 81 Args: | 96 Args: |
| 82 filename: The JSON file storing a set of credentials | 97 filename: The JSON file storing a set of credentials |
| 83 client_id: The client_id for the credential | 98 client_id: The client_id for the credential |
| 84 user_agent: The user agent for the credential | 99 user_agent: The user agent for the credential |
| 85 scope: string or iterable of strings, Scope(s) being requested | 100 scope: string or iterable of strings, Scope(s) being requested |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 132 key_dict: A dictionary to use as the key for storing this credential. | 147 key_dict: A dictionary to use as the key for storing this credential. |
| 133 There is no ordering of the keys in the dictionary. Logically | 148 There is no ordering of the keys in the dictionary. Logically |
| 134 equivalent dictionaries will produce equivalent storage keys. | 149 equivalent dictionaries will produce equivalent storage keys. |
| 135 warn_on_readonly: if True, log a warning if the store is readonly | 150 warn_on_readonly: if True, log a warning if the store is readonly |
| 136 | 151 |
| 137 Returns: | 152 Returns: |
| 138 An object derived from client.Storage for getting/setting the | 153 An object derived from client.Storage for getting/setting the |
| 139 credential. | 154 credential. |
| 140 """ | 155 """ |
| 141 multistore = _get_multistore(filename, warn_on_readonly=warn_on_readonly) | 156 multistore = _get_multistore(filename, warn_on_readonly=warn_on_readonly) |
| 142 key = util.dict_to_tuple_key(key_dict) | 157 key = _dict_to_tuple_key(key_dict) |
| 143 return multistore._get_storage(key) | 158 return multistore._get_storage(key) |
| 144 | 159 |
| 145 | 160 |
| 146 @util.positional(1) | 161 @util.positional(1) |
| 147 def get_all_credential_keys(filename, warn_on_readonly=True): | 162 def get_all_credential_keys(filename, warn_on_readonly=True): |
| 148 """Gets all the registered credential keys in the given Multistore. | 163 """Gets all the registered credential keys in the given Multistore. |
| 149 | 164 |
| 150 Args: | 165 Args: |
| 151 filename: The JSON file storing a set of credentials | 166 filename: The JSON file storing a set of credentials |
| 152 warn_on_readonly: if True, log a warning if the store is readonly | 167 warn_on_readonly: if True, log a warning if the store is readonly |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 286 except IOError as e: | 301 except IOError as e: |
| 287 if e.errno == errno.ENOSYS: | 302 if e.errno == errno.ENOSYS: |
| 288 logger.warn('File system does not support locking the ' | 303 logger.warn('File system does not support locking the ' |
| 289 'credentials file.') | 304 'credentials file.') |
| 290 elif e.errno == errno.ENOLCK: | 305 elif e.errno == errno.ENOLCK: |
| 291 logger.warn('File system is out of resources for writing the ' | 306 logger.warn('File system is out of resources for writing the ' |
| 292 'credentials file (is your disk full?).') | 307 'credentials file (is your disk full?).') |
| 293 elif e.errno == errno.EDEADLK: | 308 elif e.errno == errno.EDEADLK: |
| 294 logger.warn('Lock contention on multistore file, opening ' | 309 logger.warn('Lock contention on multistore file, opening ' |
| 295 'in read-only mode.') | 310 'in read-only mode.') |
| 311 elif e.errno == errno.EACCES: |
| 312 logger.warn('Cannot access credentials file.') |
| 296 else: | 313 else: |
| 297 raise | 314 raise |
| 298 if not self._file.is_locked(): | 315 if not self._file.is_locked(): |
| 299 self._read_only = True | 316 self._read_only = True |
| 300 if self._warn_on_readonly: | 317 if self._warn_on_readonly: |
| 301 logger.warn('The credentials file (%s) is not writable. ' | 318 logger.warn('The credentials file (%s) is not writable. ' |
| 302 'Opening in read-only mode. Any refreshed ' | 319 'Opening in read-only mode. Any refreshed ' |
| 303 'credentials will only be ' | 320 'credentials will only be ' |
| 304 'valid for this run.', self._file.filename()) | 321 'valid for this run.', self._file.filename()) |
| 305 if os.path.getsize(self._file.filename()) == 0: | 322 if os.path.getsize(self._file.filename()) == 0: |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 395 """Load a credential from our JSON serialization. | 412 """Load a credential from our JSON serialization. |
| 396 | 413 |
| 397 Args: | 414 Args: |
| 398 cred_entry: A dict entry from the data member of our format | 415 cred_entry: A dict entry from the data member of our format |
| 399 | 416 |
| 400 Returns: | 417 Returns: |
| 401 (key, cred) where the key is the key tuple and the cred is the | 418 (key, cred) where the key is the key tuple and the cred is the |
| 402 OAuth2Credential object. | 419 OAuth2Credential object. |
| 403 """ | 420 """ |
| 404 raw_key = cred_entry['key'] | 421 raw_key = cred_entry['key'] |
| 405 key = util.dict_to_tuple_key(raw_key) | 422 key = _dict_to_tuple_key(raw_key) |
| 406 credential = None | 423 credential = None |
| 407 credential = Credentials.new_from_json( | 424 credential = Credentials.new_from_json( |
| 408 json.dumps(cred_entry['credential'])) | 425 json.dumps(cred_entry['credential'])) |
| 409 return (key, credential) | 426 return (key, credential) |
| 410 | 427 |
| 411 def _write(self): | 428 def _write(self): |
| 412 """Write the cached data back out. | 429 """Write the cached data back out. |
| 413 | 430 |
| 414 The multistore must be locked. | 431 The multistore must be locked. |
| 415 """ | 432 """ |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 475 | 492 |
| 476 This Storage is a 'view' into the multistore. | 493 This Storage is a 'view' into the multistore. |
| 477 | 494 |
| 478 Args: | 495 Args: |
| 479 key: The key used to retrieve the credential | 496 key: The key used to retrieve the credential |
| 480 | 497 |
| 481 Returns: | 498 Returns: |
| 482 A Storage object that can be used to get/set this cred | 499 A Storage object that can be used to get/set this cred |
| 483 """ | 500 """ |
| 484 return self._Storage(self, key) | 501 return self._Storage(self, key) |
| OLD | NEW |