def validate_authorization_request(self, uri, http_method='GET', body=None, headers=None): """Extract response_type and route to the designated handler.""" request = Request( uri, http_method=http_method, body=body, headers=headers) request.scopes = None response_type_handler = self.response_types.get( request.response_type, self.default_response_type_handler) return response_type_handler.validate_authorization_request(request)
def verify_request(self, uri, http_method='GET', body=None, headers=None, scopes=None): """Validate client, code etc, return body + headers""" request = Request(uri, http_method, body, headers) request.token_type = self.find_token_type(request) request.scopes = scopes token_type_handler = self.tokens.get(request.token_type, self.default_token_type_handler) log.debug('Dispatching token_type %s request to %r.', request.token_type, token_type_handler) return token_type_handler.validate_request(request), request
def create_token_response(self, uri, http_method='GET', body=None, headers=None, credentials=None): """Extract grant_type and route to the designated handler.""" request = Request( uri, http_method=http_method, body=body, headers=headers) request.scopes = None request.extra_credentials = credentials grant_type_handler = self.grant_types.get(request.grant_type, self.default_grant_type_handler) log.debug('Dispatching grant_type %s request to %r.', request.grant_type, grant_type_handler) return grant_type_handler.create_token_response( request, self.default_token_type)
def validate_authorization_request(self, uri, http_method='GET', body=None, headers=None): """Extract response_type and route to the designated handler.""" request = Request(uri, http_method=http_method, body=body, headers=headers) request.scopes = None response_type_handler = self.response_types.get( request.response_type, self.default_response_type_handler) return response_type_handler.validate_authorization_request(request)
def create_authorization_response(self, uri, http_method='GET', body=None, headers=None, scopes=None, credentials=None): """Extract response_type and route to the designated handler.""" request = Request( uri, http_method=http_method, body=body, headers=headers) request.scopes = scopes # TODO: decide whether this should be a required argument request.user = None # TODO: explain this in docs for k, v in (credentials or {}).items(): setattr(request, k, v) response_type_handler = self.response_types.get( request.response_type, self.default_response_type_handler) log.debug('Dispatching response_type %s request to %r.', request.response_type, response_type_handler) return response_type_handler.create_authorization_response( request, self.default_token_type)
def create_token_response(self, uri, http_method='GET', body=None, headers=None, credentials=None): """Extract grant_type and route to the designated handler.""" request = Request(uri, http_method=http_method, body=body, headers=headers) request.scopes = None request.extra_credentials = credentials grant_type_handler = self.grant_types.get( request.grant_type, self.default_grant_type_handler) log.debug('Dispatching grant_type %s request to %r.', request.grant_type, grant_type_handler) return grant_type_handler.create_token_response( request, self.default_token_type)
def create_authorization_response(self, uri, http_method='GET', body=None, headers=None, scopes=None, credentials=None): """Extract response_type and route to the designated handler.""" request = Request(uri, http_method=http_method, body=body, headers=headers) request.scopes = scopes # TODO: decide whether this should be a required argument request.user = None # TODO: explain this in docs for k, v in (credentials or {}).items(): setattr(request, k, v) response_type_handler = self.response_types.get( request.response_type, self.default_response_type_handler) log.debug('Dispatching response_type %s request to %r.', request.response_type, response_type_handler) return response_type_handler.create_authorization_response( request, self.default_token_type)
def create_revocation_response(self, uri, http_method='POST', body=None, headers=None): """Revoke supplied access or refresh token. The authorization server responds with HTTP status code 200 if the token has been revoked sucessfully or if the client submitted an invalid token. Note: invalid tokens do not cause an error response since the client cannot handle such an error in a reasonable way. Moreover, the purpose of the revocation request, invalidating the particular token, is already achieved. The content of the response body is ignored by the client as all necessary information is conveyed in the response code. An invalid token type hint value is ignored by the authorization server and does not influence the revocation response. """ request = Request(uri, http_method=http_method, body=body, headers=headers) try: self.validate_revocation_request(request) log.debug('Token revocation valid for %r.', request) except OAuth2Error as e: log.debug('Client error during validation of %r. %r.', request, e) response_body = e.json if self.enable_jsonp and request.callback: response_body = '%s(%s);' % (request.callback, response_body) return {}, response_body, e.status_code self.request_validator.revoke_token(request.token, request.token_type_hint, request) response_body = None if self.enable_jsonp and request.callback: response_body = request.callback + '();' return {}, response_body, 200
def sign(self, uri, http_method='GET', body=None, headers=None, realm=None): """Sign a request Signs an HTTP request with the specified parts. Returns a 3-tuple of the signed request's URI, headers, and body. Note that http_method is not returned as it is unaffected by the OAuth signing process. Also worth noting is that duplicate parameters will be included in the signature, regardless of where they are specified (query, body). The body argument may be a dict, a list of 2-tuples, or a formencoded string. The Content-Type header must be 'application/x-www-form-urlencoded' if it is present. If the body argument is not one of the above, it will be returned verbatim as it is unaffected by the OAuth signing process. Attempting to sign a request with non-formencoded data using the OAuth body signature type is invalid and will raise an exception. If the body does contain parameters, it will be returned as a properly- formatted formencoded string. Body may not be included if the http_method is either GET or HEAD as this changes the semantic meaning of the request. All string data MUST be unicode or be encoded with the same encoding scheme supplied to the Client constructor, default utf-8. This includes strings inside body dicts, for example. """ # normalize request data request = Request(uri, http_method, body, headers, encoding=self.encoding) # sanity check content_type = request.headers.get('Content-Type', None) multipart = content_type and content_type.startswith('multipart/') should_have_params = content_type == CONTENT_TYPE_FORM_URLENCODED has_params = request.decoded_body is not None # 3.4.1.3.1. Parameter Sources # [Parameters are collected from the HTTP request entity-body, but only # if [...]: # * The entity-body is single-part. if multipart and has_params: raise ValueError( "Headers indicate a multipart body but body contains parameters." ) # * The entity-body follows the encoding requirements of the # "application/x-www-form-urlencoded" content-type as defined by # [W3C.REC-html40-19980424]. elif should_have_params and not has_params: raise ValueError( "Headers indicate a formencoded body but body was not decodable." ) # * The HTTP request entity-header includes the "Content-Type" # header field set to "application/x-www-form-urlencoded". elif not should_have_params and has_params: raise ValueError( "Body contains parameters but Content-Type header was not set." ) # 3.5.2. Form-Encoded Body # Protocol parameters can be transmitted in the HTTP request entity- # body, but only if the following REQUIRED conditions are met: # o The entity-body is single-part. # o The entity-body follows the encoding requirements of the # "application/x-www-form-urlencoded" content-type as defined by # [W3C.REC-html40-19980424]. # o The HTTP request entity-header includes the "Content-Type" header # field set to "application/x-www-form-urlencoded". elif self.signature_type == SIGNATURE_TYPE_BODY and not ( should_have_params and has_params and not multipart): raise ValueError( 'Body signatures may only be used with form-urlencoded content' ) # We amend http://tools.ietf.org/html/rfc5849#section-3.4.1.3.1 # with the clause that parameters from body should only be included # in non GET or HEAD requests. Extracting the request body parameters # and including them in the signature base string would give semantic # meaning to the body, which it should not have according to the # HTTP 1.1 spec. elif http_method.upper() in ('GET', 'HEAD') and has_params: raise ValueError('GET/HEAD requests should not include body.') # generate the basic OAuth parameters request.oauth_params = self.get_oauth_params(request) # generate the signature request.oauth_params.append( ('oauth_signature', self.get_oauth_signature(request))) # render the signed request and return it uri, headers, body = self._render(request, formencode=True, realm=(realm or self.realm)) if self.decoding: log.debug('Encoding URI, headers and body to %s.', self.decoding) uri = uri.encode(self.decoding) body = body.encode(self.decoding) if body else body new_headers = {} for k, v in headers.items(): new_headers[k.encode(self.decoding)] = v.encode(self.decoding) headers = new_headers return uri, headers, body
def _create_request(self, uri, http_method, body, headers): # Only include body data from x-www-form-urlencoded requests headers = headers or {} if ("Content-Type" in headers and CONTENT_TYPE_FORM_URLENCODED in headers["Content-Type"]): request = Request(uri, http_method, body, headers) else: request = Request(uri, http_method, '', headers) signature_type, params, oauth_params = ( self._get_signature_type_and_params(request)) # The server SHOULD return a 400 (Bad Request) status code when # receiving a request with duplicated protocol parameters. if len(dict(oauth_params)) != len(oauth_params): raise errors.InvalidRequestError( description='Duplicate OAuth2 entries.') oauth_params = dict(oauth_params) request.signature = oauth_params.get('oauth_signature') request.client_key = oauth_params.get('oauth_consumer_key') request.resource_owner_key = oauth_params.get('oauth_token') request.nonce = oauth_params.get('oauth_nonce') request.timestamp = oauth_params.get('oauth_timestamp') request.redirect_uri = oauth_params.get('oauth_callback') request.verifier = oauth_params.get('oauth_verifier') request.signature_method = oauth_params.get('oauth_signature_method') request.realm = dict(params).get('realm') request.oauth_params = oauth_params # Parameters to Client depend on signature method which may vary # for each request. Note that HMAC-SHA1 and PLAINTEXT share parameters request.params = [(k, v) for k, v in params if k != "oauth_signature"] if 'realm' in request.headers.get('Authorization', ''): request.params = [(k, v) for k, v in request.params if k != "realm"] return request
def sign(self, uri, http_method='GET', body=None, headers=None, realm=None): """Sign a request Signs an HTTP request with the specified parts. Returns a 3-tuple of the signed request's URI, headers, and body. Note that http_method is not returned as it is unaffected by the OAuth signing process. Also worth noting is that duplicate parameters will be included in the signature, regardless of where they are specified (query, body). The body argument may be a dict, a list of 2-tuples, or a formencoded string. The Content-Type header must be 'application/x-www-form-urlencoded' if it is present. If the body argument is not one of the above, it will be returned verbatim as it is unaffected by the OAuth signing process. Attempting to sign a request with non-formencoded data using the OAuth body signature type is invalid and will raise an exception. If the body does contain parameters, it will be returned as a properly- formatted formencoded string. Body may not be included if the http_method is either GET or HEAD as this changes the semantic meaning of the request. All string data MUST be unicode or be encoded with the same encoding scheme supplied to the Client constructor, default utf-8. This includes strings inside body dicts, for example. """ # normalize request data request = Request(uri, http_method, body, headers, encoding=self.encoding) # sanity check content_type = request.headers.get('Content-Type', None) multipart = content_type and content_type.startswith('multipart/') should_have_params = content_type == CONTENT_TYPE_FORM_URLENCODED has_params = request.decoded_body is not None # 3.4.1.3.1. Parameter Sources # [Parameters are collected from the HTTP request entity-body, but only # if [...]: # * The entity-body is single-part. if multipart and has_params: raise ValueError( "Headers indicate a multipart body but body contains parameters.") # * The entity-body follows the encoding requirements of the # "application/x-www-form-urlencoded" content-type as defined by # [W3C.REC-html40-19980424]. elif should_have_params and not has_params: raise ValueError( "Headers indicate a formencoded body but body was not decodable.") # * The HTTP request entity-header includes the "Content-Type" # header field set to "application/x-www-form-urlencoded". elif not should_have_params and has_params: raise ValueError( "Body contains parameters but Content-Type header was not set.") # 3.5.2. Form-Encoded Body # Protocol parameters can be transmitted in the HTTP request entity- # body, but only if the following REQUIRED conditions are met: # o The entity-body is single-part. # o The entity-body follows the encoding requirements of the # "application/x-www-form-urlencoded" content-type as defined by # [W3C.REC-html40-19980424]. # o The HTTP request entity-header includes the "Content-Type" header # field set to "application/x-www-form-urlencoded". elif self.signature_type == SIGNATURE_TYPE_BODY and not ( should_have_params and has_params and not multipart): raise ValueError( 'Body signatures may only be used with form-urlencoded content') # We amend http://tools.ietf.org/html/rfc5849#section-3.4.1.3.1 # with the clause that parameters from body should only be included # in non GET or HEAD requests. Extracting the request body parameters # and including them in the signature base string would give semantic # meaning to the body, which it should not have according to the # HTTP 1.1 spec. elif http_method.upper() in ('GET', 'HEAD') and has_params: raise ValueError('GET/HEAD requests should not include body.') # generate the basic OAuth parameters request.oauth_params = self.get_oauth_params(request) # generate the signature request.oauth_params.append( ('oauth_signature', self.get_oauth_signature(request))) # render the signed request and return it uri, headers, body = self._render(request, formencode=True, realm=(realm or self.realm)) if self.decoding: log.debug('Encoding URI, headers and body to %s.', self.decoding) uri = uri.encode(self.decoding) body = body.encode(self.decoding) if body else body new_headers = {} for k, v in headers.items(): new_headers[k.encode(self.decoding)] = v.encode(self.decoding) headers = new_headers return uri, headers, body