Ejemplo n.º 1
0
    def create_token_response(self, uri, http_method='GET', body=None,
                              headers=None, credentials=None, grant_type_for_scope=None,
                              claims=None):
        """Extract grant_type and route to the designated handler."""
        request = Request(
            uri, http_method=http_method, body=body, headers=headers)

        # 'scope' is an allowed Token Request param in both the "Resource Owner Password Credentials Grant"
        # and "Client Credentials Grant" flows
        # https://tools.ietf.org/html/rfc6749#section-4.3.2
        # https://tools.ietf.org/html/rfc6749#section-4.4.2
        request.scopes = utils.scope_to_list(request.scope)

        request.extra_credentials = credentials
        if grant_type_for_scope:
            request.grant_type = grant_type_for_scope

        # OpenID Connect claims, if provided.  The server using oauthlib might choose
        # to implement the claims parameter of the Authorization Request.  In this case
        # it should retrieve those claims and pass them via the claims argument here,
        # as a dict.
        if claims:
            request.claims = claims

        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)
Ejemplo n.º 2
0
def _get_authorization_header(request, client_key, client_secret):
    """
    Get proper HTTP Authorization header for a given request

    Arguments:
        request: Request object to log Authorization header for

    Returns:
        authorization header
    """
    sha1 = hashlib.sha1()
    body = request.body or ''
    sha1.update(body)
    oauth_body_hash = unicode(base64.b64encode(
        sha1.digest()  # pylint: disable=too-many-function-args
    ))
    client = Client(client_key, client_secret)
    params = client.get_oauth_params(request)
    params.append((u'oauth_body_hash', oauth_body_hash))

    blank_request = Request(urllib.unquote(request.url), http_method=request.method, body='', headers=request.headers, encoding='utf_8')
    blank_request.oauth_params = params
    blank_request.decoded_body = ''

    signature = client.get_oauth_signature(blank_request)
    blank_request.oauth_params.append((u'oauth_signature', signature))
    headers = client._render(  # pylint: disable=protected-access
        blank_request
    )[1]
    return headers['Authorization']
Ejemplo n.º 3
0
    def create_authorization_response(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)
        query_params = params_from_uri(request.uri)
        body_params = request.decoded_body

        # Prioritize response_type defined as query param over those in body.
        # Chosen because the two core grant types utilizing the response type
        # parameter both supply it in the uri. However it is not specified
        # explicitely in RFC 6748.
        if 'response_type' in query_params:
            request.response_type = query_params.get('response_type')
        elif 'response_type' in body_params:
            request.response_type = body_params.get('response_type')
        else:
            raise errors.InvalidRequestError(
                description='The response_type parameter is missing.')

        if not request.response_type in self.response_types:
            raise errors.UnsupportedResponseTypeError(
                description='Invalid response type')

        return self.response_types.get(
                request.response_type).create_authorization_response(
                        request, self.default_token)
Ejemplo n.º 4
0
    def create_authorization_response(self, uri, http_method='GET', body=None,
            headers=None, realms=None, credentials=None):
        """Create an authorization response, with a new request token if valid.

        :param uri: The full URI of the token request.
        :param http_method: A valid HTTP verb, i.e. GET, POST, PUT, HEAD, etc.
        :param body: The request body as a string.
        :param headers: The request headers as a dict.
        :param credentials: A list of credentials to include in the verifier.
        :returns: A tuple of 4 elements.
                  1. The URI to be used to redirect the user back to client.
                  2. A dict of headers to set on the response.
                  3. The response body as a string.
                  4. The response status code as an integer.

        An example of a valid request::

            >>> from your_validator import your_validator
            >>> from oauthlib.oauth1 import RequestTokenEndpoint
            >>> endpoint = RequestTokenEndpoint(your_validator)
            >>> u, h, b, s = endpoint.create_request_token_response(
            ...     'https://your.provider/request_token?foo=bar',
            ...     headers={
            ...         'Authorization': 'OAuth realm=movies user, oauth_....'
            ...     },
            ...     credentials={
            ...         'extra': 'argument',
            ...     })
            >>> u
            'https://the.client/callback?oauth_verifier=...&mextra=argument'
            >>> h
            {}
            >>> b
            ''
            >>> s
            302
        """
        request = Request(uri, http_method=http_method, body=body,
                headers=headers)

        if not self.request_validator.verify_request_token(
                request.oauth_token, request):
            raise errors.InvalidClientError()
        if not request.oauth_token:
            raise NotImplementedError('request.oauth_token must be set after '
                                      'request token verification.')

        request.realms = realms
        if (request.realms and not self.request_validator.verify_realms(
                request.oauth_token, request.realms, request)):
            raise errors.InvalidRequestError(
                    description=('User granted access to realms outside of '
                                 'what the client may request.'))

        redirect_uri = self.request_validator.get_redirect_uri(
                request.oauth_token, request)
        verifier = self.create_verifier(request, credentials or {})
        uri = add_params_to_uri(redirect_uri, verifier.items())
        return uri, {}, None, 302
Ejemplo n.º 5
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)
Ejemplo n.º 6
0
    def verify_request(self, uri, http_method='GET', body=None, headers=None):
        """Validate client, code etc, return body + headers"""
        request = Request(uri, http_method, body, headers)
        request.token_type = self.find_token_type(request)

        token_type_handler = self.tokens.get(request.token_type,
                self.default_token_type_handler)

        return token_type_handler.validate_request(request)
