Example #1
0
 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)
Example #2
0
 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
Example #3
0
 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)
Example #4
0
 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)
Example #5
0
 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
Example #6
0
 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)
Example #7
0
 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)
Example #8
0
 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)
Example #9
0
    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
Example #10
0
    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
Example #11
0
    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
Example #12
0
    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