def __call__(self, http, postproc, uri, method='GET', body=None, headers=None, methodId=None, resumable=None): """Implements the callable interface that discovery.build() expects of requestBuilder, which is to build an object compatible with HttpRequest.execute(). See that method for the description of the parameters and the expected response. """ if methodId in self.responses: response = self.responses[methodId] resp, content = response[:2] if len(response) > 2: # Test the body against the supplied expected_body. expected_body = response[2] if bool(expected_body) != bool(body): # Not expecting a body and provided one # or expecting a body and not provided one. raise UnexpectedBodyError(expected_body, body) if isinstance(expected_body, str): expected_body = simplejson.loads(expected_body) body = simplejson.loads(body) if body != expected_body: raise UnexpectedBodyError(expected_body, body) return HttpRequestMock(resp, content, postproc) elif self.check_unexpected: raise UnexpectedMethodError(methodId=methodId) else: model = JsonModel(False) return HttpRequestMock(None, '{}', model.response)
def _do_revoke(self, http_request, token): """Revokes the credentials and deletes the store if available. Args: http_request: callable, a callable that matches the method signature of httplib2.Http.request, used to make the refresh request. token: A string used as the token to be revoked. Can be either an access_token or refresh_token. Raises: TokenRevokeError: If the revoke request does not return with a 200 OK. """ logger.info('Revoking token') query_params = {'token': token} token_revoke_uri = _update_query_params(self.revoke_uri, query_params) resp, content = http_request(token_revoke_uri) if resp.status == 200: self.invalid = True else: error_msg = 'Invalid response %s.' % resp.status try: d = simplejson.loads(content) if 'error' in d: error_msg = d['error'] except StandardError: pass raise TokenRevokeError(error_msg) if self.store: self.store.delete()
def verify_id_token(id_token, audience, http=None, cert_uri=ID_TOKEN_VERIFICATON_CERTS): """Verifies a signed JWT id_token. This function requires PyOpenSSL and because of that it does not work on App Engine. Args: id_token: string, A Signed JWT. audience: string, The audience 'aud' that the token should be for. http: httplib2.Http, instance to use to make the HTTP request. Callers should supply an instance that has caching enabled. cert_uri: string, URI of the certificates in JSON format to verify the JWT against. Returns: The deserialized JSON in the JWT. Raises: oauth2client.crypt.AppIdentityError if the JWT fails to verify. """ if http is None: http = _cached_http resp, content = http.request(cert_uri) if resp.status == 200: certs = simplejson.loads(content) return crypt.verify_signed_jwt_with_certs(id_token, certs, audience) else: raise VerifyJwtTokenError('Status code: %d' % resp.status)
def new_from_json(cls, s): """Utility class method to instantiate a Credentials subclass from a JSON representation produced by to_json(). Args: s: string, JSON from to_json(). Returns: An instance of the subclass of Credentials that was serialized with to_json(). """ data = simplejson.loads(s) # Find and call the right classmethod from_json() to restore the object. module = data['_module'] try: m = __import__(module) except ImportError: # In case there's an object from the old package structure, update it module = module.replace('.apiclient', '') m = __import__(module) m = __import__(module, fromlist=module.split('.')[:-1]) kls = getattr(m, data['_class']) from_json = getattr(kls, 'from_json') return from_json(s)
def _parse_exchange_token_response(content): """Parses response of an exchange token request. Most providers return JSON but some (e.g. Facebook) return a url-encoded string. Args: content: The body of a response Returns: Content as a dictionary object. Note that the dict could be empty, i.e. {}. That basically indicates a failure. """ resp = {} try: resp = simplejson.loads(content) except StandardError: # different JSON libs raise different exceptions, # so we just do a catch-all here resp = dict(parse_qsl(content)) # some providers respond with 'expires', others with 'expires_in' if resp and 'expires' in resp: resp['expires_in'] = resp.pop('expires') return resp
def from_json(cls, s): """Instantiate a Credentials object from a JSON description of it. The JSON should have been produced by calling .to_json() on the object. Args: data: dict, A deserialized JSON object. Returns: An instance of a Credentials subclass. """ data = simplejson.loads(s) if 'token_expiry' in data and not isinstance(data['token_expiry'], datetime.datetime): try: data['token_expiry'] = datetime.datetime.strptime( data['token_expiry'], EXPIRY_FORMAT) except: data['token_expiry'] = None retval = cls(data['access_token'], data['client_id'], data['client_secret'], data['refresh_token'], data['token_expiry'], data['token_uri'], data['user_agent'], revoke_uri=data.get('revoke_uri', None), id_token=data.get('id_token', None), token_response=data.get('token_response', None)) retval.invalid = data['invalid'] return retval
def from_json(cls, s): """Instantiate a Credentials object from a JSON description of it. The JSON should have been produced by calling .to_json() on the object. Args: data: dict, A deserialized JSON object. Returns: An instance of a Credentials subclass. """ data = simplejson.loads(s) if 'token_expiry' in data and not isinstance(data['token_expiry'], datetime.datetime): try: data['token_expiry'] = datetime.datetime.strptime( data['token_expiry'], EXPIRY_FORMAT) except: data['token_expiry'] = None retval = cls( data['access_token'], data['client_id'], data['client_secret'], data['refresh_token'], data['token_expiry'], data['token_uri'], data['user_agent'], revoke_uri=data.get('revoke_uri', None), id_token=data.get('id_token', None), token_response=data.get('token_response', None)) retval.invalid = data['invalid'] return retval
def build(serviceName, version, http=None, discoveryServiceUrl=DISCOVERY_URI, developerKey=None, model=None, requestBuilder=HttpRequest): """Construct a Resource for interacting with an API. Construct a Resource object for interacting with an API. The serviceName and version are the names from the Discovery service. Args: serviceName: string, name of the service. version: string, the version of the service. http: httplib2.Http, An instance of httplib2.Http or something that acts like it that HTTP requests will be made through. discoveryServiceUrl: string, a URI Template that points to the location of the discovery service. It should have two parameters {api} and {apiVersion} that when filled in produce an absolute URI to the discovery document for that service. developerKey: string, key obtained from https://code.google.com/apis/console. model: apiclient.Model, converts to and from the wire format. requestBuilder: apiclient.http.HttpRequest, encapsulator for an HTTP request. Returns: A Resource object with methods for interacting with the service. """ params = {'api': serviceName, 'apiVersion': version} if http is None: http = httplib2.Http() requested_url = uritemplate.expand(discoveryServiceUrl, params) # REMOTE_ADDR is defined by the CGI spec [RFC3875] as the environment # variable that contains the network address of the client sending the # request. If it exists then add that to the request for the discovery # document to avoid exceeding the quota on discovery requests. if 'REMOTE_ADDR' in os.environ: requested_url = _add_query_parameter(requested_url, 'userIp', os.environ['REMOTE_ADDR']) logger.info('URL being requested: %s' % requested_url) resp, content = http.request(requested_url) if resp.status == 404: raise UnknownApiNameOrVersion("name: %s version: %s" % (serviceName, version)) if resp.status >= 400: raise HttpError(resp, content, uri=requested_url) try: service = simplejson.loads(content) except ValueError, e: logger.error('Failed to parse as JSON: ' + content) raise InvalidJsonError()
def _do_refresh_request(self, http_request): """Refresh the access_token using the refresh_token. Args: http_request: callable, a callable that matches the method signature of httplib2.Http.request, used to make the refresh request. Raises: AccessTokenRefreshError: When the refresh fails. """ body = self._generate_refresh_request_body() headers = self._generate_refresh_request_headers() logger.info('Refreshing access_token') resp, content = http_request(self.token_uri, method='POST', body=body, headers=headers) if resp.status == 200: # TODO(jcgregorio) Raise an error if loads fails? d = simplejson.loads(content) self.token_response = d self.access_token = d['access_token'] self.refresh_token = d.get('refresh_token', self.refresh_token) if 'expires_in' in d: self.token_expiry = datetime.timedelta( seconds=int(d['expires_in'])) + datetime.datetime.utcnow() else: self.token_expiry = None if self.store: self.store.locked_put(self) else: # An {'error':...} response body means the token is expired or revoked, # so we flag the credentials as such. logger.info('Failed to retrieve access token: %s' % content) error_msg = 'Invalid response %s.' % resp['status'] try: d = simplejson.loads(content) if 'error' in d: error_msg = d['error'] self.invalid = True if self.store: self.store.locked_put(self) except StandardError: pass raise AccessTokenRefreshError(error_msg)
def _do_refresh_request(self, http_request): """Refresh the access_token using the refresh_token. Args: http_request: callable, a callable that matches the method signature of httplib2.Http.request, used to make the refresh request. Raises: AccessTokenRefreshError: When the refresh fails. """ body = self._generate_refresh_request_body() headers = self._generate_refresh_request_headers() logger.info('Refreshing access_token') resp, content = http_request( self.token_uri, method='POST', body=body, headers=headers) if resp.status == 200: # TODO(jcgregorio) Raise an error if loads fails? d = simplejson.loads(content) self.token_response = d self.access_token = d['access_token'] self.refresh_token = d.get('refresh_token', self.refresh_token) if 'expires_in' in d: self.token_expiry = datetime.timedelta( seconds=int(d['expires_in'])) + datetime.datetime.utcnow() else: self.token_expiry = None if self.store: self.store.locked_put(self) else: # An {'error':...} response body means the token is expired or revoked, # so we flag the credentials as such. logger.info('Failed to retrieve access token: %s' % content) error_msg = 'Invalid response %s.' % resp['status'] try: d = simplejson.loads(content) if 'error' in d: error_msg = d['error'] self.invalid = True if self.store: self.store.locked_put(self) except StandardError: pass raise AccessTokenRefreshError(error_msg)
def _get_reason(self): """Calculate the reason for the error from the response content.""" reason = self.resp.reason try: data = simplejson.loads(self.content) reason = data['error']['message'] except (ValueError, KeyError): pass if reason is None: reason = '' return reason
def from_json(cls, s): data = simplejson.loads(s) retval = SignedJwtAssertionCredentials( data['service_account_name'], base64.b64decode(data['private_key']), data['scope'], private_key_password=data['private_key_password'], user_agent=data['user_agent'], token_uri=data['token_uri'], **data['kwargs']) retval.invalid = data['invalid'] retval.access_token = data['access_token'] return retval
def build_from_document(service, base=None, future=None, http=None, developerKey=None, model=None, requestBuilder=HttpRequest): """Create a Resource for interacting with an API. Same as `build()`, but constructs the Resource object from a discovery document that is it given, as opposed to retrieving one over HTTP. Args: service: string or object, the JSON discovery document describing the API. The value passed in may either be the JSON string or the deserialized JSON. base: string, base URI for all HTTP requests, usually the discovery URI. This parameter is no longer used as rootUrl and servicePath are included within the discovery document. (deprecated) future: string, discovery document with future capabilities (deprecated). http: httplib2.Http, An instance of httplib2.Http or something that acts like it that HTTP requests will be made through. developerKey: string, Key for controlling API usage, generated from the API Console. model: Model class instance that serializes and de-serializes requests and responses. requestBuilder: Takes an http request and packages it up to be executed. Returns: A Resource object with methods for interacting with the service. """ # future is no longer used. future = {} if isinstance(service, basestring): service = simplejson.loads(service) base = urlparse.urljoin(service['rootUrl'], service['servicePath']) schema = Schemas(service) if model is None: features = service.get('features', []) model = JsonModel('dataWrapper' in features) return Resource(http=http, baseUrl=base, model=model, developerKey=developerKey, requestBuilder=requestBuilder, resourceDesc=service, rootDesc=service, schema=schema)
def from_json(s, http, postproc): """Returns an HttpRequest populated with info from a JSON object.""" d = simplejson.loads(s) if d['resumable'] is not None: d['resumable'] = MediaUpload.new_from_json(d['resumable']) return HttpRequest( http, postproc, uri=d['uri'], method=d['method'], body=d['body'], headers=d['headers'], methodId=d['methodId'], resumable=d['resumable'])
def build_from_document( service, base=None, future=None, http=None, developerKey=None, model=None, requestBuilder=HttpRequest): """Create a Resource for interacting with an API. Same as `build()`, but constructs the Resource object from a discovery document that is it given, as opposed to retrieving one over HTTP. Args: service: string or object, the JSON discovery document describing the API. The value passed in may either be the JSON string or the deserialized JSON. base: string, base URI for all HTTP requests, usually the discovery URI. This parameter is no longer used as rootUrl and servicePath are included within the discovery document. (deprecated) future: string, discovery document with future capabilities (deprecated). http: httplib2.Http, An instance of httplib2.Http or something that acts like it that HTTP requests will be made through. developerKey: string, Key for controlling API usage, generated from the API Console. model: Model class instance that serializes and de-serializes requests and responses. requestBuilder: Takes an http request and packages it up to be executed. Returns: A Resource object with methods for interacting with the service. """ # future is no longer used. future = {} if isinstance(service, basestring): service = simplejson.loads(service) base = urlparse.urljoin(service['rootUrl'], service['servicePath']) schema = Schemas(service) if model is None: features = service.get('features', []) model = JsonModel('dataWrapper' in features) return Resource(http=http, baseUrl=base, model=model, developerKey=developerKey, requestBuilder=requestBuilder, resourceDesc=service, rootDesc=service, schema=schema)
def _extract_id_token(id_token): """Extract the JSON payload from a JWT. Does the extraction w/o checking the signature. Args: id_token: string, OAuth 2.0 id_token. Returns: object, The deserialized JSON payload. """ segments = id_token.split('.') if (len(segments) != 3): raise VerifyJwtTokenError( 'Wrong number of segments in token: %s' % id_token) return simplejson.loads(_urlsafe_b64decode(segments[1]))
def new_from_json(cls, s): """Utility class method to instantiate a MediaUpload subclass from a JSON representation produced by to_json(). Args: s: string, JSON from to_json(). Returns: An instance of the subclass of MediaUpload that was serialized with to_json(). """ data = simplejson.loads(s) # Find and call the right classmethod from_json() to restore the object. module = data['_module'] m = __import__(module, fromlist=module.split('.')[:-1]) kls = getattr(m, data['_class']) from_json = getattr(kls, 'from_json') return from_json(s)
def _extract_id_token(id_token): """Extract the JSON payload from a JWT. Does the extraction w/o checking the signature. Args: id_token: string, OAuth 2.0 id_token. Returns: object, The deserialized JSON payload. """ segments = id_token.split('.') if (len(segments) != 3): raise VerifyJwtTokenError('Wrong number of segments in token: %s' % id_token) return simplejson.loads(_urlsafe_b64decode(segments[1]))
def _refresh(self, http_request): """Refreshes the access_token. Skip all the storage hoops and just refresh using the API. Args: http_request: callable, a callable that matches the method signature of httplib2.Http.request, used to make the refresh request. Raises: AccessTokenRefreshError: When the refresh fails. """ uri = uritemplate.expand(META, {'scope': self.scope}) response, content = http_request(uri) if response.status == 200: try: d = simplejson.loads(content) except StandardError, e: raise AccessTokenRefreshError(str(e)) self.access_token = d['accessToken']
def build(serviceName, version, http=None, discoveryServiceUrl=DISCOVERY_URI, developerKey=None, model=None, requestBuilder=HttpRequest): """Construct a Resource for interacting with an API. Construct a Resource object for interacting with an API. The serviceName and version are the names from the Discovery service. Args: serviceName: string, name of the service. version: string, the version of the service. http: httplib2.Http, An instance of httplib2.Http or something that acts like it that HTTP requests will be made through. discoveryServiceUrl: string, a URI Template that points to the location of the discovery service. It should have two parameters {api} and {apiVersion} that when filled in produce an absolute URI to the discovery document for that service. developerKey: string, key obtained from https://code.google.com/apis/console. model: apiclient.Model, converts to and from the wire format. requestBuilder: apiclient.http.HttpRequest, encapsulator for an HTTP request. Returns: A Resource object with methods for interacting with the service. """ params = { 'api': serviceName, 'apiVersion': version } if http is None: http = httplib2.Http() requested_url = uritemplate.expand(discoveryServiceUrl, params) # REMOTE_ADDR is defined by the CGI spec [RFC3875] as the environment # variable that contains the network address of the client sending the # request. If it exists then add that to the request for the discovery # document to avoid exceeding the quota on discovery requests. if 'REMOTE_ADDR' in os.environ: requested_url = _add_query_parameter(requested_url, 'userIp', os.environ['REMOTE_ADDR']) logger.info('URL being requested: %s' % requested_url) resp, content = http.request(requested_url) if resp.status == 404: raise UnknownApiNameOrVersion("name: %s version: %s" % (serviceName, version)) if resp.status >= 400: raise HttpError(resp, content, uri=requested_url) try: service = simplejson.loads(content) except ValueError, e: logger.error('Failed to parse as JSON: ' + content) raise InvalidJsonError()
def from_json(cls, s): data = simplejson.loads(s) retval = AccessTokenCredentials(data['access_token'], data['user_agent']) return retval
def from_json(cls, s): data = simplejson.loads(s) retval = AccessTokenCredentials( data['access_token'], data['user_agent']) return retval
def deserialize(self, content): body = simplejson.loads(content) if self._data_wrapper and isinstance(body, dict) and 'data' in body: body = body['data'] return body
def from_json(s): d = simplejson.loads(s) return MediaFileUpload(d['_filename'], mimetype=d['_mimetype'], chunksize=d['_chunksize'], resumable=d['_resumable'])
def from_json(cls, json): data = simplejson.loads(json) return AppAssertionCredentials(data['scope'])