| OLD | NEW |
| 1 # Copyright 2014 Google Inc. All rights reserved. | 1 # Copyright (C) 2010 Google Inc. |
| 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, |
| 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 # See the License for the specific language governing permissions and | 12 # See the License for the specific language governing permissions and |
| 13 # limitations under the License. | 13 # limitations under the License. |
| 14 | 14 |
| 15 """Utilities for Google App Engine | 15 """Utilities for Google App Engine |
| 16 | 16 |
| 17 Utilities for making it easier to use OAuth 2.0 on Google App Engine. | 17 Utilities for making it easier to use OAuth 2.0 on Google App Engine. |
| 18 """ | 18 """ |
| 19 | 19 |
| 20 __author__ = 'jcgregorio@google.com (Joe Gregorio)' | 20 __author__ = 'jcgregorio@google.com (Joe Gregorio)' |
| 21 | 21 |
| 22 import base64 |
| 22 import cgi | 23 import cgi |
| 23 import json | 24 import httplib2 |
| 24 import logging | 25 import logging |
| 25 import os | 26 import os |
| 26 import pickle | 27 import pickle |
| 27 import threading | 28 import threading |
| 28 | 29 import time |
| 29 import httplib2 | |
| 30 | 30 |
| 31 from google.appengine.api import app_identity | 31 from google.appengine.api import app_identity |
| 32 from google.appengine.api import memcache | 32 from google.appengine.api import memcache |
| 33 from google.appengine.api import users | 33 from google.appengine.api import users |
| 34 from google.appengine.ext import db | 34 from google.appengine.ext import db |
| 35 from google.appengine.ext import webapp | 35 from google.appengine.ext import webapp |
| 36 from google.appengine.ext.webapp.util import login_required | 36 from google.appengine.ext.webapp.util import login_required |
| 37 from google.appengine.ext.webapp.util import run_wsgi_app | 37 from google.appengine.ext.webapp.util import run_wsgi_app |
| 38 from oauth2client import GOOGLE_AUTH_URI | 38 from oauth2client import GOOGLE_AUTH_URI |
| 39 from oauth2client import GOOGLE_REVOKE_URI | 39 from oauth2client import GOOGLE_REVOKE_URI |
| 40 from oauth2client import GOOGLE_TOKEN_URI | 40 from oauth2client import GOOGLE_TOKEN_URI |
| 41 from oauth2client import clientsecrets | 41 from oauth2client import clientsecrets |
| 42 from oauth2client import util | 42 from oauth2client import util |
| 43 from oauth2client import xsrfutil | 43 from oauth2client import xsrfutil |
| 44 from oauth2client.anyjson import simplejson |
| 44 from oauth2client.client import AccessTokenRefreshError | 45 from oauth2client.client import AccessTokenRefreshError |
| 45 from oauth2client.client import AssertionCredentials | 46 from oauth2client.client import AssertionCredentials |
| 46 from oauth2client.client import Credentials | 47 from oauth2client.client import Credentials |
| 47 from oauth2client.client import Flow | 48 from oauth2client.client import Flow |
| 48 from oauth2client.client import OAuth2WebServerFlow | 49 from oauth2client.client import OAuth2WebServerFlow |
| 49 from oauth2client.client import Storage | 50 from oauth2client.client import Storage |
| 50 | 51 |
| 51 # TODO(dhermes): Resolve import issue. | 52 # TODO(dhermes): Resolve import issue. |
| 52 # This is a temporary fix for a Google internal issue. | 53 # This is a temporary fix for a Google internal issue. |
| 53 try: | 54 try: |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 151 generate and refresh its own access tokens. | 152 generate and refresh its own access tokens. |
| 152 """ | 153 """ |
| 153 | 154 |
| 154 @util.positional(2) | 155 @util.positional(2) |
| 155 def __init__(self, scope, **kwargs): | 156 def __init__(self, scope, **kwargs): |
| 156 """Constructor for AppAssertionCredentials | 157 """Constructor for AppAssertionCredentials |
| 157 | 158 |
| 158 Args: | 159 Args: |
| 159 scope: string or iterable of strings, scope(s) of the credentials being | 160 scope: string or iterable of strings, scope(s) of the credentials being |
| 160 requested. | 161 requested. |
| 161 **kwargs: optional keyword args, including: | |
| 162 service_account_id: service account id of the application. If None or | |
| 163 unspecified, the default service account for the app is used. | |
| 164 """ | 162 """ |
| 165 self.scope = util.scopes_to_string(scope) | 163 self.scope = util.scopes_to_string(scope) |
| 166 self._kwargs = kwargs | |
| 167 self.service_account_id = kwargs.get('service_account_id', None) | |
| 168 | 164 |
| 169 # Assertion type is no longer used, but still in the parent class signature. | 165 # Assertion type is no longer used, but still in the parent class signature. |
| 170 super(AppAssertionCredentials, self).__init__(None) | 166 super(AppAssertionCredentials, self).__init__(None) |
| 171 | 167 |
| 172 @classmethod | 168 @classmethod |
| 173 def from_json(cls, json_data): | 169 def from_json(cls, json): |
| 174 data = json.loads(json_data) | 170 data = simplejson.loads(json) |
| 175 return AppAssertionCredentials(data['scope']) | 171 return AppAssertionCredentials(data['scope']) |
| 176 | 172 |
| 177 def _refresh(self, http_request): | 173 def _refresh(self, http_request): |
| 178 """Refreshes the access_token. | 174 """Refreshes the access_token. |
| 179 | 175 |
| 180 Since the underlying App Engine app_identity implementation does its own | 176 Since the underlying App Engine app_identity implementation does its own |
| 181 caching we can skip all the storage hoops and just to a refresh using the | 177 caching we can skip all the storage hoops and just to a refresh using the |
| 182 API. | 178 API. |
| 183 | 179 |
| 184 Args: | 180 Args: |
| 185 http_request: callable, a callable that matches the method signature of | 181 http_request: callable, a callable that matches the method signature of |
| 186 httplib2.Http.request, used to make the refresh request. | 182 httplib2.Http.request, used to make the refresh request. |
| 187 | 183 |
| 188 Raises: | 184 Raises: |
| 189 AccessTokenRefreshError: When the refresh fails. | 185 AccessTokenRefreshError: When the refresh fails. |
| 190 """ | 186 """ |
| 191 try: | 187 try: |
| 192 scopes = self.scope.split() | 188 scopes = self.scope.split() |
| 193 (token, _) = app_identity.get_access_token( | 189 (token, _) = app_identity.get_access_token(scopes) |
| 194 scopes, service_account_id=self.service_account_id) | 190 except app_identity.Error, e: |
| 195 except app_identity.Error as e: | |
| 196 raise AccessTokenRefreshError(str(e)) | 191 raise AccessTokenRefreshError(str(e)) |
| 197 self.access_token = token | 192 self.access_token = token |
| 198 | 193 |
| 199 @property | |
| 200 def serialization_data(self): | |
| 201 raise NotImplementedError('Cannot serialize credentials for AppEngine.') | |
| 202 | |
| 203 def create_scoped_required(self): | |
| 204 return not self.scope | |
| 205 | |
| 206 def create_scoped(self, scopes): | |
| 207 return AppAssertionCredentials(scopes, **self._kwargs) | |
| 208 | |
| 209 | 194 |
| 210 class FlowProperty(db.Property): | 195 class FlowProperty(db.Property): |
| 211 """App Engine datastore Property for Flow. | 196 """App Engine datastore Property for Flow. |
| 212 | 197 |
| 213 Utility property that allows easy storage and retrieval of an | 198 Utility property that allows easy storage and retrieval of an |
| 214 oauth2client.Flow""" | 199 oauth2client.Flow""" |
| 215 | 200 |
| 216 # Tell what the user type is. | 201 # Tell what the user type is. |
| 217 data_type = Flow | 202 data_type = Flow |
| 218 | 203 |
| (...skipping 223 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 442 | 427 |
| 443 Attempts to delete using the key_name stored on the object, whether or not | 428 Attempts to delete using the key_name stored on the object, whether or not |
| 444 the given key is in the datastore. | 429 the given key is in the datastore. |
| 445 """ | 430 """ |
| 446 if self._is_ndb(): | 431 if self._is_ndb(): |
| 447 ndb.Key(self._model, self._key_name).delete() | 432 ndb.Key(self._model, self._key_name).delete() |
| 448 else: | 433 else: |
| 449 entity_key = db.Key.from_path(self._model.kind(), self._key_name) | 434 entity_key = db.Key.from_path(self._model.kind(), self._key_name) |
| 450 db.delete(entity_key) | 435 db.delete(entity_key) |
| 451 | 436 |
| 452 @db.non_transactional(allow_existing=True) | |
| 453 def locked_get(self): | 437 def locked_get(self): |
| 454 """Retrieve Credential from datastore. | 438 """Retrieve Credential from datastore. |
| 455 | 439 |
| 456 Returns: | 440 Returns: |
| 457 oauth2client.Credentials | 441 oauth2client.Credentials |
| 458 """ | 442 """ |
| 459 credentials = None | 443 credentials = None |
| 460 if self._cache: | 444 if self._cache: |
| 461 json = self._cache.get(self._key_name) | 445 json = self._cache.get(self._key_name) |
| 462 if json: | 446 if json: |
| 463 credentials = Credentials.new_from_json(json) | 447 credentials = Credentials.new_from_json(json) |
| 464 if credentials is None: | 448 if credentials is None: |
| 465 entity = self._get_entity() | 449 entity = self._get_entity() |
| 466 if entity is not None: | 450 if entity is not None: |
| 467 credentials = getattr(entity, self._property_name) | 451 credentials = getattr(entity, self._property_name) |
| 468 if self._cache: | 452 if self._cache: |
| 469 self._cache.set(self._key_name, credentials.to_json()) | 453 self._cache.set(self._key_name, credentials.to_json()) |
| 470 | 454 |
| 471 if credentials and hasattr(credentials, 'set_store'): | 455 if credentials and hasattr(credentials, 'set_store'): |
| 472 credentials.set_store(self) | 456 credentials.set_store(self) |
| 473 return credentials | 457 return credentials |
| 474 | 458 |
| 475 @db.non_transactional(allow_existing=True) | |
| 476 def locked_put(self, credentials): | 459 def locked_put(self, credentials): |
| 477 """Write a Credentials to the datastore. | 460 """Write a Credentials to the datastore. |
| 478 | 461 |
| 479 Args: | 462 Args: |
| 480 credentials: Credentials, the credentials to store. | 463 credentials: Credentials, the credentials to store. |
| 481 """ | 464 """ |
| 482 entity = self._model.get_or_insert(self._key_name) | 465 entity = self._model.get_or_insert(self._key_name) |
| 483 setattr(entity, self._property_name, credentials) | 466 setattr(entity, self._property_name, credentials) |
| 484 entity.put() | 467 entity.put() |
| 485 if self._cache: | 468 if self._cache: |
| 486 self._cache.set(self._key_name, credentials.to_json()) | 469 self._cache.set(self._key_name, credentials.to_json()) |
| 487 | 470 |
| 488 @db.non_transactional(allow_existing=True) | |
| 489 def locked_delete(self): | 471 def locked_delete(self): |
| 490 """Delete Credential from datastore.""" | 472 """Delete Credential from datastore.""" |
| 491 | 473 |
| 492 if self._cache: | 474 if self._cache: |
| 493 self._cache.delete(self._key_name) | 475 self._cache.delete(self._key_name) |
| 494 | 476 |
| 495 self._delete_entity() | 477 self._delete_entity() |
| 496 | 478 |
| 497 | 479 |
| 498 class CredentialsModel(db.Model): | 480 class CredentialsModel(db.Model): |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 564 | 546 |
| 565 return uri | 547 return uri |
| 566 | 548 |
| 567 | 549 |
| 568 class OAuth2Decorator(object): | 550 class OAuth2Decorator(object): |
| 569 """Utility for making OAuth 2.0 easier. | 551 """Utility for making OAuth 2.0 easier. |
| 570 | 552 |
| 571 Instantiate and then use with oauth_required or oauth_aware | 553 Instantiate and then use with oauth_required or oauth_aware |
| 572 as decorators on webapp.RequestHandler methods. | 554 as decorators on webapp.RequestHandler methods. |
| 573 | 555 |
| 574 :: | 556 Example: |
| 575 | 557 |
| 576 decorator = OAuth2Decorator( | 558 decorator = OAuth2Decorator( |
| 577 client_id='837...ent.com', | 559 client_id='837...ent.com', |
| 578 client_secret='Qh...wwI', | 560 client_secret='Qh...wwI', |
| 579 scope='https://www.googleapis.com/auth/plus') | 561 scope='https://www.googleapis.com/auth/plus') |
| 580 | 562 |
| 563 |
| 581 class MainHandler(webapp.RequestHandler): | 564 class MainHandler(webapp.RequestHandler): |
| 565 |
| 582 @decorator.oauth_required | 566 @decorator.oauth_required |
| 583 def get(self): | 567 def get(self): |
| 584 http = decorator.http() | 568 http = decorator.http() |
| 585 # http is authorized with the user's Credentials and can be used | 569 # http is authorized with the user's Credentials and can be used |
| 586 # in API calls | 570 # in API calls |
| 587 | 571 |
| 588 """ | 572 """ |
| 589 | 573 |
| 590 def set_credentials(self, credentials): | 574 def set_credentials(self, credentials): |
| 591 self._tls.credentials = credentials | 575 self._tls.credentials = credentials |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 659 _storage_class: "Protected" keyword argument not typically provided to | 643 _storage_class: "Protected" keyword argument not typically provided to |
| 660 this constructor. A storage class to aid in storing a Credentials object | 644 this constructor. A storage class to aid in storing a Credentials object |
| 661 for a user in the datastore. Defaults to StorageByKeyName. | 645 for a user in the datastore. Defaults to StorageByKeyName. |
| 662 _credentials_class: "Protected" keyword argument not typically provided to | 646 _credentials_class: "Protected" keyword argument not typically provided to |
| 663 this constructor. A db or ndb Model class to hold credentials. Defaults | 647 this constructor. A db or ndb Model class to hold credentials. Defaults |
| 664 to CredentialsModel. | 648 to CredentialsModel. |
| 665 _credentials_property_name: "Protected" keyword argument not typically | 649 _credentials_property_name: "Protected" keyword argument not typically |
| 666 provided to this constructor. A string indicating the name of the field | 650 provided to this constructor. A string indicating the name of the field |
| 667 on the _credentials_class where a Credentials object will be stored. | 651 on the _credentials_class where a Credentials object will be stored. |
| 668 Defaults to 'credentials'. | 652 Defaults to 'credentials'. |
| 669 **kwargs: dict, Keyword arguments are passed along as kwargs to | 653 **kwargs: dict, Keyword arguments are be passed along as kwargs to the |
| 670 the OAuth2WebServerFlow constructor. | 654 OAuth2WebServerFlow constructor. |
| 671 | |
| 672 """ | 655 """ |
| 673 self._tls = threading.local() | 656 self._tls = threading.local() |
| 674 self.flow = None | 657 self.flow = None |
| 675 self.credentials = None | 658 self.credentials = None |
| 676 self._client_id = client_id | 659 self._client_id = client_id |
| 677 self._client_secret = client_secret | 660 self._client_secret = client_secret |
| 678 self._scope = util.scopes_to_string(scope) | 661 self._scope = util.scopes_to_string(scope) |
| 679 self._auth_uri = auth_uri | 662 self._auth_uri = auth_uri |
| 680 self._token_uri = token_uri | 663 self._token_uri = token_uri |
| 681 self._revoke_uri = revoke_uri | 664 self._revoke_uri = revoke_uri |
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 808 | 791 |
| 809 def authorize_url(self): | 792 def authorize_url(self): |
| 810 """Returns the URL to start the OAuth dance. | 793 """Returns the URL to start the OAuth dance. |
| 811 | 794 |
| 812 Must only be called from with a webapp.RequestHandler subclassed method | 795 Must only be called from with a webapp.RequestHandler subclassed method |
| 813 that had been decorated with either @oauth_required or @oauth_aware. | 796 that had been decorated with either @oauth_required or @oauth_aware. |
| 814 """ | 797 """ |
| 815 url = self.flow.step1_get_authorize_url() | 798 url = self.flow.step1_get_authorize_url() |
| 816 return str(url) | 799 return str(url) |
| 817 | 800 |
| 818 def http(self, *args, **kwargs): | 801 def http(self): |
| 819 """Returns an authorized http instance. | 802 """Returns an authorized http instance. |
| 820 | 803 |
| 821 Must only be called from within an @oauth_required decorated method, or | 804 Must only be called from within an @oauth_required decorated method, or |
| 822 from within an @oauth_aware decorated method where has_credentials() | 805 from within an @oauth_aware decorated method where has_credentials() |
| 823 returns True. | 806 returns True. |
| 824 | |
| 825 Args: | |
| 826 *args: Positional arguments passed to httplib2.Http constructor. | |
| 827 **kwargs: Positional arguments passed to httplib2.Http constructor. | |
| 828 """ | 807 """ |
| 829 return self.credentials.authorize(httplib2.Http(*args, **kwargs)) | 808 return self.credentials.authorize(httplib2.Http()) |
| 830 | 809 |
| 831 @property | 810 @property |
| 832 def callback_path(self): | 811 def callback_path(self): |
| 833 """The absolute path where the callback will occur. | 812 """The absolute path where the callback will occur. |
| 834 | 813 |
| 835 Note this is the absolute path, not the absolute URI, that will be | 814 Note this is the absolute path, not the absolute URI, that will be |
| 836 calculated by the decorator at runtime. See callback_handler() for how this | 815 calculated by the decorator at runtime. See callback_handler() for how this |
| 837 should be used. | 816 should be used. |
| 838 | 817 |
| 839 Returns: | 818 Returns: |
| 840 The callback path as a string. | 819 The callback path as a string. |
| 841 """ | 820 """ |
| 842 return self._callback_path | 821 return self._callback_path |
| 843 | 822 |
| 844 | 823 |
| 845 def callback_handler(self): | 824 def callback_handler(self): |
| 846 """RequestHandler for the OAuth 2.0 redirect callback. | 825 """RequestHandler for the OAuth 2.0 redirect callback. |
| 847 | 826 |
| 848 Usage:: | 827 Usage: |
| 849 | |
| 850 app = webapp.WSGIApplication([ | 828 app = webapp.WSGIApplication([ |
| 851 ('/index', MyIndexHandler), | 829 ('/index', MyIndexHandler), |
| 852 ..., | 830 ..., |
| 853 (decorator.callback_path, decorator.callback_handler()) | 831 (decorator.callback_path, decorator.callback_handler()) |
| 854 ]) | 832 ]) |
| 855 | 833 |
| 856 Returns: | 834 Returns: |
| 857 A webapp.RequestHandler that handles the redirect back from the | 835 A webapp.RequestHandler that handles the redirect back from the |
| 858 server during the OAuth 2.0 dance. | 836 server during the OAuth 2.0 dance. |
| 859 """ | 837 """ |
| (...skipping 13 matching lines...) Expand all Loading... |
| 873 user = users.get_current_user() | 851 user = users.get_current_user() |
| 874 decorator._create_flow(self) | 852 decorator._create_flow(self) |
| 875 credentials = decorator.flow.step2_exchange(self.request.params) | 853 credentials = decorator.flow.step2_exchange(self.request.params) |
| 876 decorator._storage_class( | 854 decorator._storage_class( |
| 877 decorator._credentials_class, None, | 855 decorator._credentials_class, None, |
| 878 decorator._credentials_property_name, user=user).put(credentials) | 856 decorator._credentials_property_name, user=user).put(credentials) |
| 879 redirect_uri = _parse_state_value(str(self.request.get('state')), | 857 redirect_uri = _parse_state_value(str(self.request.get('state')), |
| 880 user) | 858 user) |
| 881 | 859 |
| 882 if decorator._token_response_param and credentials.token_response: | 860 if decorator._token_response_param and credentials.token_response: |
| 883 resp_json = json.dumps(credentials.token_response) | 861 resp_json = simplejson.dumps(credentials.token_response) |
| 884 redirect_uri = util._add_query_parameter( | 862 redirect_uri = util._add_query_parameter( |
| 885 redirect_uri, decorator._token_response_param, resp_json) | 863 redirect_uri, decorator._token_response_param, resp_json) |
| 886 | 864 |
| 887 self.redirect(redirect_uri) | 865 self.redirect(redirect_uri) |
| 888 | 866 |
| 889 return OAuth2Handler | 867 return OAuth2Handler |
| 890 | 868 |
| 891 def callback_application(self): | 869 def callback_application(self): |
| 892 """WSGI application for handling the OAuth 2.0 redirect callback. | 870 """WSGI application for handling the OAuth 2.0 redirect callback. |
| 893 | 871 |
| 894 If you need finer grained control use `callback_handler` which returns just | 872 If you need finer grained control use `callback_handler` which returns just |
| 895 the webapp.RequestHandler. | 873 the webapp.RequestHandler. |
| 896 | 874 |
| 897 Returns: | 875 Returns: |
| 898 A webapp.WSGIApplication that handles the redirect back from the | 876 A webapp.WSGIApplication that handles the redirect back from the |
| 899 server during the OAuth 2.0 dance. | 877 server during the OAuth 2.0 dance. |
| 900 """ | 878 """ |
| 901 return webapp.WSGIApplication([ | 879 return webapp.WSGIApplication([ |
| 902 (self.callback_path, self.callback_handler()) | 880 (self.callback_path, self.callback_handler()) |
| 903 ]) | 881 ]) |
| 904 | 882 |
| 905 | 883 |
| 906 class OAuth2DecoratorFromClientSecrets(OAuth2Decorator): | 884 class OAuth2DecoratorFromClientSecrets(OAuth2Decorator): |
| 907 """An OAuth2Decorator that builds from a clientsecrets file. | 885 """An OAuth2Decorator that builds from a clientsecrets file. |
| 908 | 886 |
| 909 Uses a clientsecrets file as the source for all the information when | 887 Uses a clientsecrets file as the source for all the information when |
| 910 constructing an OAuth2Decorator. | 888 constructing an OAuth2Decorator. |
| 911 | 889 |
| 912 :: | 890 Example: |
| 913 | 891 |
| 914 decorator = OAuth2DecoratorFromClientSecrets( | 892 decorator = OAuth2DecoratorFromClientSecrets( |
| 915 os.path.join(os.path.dirname(__file__), 'client_secrets.json') | 893 os.path.join(os.path.dirname(__file__), 'client_secrets.json') |
| 916 scope='https://www.googleapis.com/auth/plus') | 894 scope='https://www.googleapis.com/auth/plus') |
| 917 | 895 |
| 896 |
| 918 class MainHandler(webapp.RequestHandler): | 897 class MainHandler(webapp.RequestHandler): |
| 898 |
| 919 @decorator.oauth_required | 899 @decorator.oauth_required |
| 920 def get(self): | 900 def get(self): |
| 921 http = decorator.http() | 901 http = decorator.http() |
| 922 # http is authorized with the user's Credentials and can be used | 902 # http is authorized with the user's Credentials and can be used |
| 923 # in API calls | 903 # in API calls |
| 924 | |
| 925 """ | 904 """ |
| 926 | 905 |
| 927 @util.positional(3) | 906 @util.positional(3) |
| 928 def __init__(self, filename, scope, message=None, cache=None, **kwargs): | 907 def __init__(self, filename, scope, message=None, cache=None): |
| 929 """Constructor | 908 """Constructor |
| 930 | 909 |
| 931 Args: | 910 Args: |
| 932 filename: string, File name of client secrets. | 911 filename: string, File name of client secrets. |
| 933 scope: string or iterable of strings, scope(s) of the credentials being | 912 scope: string or iterable of strings, scope(s) of the credentials being |
| 934 requested. | 913 requested. |
| 935 message: string, A friendly string to display to the user if the | 914 message: string, A friendly string to display to the user if the |
| 936 clientsecrets file is missing or invalid. The message may contain HTML | 915 clientsecrets file is missing or invalid. The message may contain HTML |
| 937 and will be presented on the web interface for any method that uses the | 916 and will be presented on the web interface for any method that uses the |
| 938 decorator. | 917 decorator. |
| 939 cache: An optional cache service client that implements get() and set() | 918 cache: An optional cache service client that implements get() and set() |
| 940 methods. See clientsecrets.loadfile() for details. | 919 methods. See clientsecrets.loadfile() for details. |
| 941 **kwargs: dict, Keyword arguments are passed along as kwargs to | |
| 942 the OAuth2WebServerFlow constructor. | |
| 943 """ | 920 """ |
| 944 client_type, client_info = clientsecrets.loadfile(filename, cache=cache) | 921 client_type, client_info = clientsecrets.loadfile(filename, cache=cache) |
| 945 if client_type not in [ | 922 if client_type not in [ |
| 946 clientsecrets.TYPE_WEB, clientsecrets.TYPE_INSTALLED]: | 923 clientsecrets.TYPE_WEB, clientsecrets.TYPE_INSTALLED]: |
| 947 raise InvalidClientSecretsError( | 924 raise InvalidClientSecretsError( |
| 948 "OAuth2Decorator doesn't support this OAuth 2.0 flow.") | 925 'OAuth2Decorator doesn\'t support this OAuth 2.0 flow.') |
| 949 constructor_kwargs = dict(kwargs) | 926 constructor_kwargs = { |
| 950 constructor_kwargs.update({ | 927 'auth_uri': client_info['auth_uri'], |
| 951 'auth_uri': client_info['auth_uri'], | 928 'token_uri': client_info['token_uri'], |
| 952 'token_uri': client_info['token_uri'], | 929 'message': message, |
| 953 'message': message, | 930 } |
| 954 }) | |
| 955 revoke_uri = client_info.get('revoke_uri') | 931 revoke_uri = client_info.get('revoke_uri') |
| 956 if revoke_uri is not None: | 932 if revoke_uri is not None: |
| 957 constructor_kwargs['revoke_uri'] = revoke_uri | 933 constructor_kwargs['revoke_uri'] = revoke_uri |
| 958 super(OAuth2DecoratorFromClientSecrets, self).__init__( | 934 super(OAuth2DecoratorFromClientSecrets, self).__init__( |
| 959 client_info['client_id'], client_info['client_secret'], | 935 client_info['client_id'], client_info['client_secret'], |
| 960 scope, **constructor_kwargs) | 936 scope, **constructor_kwargs) |
| 961 if message is not None: | 937 if message is not None: |
| 962 self._message = message | 938 self._message = message |
| 963 else: | 939 else: |
| 964 self._message = 'Please configure your application for OAuth 2.0.' | 940 self._message = 'Please configure your application for OAuth 2.0.' |
| (...skipping 12 matching lines...) Expand all Loading... |
| 977 clientsecrets file is missing or invalid. The message may contain HTML and | 953 clientsecrets file is missing or invalid. The message may contain HTML and |
| 978 will be presented on the web interface for any method that uses the | 954 will be presented on the web interface for any method that uses the |
| 979 decorator. | 955 decorator. |
| 980 cache: An optional cache service client that implements get() and set() | 956 cache: An optional cache service client that implements get() and set() |
| 981 methods. See clientsecrets.loadfile() for details. | 957 methods. See clientsecrets.loadfile() for details. |
| 982 | 958 |
| 983 Returns: An OAuth2Decorator | 959 Returns: An OAuth2Decorator |
| 984 | 960 |
| 985 """ | 961 """ |
| 986 return OAuth2DecoratorFromClientSecrets(filename, scope, | 962 return OAuth2DecoratorFromClientSecrets(filename, scope, |
| 987 message=message, cache=cache) | 963 message=message, cache=cache) |
| OLD | NEW |