Adding dictionary storage for #319.
DictionaryStorage implements an optionally-locked storage over a dictionary-like object.
This commit is contained in:
7
docs/source/oauth2client.contrib.dictionary_storage.rst
Normal file
7
docs/source/oauth2client.contrib.dictionary_storage.rst
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
oauth2client.contrib.dictionary_storage module
|
||||||
|
==============================================
|
||||||
|
|
||||||
|
.. automodule:: oauth2client.contrib.dictionary_storage
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
@@ -15,6 +15,7 @@ Submodules
|
|||||||
|
|
||||||
oauth2client.contrib.appengine
|
oauth2client.contrib.appengine
|
||||||
oauth2client.contrib.devshell
|
oauth2client.contrib.devshell
|
||||||
|
oauth2client.contrib.dictionary_storage
|
||||||
oauth2client.contrib.django_orm
|
oauth2client.contrib.django_orm
|
||||||
oauth2client.contrib.flask_util
|
oauth2client.contrib.flask_util
|
||||||
oauth2client.contrib.gce
|
oauth2client.contrib.gce
|
||||||
|
|||||||
66
oauth2client/contrib/dictionary_storage.py
Normal file
66
oauth2client/contrib/dictionary_storage.py
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
# Copyright 2016 Google Inc. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
"""Dictionary storage for OAuth2 Credentials."""
|
||||||
|
|
||||||
|
from oauth2client.client import OAuth2Credentials
|
||||||
|
from oauth2client.client import Storage
|
||||||
|
|
||||||
|
|
||||||
|
class DictionaryStorage(Storage):
|
||||||
|
"""Store and retrieve credentials to and from a dictionary-like object.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dictionary: A dictionary or dictionary-like object.
|
||||||
|
key: A string or other hashable. The credentials will be stored in
|
||||||
|
``dictionary[key]``.
|
||||||
|
lock: An optional threading.Lock-like object. The lock will be
|
||||||
|
acquired before anything is written or read from the
|
||||||
|
dictionary.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, dictionary, key, lock=None):
|
||||||
|
"""Construct a DictionaryStorage instance."""
|
||||||
|
super(DictionaryStorage, self).__init__(lock=lock)
|
||||||
|
self._dictionary = dictionary
|
||||||
|
self._key = key
|
||||||
|
|
||||||
|
def locked_get(self):
|
||||||
|
"""Retrieve the credentials from the dictionary, if they exist.
|
||||||
|
|
||||||
|
Returns: A :class:`oauth2client.client.OAuth2Credentials` instance.
|
||||||
|
"""
|
||||||
|
serialized = self._dictionary.get(self._key)
|
||||||
|
|
||||||
|
if serialized is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
credentials = OAuth2Credentials.from_json(serialized)
|
||||||
|
credentials.set_store(self)
|
||||||
|
|
||||||
|
return credentials
|
||||||
|
|
||||||
|
def locked_put(self, credentials):
|
||||||
|
"""Save the credentials to the dictionary.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
credentials: A :class:`oauth2client.client.OAuth2Credentials`
|
||||||
|
instance.
|
||||||
|
"""
|
||||||
|
serialized = credentials.to_json()
|
||||||
|
self._dictionary[self._key] = serialized
|
||||||
|
|
||||||
|
def locked_delete(self):
|
||||||
|
"""Remove the credentials from the dictionary, if they exist."""
|
||||||
|
self._dictionary.pop(self._key, None)
|
||||||
110
tests/contrib/test_dictionary_storage.py
Normal file
110
tests/contrib/test_dictionary_storage.py
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
# Copyright 2016 Google Inc. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
"""Unit tests for oauth2client.contrib.dictionary_storage"""
|
||||||
|
|
||||||
|
import unittest2
|
||||||
|
|
||||||
|
from oauth2client import GOOGLE_TOKEN_URI
|
||||||
|
from oauth2client.client import OAuth2Credentials
|
||||||
|
from oauth2client.contrib.dictionary_storage import DictionaryStorage
|
||||||
|
|
||||||
|
|
||||||
|
def _generate_credentials(scopes=None):
|
||||||
|
return OAuth2Credentials(
|
||||||
|
'access_tokenz',
|
||||||
|
'client_idz',
|
||||||
|
'client_secretz',
|
||||||
|
'refresh_tokenz',
|
||||||
|
'3600',
|
||||||
|
GOOGLE_TOKEN_URI,
|
||||||
|
'Test',
|
||||||
|
id_token={
|
||||||
|
'sub': '123',
|
||||||
|
'email': 'user@example.com'
|
||||||
|
},
|
||||||
|
scopes=scopes)
|
||||||
|
|
||||||
|
|
||||||
|
class DictionaryStorageTests(unittest2.TestCase):
|
||||||
|
|
||||||
|
def test_constructor_defaults(self):
|
||||||
|
dictionary = {}
|
||||||
|
key = 'test-key'
|
||||||
|
storage = DictionaryStorage(dictionary, key)
|
||||||
|
|
||||||
|
self.assertEqual(dictionary, storage._dictionary)
|
||||||
|
self.assertEqual(key, storage._key)
|
||||||
|
self.assertIsNone(storage._lock)
|
||||||
|
|
||||||
|
def test_constructor_explicit(self):
|
||||||
|
dictionary = {}
|
||||||
|
key = 'test-key'
|
||||||
|
storage = DictionaryStorage(dictionary, key)
|
||||||
|
|
||||||
|
lock = object()
|
||||||
|
storage = DictionaryStorage(dictionary, key, lock=lock)
|
||||||
|
self.assertEqual(storage._lock, lock)
|
||||||
|
|
||||||
|
def test_get(self):
|
||||||
|
credentials = _generate_credentials()
|
||||||
|
dictionary = {}
|
||||||
|
key = 'credentials'
|
||||||
|
storage = DictionaryStorage(dictionary, key)
|
||||||
|
|
||||||
|
self.assertIsNone(storage.get())
|
||||||
|
|
||||||
|
dictionary[key] = credentials.to_json()
|
||||||
|
returned = storage.get()
|
||||||
|
|
||||||
|
self.assertIsNotNone(returned)
|
||||||
|
self.assertEqual(returned.access_token, credentials.access_token)
|
||||||
|
self.assertEqual(returned.id_token, credentials.id_token)
|
||||||
|
self.assertEqual(returned.refresh_token, credentials.refresh_token)
|
||||||
|
self.assertEqual(returned.client_id, credentials.client_id)
|
||||||
|
|
||||||
|
def test_put(self):
|
||||||
|
credentials = _generate_credentials()
|
||||||
|
dictionary = {}
|
||||||
|
key = 'credentials'
|
||||||
|
storage = DictionaryStorage(dictionary, key)
|
||||||
|
|
||||||
|
storage.put(credentials)
|
||||||
|
returned = storage.get()
|
||||||
|
|
||||||
|
self.assertIn(key, dictionary)
|
||||||
|
self.assertIsNotNone(returned)
|
||||||
|
self.assertEqual(returned.access_token, credentials.access_token)
|
||||||
|
self.assertEqual(returned.id_token, credentials.id_token)
|
||||||
|
self.assertEqual(returned.refresh_token, credentials.refresh_token)
|
||||||
|
self.assertEqual(returned.client_id, credentials.client_id)
|
||||||
|
|
||||||
|
def test_delete(self):
|
||||||
|
credentials = _generate_credentials()
|
||||||
|
dictionary = {}
|
||||||
|
key = 'credentials'
|
||||||
|
storage = DictionaryStorage(dictionary, key)
|
||||||
|
|
||||||
|
storage.put(credentials)
|
||||||
|
|
||||||
|
self.assertIn(key, dictionary)
|
||||||
|
|
||||||
|
storage.delete()
|
||||||
|
|
||||||
|
self.assertNotIn(key, dictionary)
|
||||||
|
self.assertIsNone(storage.get())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__': # pragma: NO COVER
|
||||||
|
unittest2.main()
|
||||||
Reference in New Issue
Block a user