Ejemplo n.º 7
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
Ejemplo n.º 8
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.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)
Ejemplo n.º 9
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.authorized_scopes = scopes
     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)
     return response_type_handler.create_authorization_response(
                     request, self.default_token)
    def test_request_invalid_client_id(self):
        request = Request('https://a.b./path')
        request.scope = 'openid profile'
        request.response_type = 'code'
        with pytest.raises(errors.MissingClientIdError):
            self.auth.validate_authorization_request(request)

        request.client_id = 'invalid_client'
        self.validator.validate_client_id.return_value = False
        with pytest.raises(errors.InvalidClientIdError):
            self.auth.validate_authorization_request(request)
Ejemplo n.º 11
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)
Ejemplo n.º 12
0
    def verify_request(self, uri, http_method='GET', body=None, headers=None):
        """Validate client, code etc, return body + headers"""
        request = Request(uri, http_method, body, headers)
        request.token_type = self.find_token_type(request)

        # TODO(ib-lundgren): How to return errors is not strictly defined and
        # should allow for customization.
        if not request.token_type:
            raise ValueError('Could not determine the token type.')

        if not request.token_type in self.tokens:
            raise ValueError('Unsupported token type.')

        return self.tokens.get(request.token_type).validate_request(request)
Ejemplo n.º 13
0
    def test_header_with_multispaces_is_validated(self):
        request_validator = mock.MagicMock()
        request_validator.validate_bearer_token = self._mocked_validate_bearer_token

        request = Request("/", headers=self.valid_header_with_multiple_spaces)
        result = BearerToken(
            request_validator=request_validator).validate_request(request)

        self.assertTrue(result)
Ejemplo n.º 14
0
 def test_extract_params_with_urlencoded_json(self):
     wsgi_environ = {
         'QUERY_STRING': 'state=%7B%22t%22%3A%22a%22%2C%22i%22%3A%22l%22%7D'
     }
     with set_flask_request(wsgi_environ):
         uri, http_method, body, headers = extract_params()
         # Request constructor will try to urldecode the querystring, make
         # sure this doesn't fail.
         Request(uri, http_method, body, headers)
Ejemplo n.º 15
0
    def setUp(self):
        self.request = Request('http://a.b/path')
        request_validator = mock.MagicMock()
        implicit_grant = ImplicitGrant(request_validator)
        openid_connect_implicit = OpenIDConnectImplicit(request_validator)

        self.dispatcher = ImplicitTokenGrantDispatcher(
            default_implicit_grant=implicit_grant,
            oidc_implicit_grant=openid_connect_implicit)
Ejemplo n.º 16
0
 def test_create_authorization_grant(self):
     bearer = BearerToken(self.mock_validator)
     self.request.response_mode = 'query'
     h, b, s = self.auth.create_authorization_response(self.request, bearer)
     grant = dict(Request(h['Location']).uri_query_params)
     self.assertIn('code', grant)
     self.assertTrue(self.mock_validator.validate_redirect_uri.called)
     self.assertTrue(self.mock_validator.validate_response_type.called)
     self.assertTrue(self.mock_validator.validate_scopes.called)
Ejemplo n.º 17
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.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)
Ejemplo n.º 18
0
    def test_fake_bearer_is_not_validated(self):
        request_validator = mock.MagicMock()
        request_validator.validate_bearer_token = self._mocked_validate_bearer_token

        for fake_header in self.fake_bearer_headers:
            request = Request("/", headers=fake_header)
            result = BearerToken(
                request_validator=request_validator).validate_request(request)

            self.assertFalse(result)
Ejemplo n.º 19
0
    def clean_oauth_signature(self):
        """
        Cleans and validates the 'oauth signature'. The signature is verified by calculating the hash (using the
        algorithm specified in 'oauth_signature_method') of the URL, METHOD and BODY. After the calculation the
        signature is compared with the 'oauth_signature' to check if they match. When the signatures match we can
        assure that the request is from a authorized entity.
        """
        oauth_request = Request(self.request.build_absolute_uri(),
                                self.request.method, self.request.POST)
        oauth_request.signature = self.cleaned_data["oauth_signature"]
        oauth_request.params = [(k, v) for k, v in self.request.POST.items()
                                if k != "oauth_signature"]

        if not oauth.verify_hmac_sha1(oauth_request, settings.LTI_SECRET):
            raise ValidationError(
                "Invalid signature, URL: {}, method: {}".format(
                    self.request.build_absolute_uri(), self.request.method))

        return self.cleaned_data["oauth_signature"]
Ejemplo n.º 20
0
 def test_authenticate_client(self):
     request = Request('http://localhost/authenticate_client', 'GET', None,
                       {
                           'client_id': '10',
                           'client_secret': 'secret'
                       })
     self.assertTrue(self.validator.authenticate_client(request))
     request = Request('http://localhost/authenticate_client', 'GET', None,
                       {
                           'client_id': '10',
                           'client_secret': 'secre'
                       })
     self.assertFalse(self.validator.authenticate_client(request))
     request = Request('http://localhost/authenticate_client', 'GET', None,
                       {
                           'client_id': '1',
                           'client_secret': 'secret'
                       })
     self.assertFalse(self.validator.authenticate_client(request))
Ejemplo n.º 21
0
 def setUp(self):
     mock_client = mock.MagicMock()
     mock_client.user.return_value = 'mocked user'
     self.request = Request('http://a.b/path')
     self.request.assertion = 'mocked assertion'
     self.request.grant_type = JWT_BEARER
     self.request.client = mock_client
     self.request.scope = 'foo'
     self.mock_validator = mock.MagicMock()
     self.auth = JWTBearerGrant(request_validator=self.mock_validator)
