1# Copyright 2016 Google Inc. All rights reserved. 2# 3# Licensed under the Apache License, Version 2.0 (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 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15"""OAuth 2.0 utilities for SQLAlchemy. 16 17Utilities for using OAuth 2.0 in conjunction with a SQLAlchemy. 18 19Configuration 20============= 21 22In order to use this storage, you'll need to create table 23with :class:`oauth2client.contrib.sqlalchemy.CredentialsType` column. 24It's recommended to either put this column on some sort of user info 25table or put the column in a table with a belongs-to relationship to 26a user info table. 27 28Here's an example of a simple table with a :class:`CredentialsType` 29column that's related to a user table by the `user_id` key. 30 31.. code-block:: python 32 33 from sqlalchemy import Column, ForeignKey, Integer 34 from sqlalchemy.ext.declarative import declarative_base 35 from sqlalchemy.orm import relationship 36 37 from oauth2client.contrib.sqlalchemy import CredentialsType 38 39 40 Base = declarative_base() 41 42 43 class Credentials(Base): 44 __tablename__ = 'credentials' 45 46 user_id = Column(Integer, ForeignKey('user.id')) 47 credentials = Column(CredentialsType) 48 49 50 class User(Base): 51 id = Column(Integer, primary_key=True) 52 # bunch of other columns 53 credentials = relationship('Credentials') 54 55 56Usage 57===== 58 59With tables ready, you are now able to store credentials in database. 60We will reuse tables defined above. 61 62.. code-block:: python 63 64 from sqlalchemy.orm import Session 65 66 from oauth2client.client import OAuth2Credentials 67 from oauth2client.contrib.sql_alchemy import Storage 68 69 session = Session() 70 user = session.query(User).first() 71 storage = Storage( 72 session=session, 73 model_class=Credentials, 74 # This is the key column used to identify 75 # the row that stores the credentials. 76 key_name='user_id', 77 key_value=user.id, 78 property_name='credentials', 79 ) 80 81 # Store 82 credentials = OAuth2Credentials(...) 83 storage.put(credentials) 84 85 # Retrieve 86 credentials = storage.get() 87 88 # Delete 89 storage.delete() 90 91""" 92 93from __future__ import absolute_import 94 95import sqlalchemy.types 96 97from oauth2client import client 98 99 100class CredentialsType(sqlalchemy.types.PickleType): 101 """Type representing credentials. 102 103 Alias for :class:`sqlalchemy.types.PickleType`. 104 """ 105 106 107class Storage(client.Storage): 108 """Store and retrieve a single credential to and from SQLAlchemy. 109 This helper presumes the Credentials 110 have been stored as a Credentials column 111 on a db model class. 112 """ 113 114 def __init__(self, session, model_class, key_name, 115 key_value, property_name): 116 """Constructor for Storage. 117 118 Args: 119 session: An instance of :class:`sqlalchemy.orm.Session`. 120 model_class: SQLAlchemy declarative mapping. 121 key_name: string, key name for the entity that has the credentials 122 key_value: key value for the entity that has the credentials 123 property_name: A string indicating which property on the 124 ``model_class`` to store the credentials. 125 This property must be a 126 :class:`CredentialsType` column. 127 """ 128 super(Storage, self).__init__() 129 130 self.session = session 131 self.model_class = model_class 132 self.key_name = key_name 133 self.key_value = key_value 134 self.property_name = property_name 135 136 def locked_get(self): 137 """Retrieve stored credential. 138 139 Returns: 140 A :class:`oauth2client.Credentials` instance or `None`. 141 """ 142 filters = {self.key_name: self.key_value} 143 query = self.session.query(self.model_class).filter_by(**filters) 144 entity = query.first() 145 146 if entity: 147 credential = getattr(entity, self.property_name) 148 if credential and hasattr(credential, 'set_store'): 149 credential.set_store(self) 150 return credential 151 else: 152 return None 153 154 def locked_put(self, credentials): 155 """Write a credentials to the SQLAlchemy datastore. 156 157 Args: 158 credentials: :class:`oauth2client.Credentials` 159 """ 160 filters = {self.key_name: self.key_value} 161 query = self.session.query(self.model_class).filter_by(**filters) 162 entity = query.first() 163 164 if not entity: 165 entity = self.model_class(**filters) 166 167 setattr(entity, self.property_name, credentials) 168 self.session.add(entity) 169 170 def locked_delete(self): 171 """Delete credentials from the SQLAlchemy datastore.""" 172 filters = {self.key_name: self.key_value} 173 self.session.query(self.model_class).filter_by(**filters).delete() 174