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