Ejemplo n.º 22
0
 def setUp(self):
     mock_client = mock.MagicMock()
     mock_client.user.return_value = 'mocked user'
     self.request = Request('http://a.b/path')
     self.request.grant_type = 'urn:ietf:params:oauth:grant-type:saml2-bearer'
     self.request.assertion = 'assertion'
     self.request.client = mock_client
     self.request.scopes = ('mocked', 'scopes')
     self.mock_validator = mock.MagicMock()
     self.auth = SAML2BearerGrant(request_validator=self.mock_validator)
Ejemplo n.º 23
0
 def test_sanitizing_authorization_header(self):
     r = Request(URI,
                 headers={
                     'Accept': 'application/json',
                     'Authorization': 'Basic Zm9vOmJhcg=='
                 })
     self.assertNotIn('Zm9vOmJhcg==', repr(r))
     self.assertIn('<SANITIZED>', repr(r))
     # Double-check we didn't modify the underlying object:
     self.assertEqual(r.headers['Authorization'], 'Basic Zm9vOmJhcg==')
Ejemplo n.º 24
0
 def setUp(self):
     mock_client = mock.MagicMock()
     mock_client.user.return_value = 'mocked user'
     self.request = Request('http://a.b/path')
     self.request.grant_type = 'client_credentials'
     self.request.client = mock_client
     self.request.scopes = ('mocked', 'scopes')
     self.mock_validator = mock.MagicMock()
     self.auth = ClientCredentialsGrant(
             request_validator=self.mock_validator)
Ejemplo n.º 25
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.authorized_scopes = scopes
     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)
     return response_type_handler.create_authorization_response(
         request, self.default_token)
Ejemplo n.º 26
0
 def setUp(self):
     self.request = Request('http://a.b/path')
     self.request.grant_type = 'password'
     self.request.username = '******'
     self.request.password = '******'
     self.request.client = 'mock authenticated'
     self.request.scopes = ('mocked', 'scopes')
     self.mock_validator = mock.MagicMock()
     self.auth = ResourceOwnerPasswordCredentialsGrant(
         request_validator=self.mock_validator)
Ejemplo n.º 27
0
 def setUp(self):
     mock_client = mock.MagicMock()
     mock_client.user.return_value = 'mocked user'
     self.request = Request('http://a.b/path')
     self.request.grant_type = 'refresh_token'
     self.request.refresh_token = 'lsdkfhj230'
     self.request.client = mock_client
     self.request.scope = 'foo'
     self.mock_validator = mock.MagicMock()
     self.auth = RefreshTokenGrant(request_validator=self.mock_validator)
Ejemplo n.º 28
0
 def to_representation(self, instance):
     core = OAuthLibCore()
     uri, http_method, body, headers = core._extract_params(
         self.context['request'])
     headers = {
         **headers,
         'client_id': self.initial_data['client_id'],
         'client_secret': self.initial_data['client_secret'],
     }
     request = Request(uri=uri,
                       http_method=http_method,
                       body=body,
                       headers=headers)
     request.scopes = ['read', 'write']
     request.user = instance.user
     validator = OAuth2Validator()
     validator.authenticate_client(request)
     token = BearerToken(request_validator=validator).create_token(
         request, refresh_token=True, save_token=True)
     return {**token, 'profile': super().to_representation(instance)}
Ejemplo n.º 29
0
    def setUp(self):
        self.request = Request('http://a.b/path')
        self.request.scopes = ('hello', 'world')
        self.request.client = 'batman'
        self.request.client_id = 'abcdef'
        self.request.response_type = 'token'
        self.request.state = 'xyz'
        self.request.redirect_uri = 'https://b.c/p'

        self.mock_validator = mock.MagicMock()
        self.auth = ImplicitGrant(request_validator=self.mock_validator)
Ejemplo n.º 30
0
    def setUp(self):
        self.request = Request('http://a.b/path')
        self.request.decoded_body = (
            ("client_id", "me"),
            ("code", "code"),
            ("redirect_url", "https://a.b/cb"),
        )

        self.request_validator = mock.MagicMock()
        self.auth_grant = OAuth2AuthorizationCodeGrant(self.request_validator)
        self.openid_connect_auth = AuthorizationCodeGrant(self.request_validator)
Ejemplo n.º 31
0
 def oauth_error(self, request, error, **kwargs):
     # UGLY HACK
     from oauthlib.common import Request
     core = self.get_oauthlib_core()
     uri, http_method, body, headers = core._extract_params(request)
     orequest = Request(uri,
                        http_method=http_method,
                        body=body,
                        headers=headers)
     raise OAuthToolkitError(
         error=error(request=orequest, state=orequest.state, **kwargs))
Ejemplo n.º 32
0
 def test_non_unicode_params(self):
     r = Request(
         bytes_type('http://a.b/path?query', 'utf-8'),
         http_method=bytes_type('GET', 'utf-8'),
         body=bytes_type('you=shall+pass', 'utf-8'),
         headers={bytes_type('a', 'utf-8'): bytes_type('b', 'utf-8')})
     self.assertEqual(r.uri, 'http://a.b/path?query')
     self.assertEqual(r.http_method, 'GET')
     self.assertEqual(r.body, 'you=shall+pass')
     self.assertEqual(r.decoded_body, [('you', 'shall pass')])
     self.assertEqual(r.headers, {'a': 'b'})
