Skip to content
This repository has been archived by the owner on Nov 5, 2019. It is now read-only.

Adding dictionary storage for #319. #351

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/source/oauth2client.contrib.dictionary_storage.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
oauth2client.contrib.dictionary_storage module
==============================================

.. automodule:: oauth2client.contrib.dictionary_storage
:members:
:undoc-members:
:show-inheritance:
1 change: 1 addition & 0 deletions docs/source/oauth2client.contrib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Submodules
.. toctree::

oauth2client.contrib.appengine
oauth2client.contrib.dictionary_storage
oauth2client.contrib.django_orm
oauth2client.contrib.flask_util
oauth2client.contrib.gce
Expand Down
16 changes: 13 additions & 3 deletions oauth2client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,21 +346,31 @@ class Storage(object):
such that multiple processes and threads can operate on a single
store.
"""
def __init__(self, lock=None):
"""Create a Storage instance.

Args:
lock: An optional threading.Lock-like object. Must implement at
least acquire() and release(). Does not need to be re-entrant.
"""
self._lock = lock

def acquire_lock(self):
"""Acquires any lock necessary to access this Storage.

This lock is not reentrant.
"""
pass
if self._lock is not None:
self._lock.acquire()

def release_lock(self):
"""Release the Storage lock.

Trying to release a lock that isn't held will result in a
RuntimeError.
RuntimeError in the case of a threading.Lock or multiprocessing.Lock.
"""
pass
if self._lock is not None:
self._lock.release()

def locked_get(self):
"""Retrieve credential.
Expand Down
2 changes: 2 additions & 0 deletions oauth2client/contrib/appengine.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,8 @@ def __init__(self, model, key_name, property_name, cache=None, user=None):
user: users.User object, optional. Can be used to grab user ID as a
key_name if no key name is specified.
"""
super(StorageByKeyName, self).__init__()

if key_name is None:
if user is None:
raise ValueError('StorageByKeyName called with no '
Expand Down
58 changes: 58 additions & 0 deletions oauth2client/contrib/dictionary_storage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Copyright 2014 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."""

def __init__(self, dictionary, key, lock=None):
"""Construct a DictionaryStorage instance.

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.
"""
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."""
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."""
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)
1 change: 1 addition & 0 deletions oauth2client/contrib/django_orm.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ def __init__(self, model_class, key_name, key_value, property_name):
property_name: string, name of the property that is an
CredentialsProperty
"""
super(Storage, self).__init__()
self.model_class = model_class
self.key_name = key_name
self.key_value = key_value
Expand Down
1 change: 1 addition & 0 deletions oauth2client/contrib/django_util/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class DjangoSessionStorage(client.Storage):
"""Storage implementation that uses Django sessions."""

def __init__(self, session):
super(DjangoSessionStorage, self).__init__()
self.session = session

def locked_get(self):
Expand Down
33 changes: 2 additions & 31 deletions oauth2client/contrib/flask_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,8 @@ def requires_calendar():
raise ImportError('The flask utilities require flask 0.9 or newer.')

from oauth2client.client import FlowExchangeError
from oauth2client.client import OAuth2Credentials
from oauth2client.client import OAuth2WebServerFlow
from oauth2client.client import Storage
from oauth2client.contrib.dictionary_storage import DictionaryStorage
from oauth2client import clientsecrets


Expand Down Expand Up @@ -264,7 +263,7 @@ def init_app(self, app, scopes=None, client_secrets_file=None,
self.flow_kwargs = kwargs

if storage is None:
storage = FlaskSessionStorage()
storage = DictionaryStorage(session, _CREDENTIALS_KEY)
self.storage = storage

if scopes is None:
Expand Down Expand Up @@ -548,31 +547,3 @@ def http(self, *args, **kwargs):
if not self.credentials:
raise ValueError('No credentials available.')
return self.credentials.authorize(httplib2.Http(*args, **kwargs))


class FlaskSessionStorage(Storage):
"""Storage implementation that uses Flask sessions.

Note that flask's default sessions are signed but not encrypted. Users
can see their own credentials and non-https connections can intercept user
credentials. We strongly recommend using a server-side session
implementation.
"""

def locked_get(self):
serialized = session.get(_CREDENTIALS_KEY)

if serialized is None:
return None

credentials = OAuth2Credentials.from_json(serialized)
credentials.set_store(self)

return credentials

def locked_put(self, credentials):
session[_CREDENTIALS_KEY] = credentials.to_json()

def locked_delete(self):
if _CREDENTIALS_KEY in session:
del session[_CREDENTIALS_KEY]
17 changes: 1 addition & 16 deletions oauth2client/contrib/keyring_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,24 +59,9 @@ def __init__(self, service_name, user_name):
credentials are stored.
user_name: string, The name of the user to store credentials for.
"""
super(Storage, self).__init__(lock=threading.Lock())
self._service_name = service_name
self._user_name = user_name
self._lock = threading.Lock()

def acquire_lock(self):
"""Acquires any lock necessary to access this Storage.

This lock is not reentrant.
"""
self._lock.acquire()

def release_lock(self):
"""Release the Storage lock.

Trying to release a lock that isn't held will result in a
RuntimeError.
"""
self._lock.release()

def locked_get(self):
"""Retrieve Credential from file.
Expand Down
17 changes: 1 addition & 16 deletions oauth2client/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,29 +36,14 @@ class Storage(BaseStorage):
"""Store and retrieve a single credential to and from a file."""

def __init__(self, filename):
super(Storage, self).__init__(lock=threading.Lock())
self._filename = filename
self._lock = threading.Lock()

def _validate_file(self):
if os.path.islink(self._filename):
raise CredentialsFileSymbolicLinkError(
'File: %s is a symbolic link.' % self._filename)

def acquire_lock(self):
"""Acquires any lock necessary to access this Storage.

This lock is not reentrant.
"""
self._lock.acquire()

def release_lock(self):
"""Release the Storage lock.

Trying to release a lock that isn't held will result in a
RuntimeError.
"""
self._lock.release()

def locked_get(self):
"""Retrieve Credential from file.

Expand Down
65 changes: 65 additions & 0 deletions tests/contrib/test_dictionary_storage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Copyright 2014 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.dictionary_storage"""

import unittest

from oauth2client import GOOGLE_TOKEN_URI
from oauth2client.client import OAuth2Credentials
from oauth2client.contrib.dictionary_storage import DictionaryStorage


__author__ = '[email protected] (Jon Wayne Parrott)'


class DictionaryStorageTests(unittest.TestCase):

def _generate_credentials(self, scopes=None):
return OAuth2Credentials(
'access_tokenz',
'client_idz',
'client_secretz',
'refresh_tokenz',
'3600',
GOOGLE_TOKEN_URI,
'Test',
id_token={
'sub': '123',
'email': '[email protected]'
},
scopes=scopes)

def test_string_key(self):
dictionary = {}
key = 'credentials'
credentials = self._generate_credentials()
storage = DictionaryStorage(dictionary, key)

storage.put(credentials)

self.assertTrue(key in dictionary)

retrieved = storage.get()

self.assertEqual(retrieved.access_token, credentials.access_token)

storage.delete()

self.assertTrue(key not in dictionary)
self.assertTrue(storage.get() is None)


if __name__ == '__main__': # pragma: NO COVER
unittest.main()