Ejemplo n.º 33
0
 def test_validate_lti_old_timestamp(self):
     request = Request(uri='https://example.com/lti',
                       http_method='POST',
                       body=self.read_data_file('lti_old_timestamp.txt'))
     parameters = LTIAuthBackend._get_validated_lti_params_from_values(  # pylint: disable=protected-access
         request=request,
         current_time=1436900000,
         lti_consumer_valid=True,
         lti_consumer_secret='secret',
         lti_max_timestamp_age=10)
     self.assertFalse(parameters)
Ejemplo n.º 34
0
    def test_validate_token_request(self):
        mock_validator = mock.MagicMock()
        auth = AuthorizationCodeGrant(request_validator=mock_validator)
        request = Request('http://a.b/path')
        self.assertRaises(UnsupportedGrantTypeError,
                          auth.validate_token_request, request)

        request.grant_type = 'authorization_code'
        self.assertRaises(InvalidRequestError, auth.validate_token_request,
                          request)

        mock_validator.validate_client = mock.MagicMock(return_value=False)
        request.code = 'waffles'
        request.client = 'batman'
        self.assertRaises(UnauthorizedClientError, auth.validate_token_request,
                          request)

        mock_validator.validate_client = mock.MagicMock(return_value=True)
        mock_validator.validate_code = mock.MagicMock(return_value=False)
        self.assertRaises(InvalidGrantError, auth.validate_token_request,
                          request)
Ejemplo n.º 35
0
 def test_non_unicode_params(self):
     r = Request(b'http://a.b/path?query',
                 http_method=b'GET',
                 body=b'you=shall+pass',
                 headers={
                     b'a': b'b',
                 })
     self.assertEqual(r.uri, 'http://a.b/path?query')
     self.assertEqual(r.http_method, 'GET')
     self.assertEqual(r.body, 'you=shall+pass')
     self.assertEqual(r.decoded_body, [('you', 'shall pass')])
     self.assertEqual(r.headers, {'a': 'b'})
 def test_validate_lti_cannot_add_get_params(self):
     request = Request(
         uri='https://example.com/lti?custom_another=parameter',
         http_method='POST',
         body=self.read_data_file('lti_cannot_add_get_params.txt')
     )
     parameters = LTIAuthBackend._get_validated_lti_params_from_values(  # pylint: disable=protected-access
         request=request, current_time=1436823554,
         lti_consumer_valid=True, lti_consumer_secret='secret',
         lti_max_timestamp_age=10
     )
     assert not parameters
Ejemplo n.º 37
0
    def test_validate_token_request(self):
        mock_validator = mock.MagicMock()
        auth = AuthorizationCodeGrant(request_validator=mock_validator)
        request = Request('http://a.b/path')
        self.assertRaises(UnsupportedGrantTypeError,
                auth.validate_token_request, request)

        request.grant_type = 'authorization_code'
        self.assertRaises(InvalidRequestError,
                auth.validate_token_request, request)

        mock_validator.validate_client = mock.MagicMock(return_value=False)
        request.code = 'waffles'
        request.client = 'batman'
        self.assertRaises(UnauthorizedClientError,
                auth.validate_token_request, request)

        mock_validator.validate_client = mock.MagicMock(return_value=True)
        mock_validator.validate_code = mock.MagicMock(return_value=False)
        self.assertRaises(InvalidGrantError,
                auth.validate_token_request, request)
Ejemplo n.º 38
0
    def test_extract_params_with_json(self):
        data = {'test': 'foo', 'foo': 'bar'}
        json_payload = json.dumps(data).encode('utf-8')

        wsgi_environ = {
            'CONTENT_TYPE': 'application/json',
            'CONTENT_LENGHT': str(len(data))
        }
        with set_flask_request(wsgi_environ, data):
            uri, http_method, body, headers = extract_params()
            Request(uri, http_method, body, headers)
            self.assertEqual(body, {'test': 'foo', 'foo': 'bar'})
Ejemplo n.º 39
0
def validate_2legged_oauth(oauth, uri, method, auth_header):
    """
    "Two-legged" OAuth authorization isn't standard and so not
    supported by current versions of oauthlib. The implementation
    here is sufficient for simple developer tools and testing. Real
    usage of OAuth will always require directing the user to the
    authorization page so that a resource-owner token can be
    generated.
    """
    req = Request(uri, method, "", auth_header)
    typ, params, oauth_params = oauth._get_signature_type_and_params(req)
    oauth_params = dict(oauth_params)
    req.params = filter(lambda x: x[0] not in ("oauth_signature", "realm"), params)
    req.signature = oauth_params.get("oauth_signature")
    req.client_key = oauth_params.get("oauth_consumer_key")
    req.nonce = oauth_params.get("oauth_nonce")
    req.timestamp = oauth_params.get("oauth_timestamp")
    if oauth_params.get("oauth_signature_method").lower() != "hmac-sha1":
        raise TwoLeggedOAuthError(u"unsupported signature method " + oauth_params.get("oauth_signature_method"))
    secret = validator.get_client_secret(req.client_key, req)
    valid_signature = signature.verify_hmac_sha1(req, secret, None)
    if valid_signature:
        return req.client_key
    else:
        raise TwoLeggedOAuthError(u"Cannot find APIAccess token with that key: %s" % req.client_key)
Ejemplo n.º 40
0
    def create_token_response(self, uri, http_method='POST', body=None,
                              headers=None, credentials=None, grant_type_for_scope=None,
                              claims=None):
        """Extract grant_type and route to the designated handler."""
        request = Request(
            uri, http_method=http_method, body=body, headers=headers)
        self.validate_token_request(request)
        # 'scope' is an allowed Token Request param in both the "Resource Owner Password Credentials Grant"
        # and "Client Credentials Grant" flows
        # https://tools.ietf.org/html/rfc6749#section-4.3.2
        # https://tools.ietf.org/html/rfc6749#section-4.4.2
        request.scopes = utils.scope_to_list(request.scope)

        request.extra_credentials = credentials
        if grant_type_for_scope:
            request.grant_type = grant_type_for_scope

        # OpenID Connect claims, if provided.  The server using oauthlib might choose
        # to implement the claims parameter of the Authorization Request.  In this case
        # it should retrieve those claims and pass them via the claims argument here,
        # as a dict.
        if claims:
            request.claims = claims

        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)
Ejemplo n.º 41
0
    def create_token_response(self,
                              uri,
                              http_method='GET',
                              body=None,
                              headers=None,
                              credentials=None,
                              grant_type_for_scope=None,
                              claims=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
        if grant_type_for_scope:
            request.grant_type = grant_type_for_scope

        # OpenID Connect claims, if provided.  The server using oauthlib might choose
        # to implement the claims parameter of the Authorization Request.  In this case
        # it should retrieve those claims and pass them via the claims argument here,
        # as a dict.
        if claims:
            request.claims = claims

        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)
Ejemplo n.º 42
0
    async 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.
        """
        resp_headers = {
            'Content-Type': 'application/json',
            'Cache-Control': 'no-store',
            'Pragma': 'no-cache',
        }
        request = Request(uri,
                          http_method=http_method,
                          body=body,
                          headers=headers)
        try:
            await 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 = '{}({});'.format(request.callback,
                                                 response_body)
            resp_headers.update(e.headers)
            return resp_headers, response_body, e.status_code

        await self.request_validator.revoke_token(request.token,
                                                  request.token_type_hint,
                                                  request)

        response_body = ''
        if self.enable_jsonp and request.callback:
            response_body = request.callback + '();'
        return {}, response_body, 200
Ejemplo n.º 43
0
 def test_create_authorization_grant_state(self):
     self.request.state = 'abc'
     self.request.redirect_uri = None
     self.mock_validator.get_default_redirect_uri.return_value = 'https://a.b/cb'
     bearer = BearerToken(self.mock_validator)
     h, b, s = self.auth.create_authorization_response(self.request, bearer)
     grant = dict(Request(h['Location']).uri_query_params)
     self.assertIn('code', grant)
     self.assertIn('state', grant)
     self.assertFalse(self.mock_validator.validate_redirect_uri.called)
     self.assertTrue(self.mock_validator.get_default_redirect_uri.called)
     self.assertTrue(self.mock_validator.validate_response_type.called)
     self.assertTrue(self.mock_validator.validate_scopes.called)
Ejemplo n.º 44
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)
     response_type_handler = self.response_types.get(
         request.response_type, self.default_response_type_handler)
     return response_type_handler.validate_authorization_request(request)
Ejemplo n.º 45
0
    def sign(self, uri, http_method=u'GET', body='', headers=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.

        The body argument may be a dict, a list of 2-tuples, or a formencoded
        string. If the body argument is not a formencoded string and/or the
        Content-Type header is not 'x-www-form-urlencoded', 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.

        All string data MUST be unicode. This includes strings inside body
        dicts, for example.
        """
        # normalize request data
        request = Request(uri, http_method, body, headers)

        # sanity check
        content_type = request.headers.get('Content-Type', None)
        if content_type == 'application/x-www-form-urlencoded' and not request.body_has_params:
            raise ValueError("Headers indicate a formencoded body but body was not decodable.")

        # generate the basic OAuth parameters
        request.oauth_params = self.get_oauth_params()

        # generate the signature
        request.oauth_params.append((u'oauth_signature', self.get_oauth_signature(request)))

        # render the signed request and return it
        return self._render(request, formencode=True)
Ejemplo n.º 46
0
    def create_token_response(self, uri, http_method='GET', body=None,
                              headers=None, credentials=None, grant_type_for_scope=None,
                              claims=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
        if grant_type_for_scope:
            request.grant_type = grant_type_for_scope

        # OpenID Connect claims, if provided.  The server using oauthlib might choose
        # to implement the claims parameter of the Authorization Request.  In this case
        # it should retrieve those claims and pass them via the claims argument here,
        # as a dict.
        if claims:
            request.claims = claims

        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)
Ejemplo n.º 47
0
    def create_token_response(self, uri, http_method='GET', body=None, headers=None):
        """Extract grant_type and route to the designated handler."""
        request = Request(uri, http_method=http_method, body=body, headers=headers)
        query_params = params_from_uri(request.uri)
        body_params = self.request.decoded_body

        # Prioritize grant_type defined as body param over those in uri.
        # Chosen because all three core grant types supply this parameter
        # in the body. However it is not specified explicitely in RFC 6748.
        if 'grant_type' in body_params:
            request.grant_type = query_params.get('grant_type')
        elif 'grant_type' in query_params:
            request.grant_type = body_params.get('grant_type')
        else:
            raise errors.InvalidRequestError(
                description='The grant_type parameter is missing.')

        if not request.grant_type in self.grant_types:
            raise errors.UnsupportedGrantTypeError(
                description='Invalid response type')

        return self.grant_types.get(
                request.grant_type).create_token_response(
                        request, self.default_token)
Ejemplo n.º 48
0
    def make_request(self, response_type='code',
            scope='openid profile email', **kwargs):
        request = Request('https://a.b/path')

        request.scope = scope
        request.client = 'superman'
        request.client_id = 'abcdef'
        request.redirect_uri = 'https://a.b/'
        request.response_type = response_type

        for prop, val in kwargs.items():
            setattr(request, prop, val)

        return request
Ejemplo n.º 49
0
 def authorize_request_token(self, request_token, user):
     verifier = self.token_generator()
     request = Request('')
     request.resource_owner_key = user
     return self.request_validator.save_verifier(request_token, verifier, request)
Ejemplo n.º 50
0
    def sign(self, uri, http_method='GET', body=None, headers=None, realm=None):
        __doc__ = Client.sign.__doc__

        # 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
Ejemplo n.º 51
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 OAuth1 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
Ejemplo n.º 52
0
    def verify_request(self, uri, http_method='GET', body=None,
            headers=None, require_resource_owner=True, require_verifier=False,
            require_realm=False, required_realm=None, require_callback=False):
        """Verifies a request ensuring that the following is true:

        Per `section 3.2`_ of the spec.

        - all mandated OAuth parameters are supplied
        - parameters are only supplied in one source which may be the URI
          query, the Authorization header or the body
        - all parameters are checked and validated, see comments and the
          methods and properties of this class for further details.
        - the supplied signature is verified against a recalculated one

        A ValueError will be raised if any parameter is missing,
        supplied twice or invalid. A HTTP 400 Response should be returned
        upon catching an exception.

        A HTTP 401 Response should be returned if verify_request returns False.

        `Timing attacks`_ are prevented through the use of dummy credentials to
        create near constant time verification even if an invalid credential
        is used. Early exit on invalid credentials would enable attackers
        to perform `enumeration attacks`_. Near constant time string comparison
        is used to prevent secret key guessing. Note that timing attacks can
        only be prevented through near constant time execution, not by adding
        a random delay which would only require more samples to be gathered.

        .. _`section 3.2`: http://tools.ietf.org/html/rfc5849#section-3.2
        .. _`Timing attacks`: http://rdist.root.org/2010/07/19/exploiting-remote-timing-attacks/
        .. _`enumeration attacks`: http://www.sans.edu/research/security-laboratory/article/attacks-browsing
        """
        # Only include body data from x-www-form-urlencoded requests
        headers = headers or {}
        if ("Content-Type" in headers and
                headers["Content-Type"] == CONTENT_TYPE_FORM_URLENCODED):
            request = Request(uri, http_method, body, headers)
        else:
            request = Request(uri, http_method, '', headers)

        if self.enforce_ssl and not request.uri.lower().startswith("https://"):
            raise ValueError("Insecure transport, only HTTPS is allowed.")

        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 ValueError("Duplicate OAuth 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.callback_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')

        # The server SHOULD return a 400 (Bad Request) status code when
        # receiving a request with missing parameters.
        if not all((request.signature, request.client_key,
                    request.nonce, request.timestamp,
                    request.signature_method)):
            raise ValueError("Missing OAuth parameters.")

        # OAuth does not mandate a particular signature method, as each
        # implementation can have its own unique requirements.  Servers are
        # free to implement and document their own custom methods.
        # Recommending any particular method is beyond the scope of this
        # specification.  Implementers should review the Security
        # Considerations section (`Section 4`_) before deciding on which
        # method to support.
        # .. _`Section 4`: http://tools.ietf.org/html/rfc5849#section-4
        if not request.signature_method in self.allowed_signature_methods:
            raise ValueError("Invalid signature method.")

        # Servers receiving an authenticated request MUST validate it by:
        #   If the "oauth_version" parameter is present, ensuring its value is
        #   "1.0".
        if ('oauth_version' in request.oauth_params and
            request.oauth_params['oauth_version'] != '1.0'):
            raise ValueError("Invalid OAuth version.")

        # The timestamp value MUST be a positive integer. Unless otherwise
        # specified by the server's documentation, the timestamp is expressed
        # in the number of seconds since January 1, 1970 00:00:00 GMT.
        if len(request.timestamp) != 10:
            raise ValueError("Invalid timestamp size")
        try:
            ts = int(request.timestamp)

        except ValueError:
            raise ValueError("Timestamp must be an integer")

        else:
            # To avoid the need to retain an infinite number of nonce values for
            # future checks, servers MAY choose to restrict the time period after
            # which a request with an old timestamp is rejected.
            if time.time() - ts > self.timestamp_lifetime:
                raise ValueError("Request too old, over 10 minutes.")

        # Provider specific validation of parameters, used to enforce
        # restrictions such as character set and length.
        if not self.check_client_key(request.client_key):
            raise ValueError("Invalid client key.")

        if not request.resource_owner_key and require_resource_owner:
            raise ValueError("Missing resource owner.")

        if (require_resource_owner and not require_verifier and
            not self.check_access_token(request.resource_owner_key)):
            raise ValueError("Invalid resource owner key.")

        if (require_resource_owner and require_verifier and
            not self.check_request_token(request.resource_owner_key)):
            raise ValueError("Invalid resource owner key.")

        if not self.check_nonce(request.nonce):
            raise ValueError("Invalid nonce.")

        if request.realm and not self.check_realm(request.realm):
            raise ValueError("Invalid realm. Allowed are %s" % self.realms)

        if not request.verifier and require_verifier:
            raise ValueError("Missing verifier.")

        if require_verifier and not self.check_verifier(request.verifier):
            raise ValueError("Invalid verifier.")

        # Servers receiving an authenticated request MUST validate it by:
        #   If using the "HMAC-SHA1" or "RSA-SHA1" signature methods, ensuring
        #   that the combination of nonce/timestamp/token (if present)
        #   received from the client has not been used before in a previous
        #   request (the server MAY reject requests with stale timestamps as
        #   described in `Section 3.3`_).
        # .._`Section 3.3`: http://tools.ietf.org/html/rfc5849#section-3.3
        #
        # We check this before validating client and resource owner for
        # increased security and performance, both gained by doing less work.
        if require_verifier:
            token = {"request_token": request.resource_owner_key}
        else:
            token = {"access_token": request.resource_owner_key}
        if not self.validate_timestamp_and_nonce(request.client_key,
                request.timestamp, request.nonce, **token):
                return False, request

        # The server SHOULD return a 401 (Unauthorized) status code when
        # receiving a request with invalid client credentials.
        # Note: This is postponed in order to avoid timing attacks, instead
        # a dummy client is assigned and used to maintain near constant
        # time request verification.
        #
        # Note that early exit would enable client enumeration
        valid_client = self.validate_client_key(request.client_key)
        if not valid_client:
            client_key = self.dummy_client

        # Callback is normally never required, except for requests for
        # a Temporary Credential as described in `Section 2.1`_
        # .._`Section 2.1`: http://tools.ietf.org/html/rfc5849#section-2.1
        if require_callback:
            valid_redirect = self.validate_redirect_uri(request.client_key,
                    request.callback_uri)
        else:
            valid_redirect = True

        # The server SHOULD return a 401 (Unauthorized) status code when
        # receiving a request with invalid or expired token.
        # Note: This is postponed in order to avoid timing attacks, instead
        # a dummy token is assigned and used to maintain near constant
        # time request verification.
        #
        # Note that early exit would enable resource owner enumeration
        if request.resource_owner_key:
            if require_verifier:
                valid_resource_owner = self.validate_request_token(
                    request.client_key, request.resource_owner_key)
                if not valid_resource_owner:
                    resource_owner_key = self.dummy_request_token
            else:
                valid_resource_owner = self.validate_access_token(
                    request.client_key, request.resource_owner_key)
                if not valid_resource_owner:
                    resource_owner_key = self.dummy_access_token
        else:
            valid_resource_owner = True

        # Note that `realm`_ is only used in authorization headers and how
        # it should be interepreted is not included in the OAuth spec.
        # However they could be seen as a scope or realm to which the
        # client has access and as such every client should be checked
        # to ensure it is authorized access to that scope or realm.
        # .. _`realm`: http://tools.ietf.org/html/rfc2617#section-1.2
        #
        # Note that early exit would enable client realm access enumeration.
        #
        # The require_realm indicates this is the first step in the OAuth
        # workflow where a client requests access to a specific realm.
        # This first step (obtaining request token) need not require a realm
        # and can then be identified by checking the require_resource_owner
        # flag and abscence of realm.
        #
        # Clients obtaining an access token will not supply a realm and it will
        # not be checked. Instead the previously requested realm should be
        # transferred from the request token to the access token.
        #
        # Access to protected resources will always validate the realm but note
        # that the realm is now tied to the access token and not provided by
        # the client.
        if ((require_realm and not request.resource_owner_key) or
            (not require_resource_owner and not request.realm)):
            valid_realm = self.validate_requested_realm(request.client_key,
                    request.realm)
        elif require_verifier:
            valid_realm = True
        else:
            valid_realm = self.validate_realm(request.client_key,
                    request.resource_owner_key, uri=request.uri,
                    required_realm=required_realm)

        # The server MUST verify (Section 3.2) the validity of the request,
        # ensure that the resource owner has authorized the provisioning of
        # token credentials to the client, and ensure that the temporary
        # credentials have not expired or been used before.  The server MUST
        # also verify the verification code received from the client.
        # .. _`Section 3.2`: http://tools.ietf.org/html/rfc5849#section-3.2
        #
        # Note that early exit would enable resource owner authorization
        # verifier enumertion.
        if request.verifier:
            valid_verifier = self.validate_verifier(request.client_key,
                request.resource_owner_key, request.verifier)
        else:
            valid_verifier = True

        # Parameters to Client depend on signature method which may vary
        # for each request. Note that HMAC-SHA1 and PLAINTEXT share parameters

        request.params = filter(lambda x: x[0] not in ("oauth_signature", "realm"), params)

        # ---- RSA Signature verification ----
        if request.signature_method == SIGNATURE_RSA:
            # The server verifies the signature per `[RFC3447] section 8.2.2`_
            # .. _`[RFC3447] section 8.2.2`: http://tools.ietf.org/html/rfc3447#section-8.2.1
            rsa_key = self.get_rsa_key(request.client_key)
            valid_signature = signature.verify_rsa_sha1(request, rsa_key)

        # ---- HMAC or Plaintext Signature verification ----
        else:
            # Servers receiving an authenticated request MUST validate it by:
            #   Recalculating the request signature independently as described in
            #   `Section 3.4`_ and comparing it to the value received from the
            #   client via the "oauth_signature" parameter.
            # .. _`Section 3.4`: http://tools.ietf.org/html/rfc5849#section-3.4
            client_secret = self.get_client_secret(request.client_key)
            resource_owner_secret = None
            if require_resource_owner:
                if require_verifier:
                    resource_owner_secret = self.get_request_token_secret(
                        request.client_key, request.resource_owner_key)
                else:
                    resource_owner_secret = self.get_access_token_secret(
                        request.client_key, request.resource_owner_key)

            if request.signature_method == SIGNATURE_HMAC:
                valid_signature = signature.verify_hmac_sha1(request,
                    client_secret, resource_owner_secret)
            else:
                valid_signature = signature.verify_plaintext(request,
                    client_secret, resource_owner_secret)

        # We delay checking validity until the very end, using dummy values for
        # calculations and fetching secrets/keys to ensure the flow of every
        # request remains almost identical regardless of whether valid values
        # have been supplied. This ensures near constant time execution and
        # prevents malicious users from guessing sensitive information
        v = all((valid_client, valid_resource_owner, valid_realm,
                    valid_redirect, valid_verifier, valid_signature))
        logger = logging.getLogger("oauthlib")
        if not v:
            logger.info("[Failure] OAuthLib request verification failed.")
            logger.info("Valid client:\t%s" % valid_client)
            logger.info("Valid token:\t%s\t(Required: %s" % (valid_resource_owner, require_resource_owner))
            logger.info("Valid realm:\t%s\t(Required: %s)" % (valid_realm, require_realm))
            logger.info("Valid callback:\t%s" % valid_redirect)
            logger.info("Valid verifier:\t%s\t(Required: %s)" % (valid_verifier, require_verifier))
            logger.info("Valid signature:\t%s" % valid_signature)
        return v, request
Ejemplo n.º 53
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.

        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.

        All string data MUST be unicode. 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')

        # generate the basic OAuth parameters
        request.oauth_params = self.get_oauth_params()

        # generate the signature
        request.oauth_params.append(('oauth_signature', self.get_oauth_signature(request)))

        # render the signed request and return it
        return self._render(request, formencode=True,
                            realm=(realm or self.realm))
Ejemplo n.º 54
0
    def check_request_signature(self, uri, http_method=u'GET', body='',
            headers=None):
        """Check a request's supplied signature to make sure the request is
        valid.

        Servers should return HTTP status 400 if a ValueError exception
        is raised and HTTP status 401 on return value False.

        Per `section 3.2`_ of the spec.

        .. _`section 3.2`: http://tools.ietf.org/html/rfc5849#section-3.2
        """
        headers = headers or {}
        signature_type = None
        # FIXME: urlparse does not return unicode!
        uri_query = urlparse.urlparse(uri).query

        signature_type, params = self.get_signature_type_and_params(uri_query,
            headers, body)

        # the parameters may not include duplicate oauth entries
        filtered_params = utils.filter_oauth_params(params)
        if len(filtered_params) != len(params):
            raise ValueError("Duplicate OAuth entries.")

        params = dict(params)
        request_signature = params.get(u'oauth_signature')
        client_key = params.get(u'oauth_consumer_key')
        resource_owner_key = params.get(u'oauth_token')
        nonce = params.get(u'oauth_nonce')
        timestamp = params.get(u'oauth_timestamp')
        callback_uri = params.get(u'oauth_callback')
        verifier = params.get(u'oauth_verifier')
        signature_method = params.get(u'oauth_signature_method')

        # ensure all mandatory parameters are present
        if not all((request_signature, client_key, nonce,
                    timestamp, signature_method)):
            raise ValueError("Missing OAuth parameters.")

        # if version is supplied, it must be "1.0"
        if u'oauth_version' in params and params[u'oauth_version'] != u'1.0':
            raise ValueError("Invalid OAuth version.")

        # signature method must be valid
        if not signature_method in SIGNATURE_METHODS:
            raise ValueError("Invalid signature method.")

        # ensure client key is valid
        if not self.check_client_key(client_key):
            return False

        # ensure resource owner key is valid and not expired
        if not self.check_resource_owner_key(client_key, resource_owner_key):
            return False

        # ensure the nonce and timestamp haven't been used before
        if not self.check_timestamp_and_nonce(timestamp, nonce):
            return False

        # FIXME: extract realm, then self.check_realm

        # oauth_client parameters depend on client chosen signature method
        # which may vary for each request, section 3.4
        # HMAC-SHA1 and PLAINTEXT share parameters
        if signature_method == SIGNATURE_RSA:
            oauth_client = Client(client_key,
                resource_owner_key=resource_owner_key,
                callback_uri=callback_uri,
                signature_method=signature_method,
                signature_type=signature_type,
                rsa_key=self.rsa_key, verifier=verifier)
        else:
            client_secret = self.get_client_secret(client_key)
            resource_owner_secret = self.get_resource_owner_secret(
                resource_owner_key)
            oauth_client = Client(client_key,
                client_secret=client_secret,
                resource_owner_key=resource_owner_key,
                resource_owner_secret=resource_owner_secret,
                callback_uri=callback_uri,
                signature_method=signature_method,
                signature_type=signature_type,
                verifier=verifier)

        request = Request(uri, http_method, body, headers)
        # OAuth parameters transmitted in the query or entity-body are
        # already present in the parameter sources (section 3.4.1.3.1)
        # used to construct the signature base string.
        # FIXME: This should probably be handled by Request.
        if signature_type == SIGNATURE_TYPE_AUTH_HEADER:
            request.oauth_params = params.items()

        client_signature = oauth_client.get_oauth_signature(request)

        # FIXME: use near constant time string compare to avoid timing attacks
        return client_signature == request_signature
Ejemplo n.º 55
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()

        # 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