Example #1
0
 def _validate_signature(self, request, principal, args, params):
     """Validate the signature."""
     creds = AWSCredentials(principal.access_key, principal.secret_key)
     endpoint = AWSServiceEndpoint()
     endpoint.set_method(request.method)
     endpoint.set_canonical_host(request.getHeader("Host"))
     path = request.path
     if self.path is not None:
         path = "%s/%s" % (self.path.rstrip("/"), path.lstrip("/"))
     endpoint.set_path(path)
     signature = Signature(
         creds,
         endpoint,
         params,
         signature_method=args["signature_method"],
         signature_version=args["signature_version"],
     )
     if signature.compute() != args["signature"]:
         raise APIError(
             403,
             "SignatureDoesNotMatch",
             "The request signature we calculated does not "
             "match the signature you provided. Check your "
             "key and signing method.",
         )
Example #2
0
class AWSServiceEndpointTestCase(TXAWSTestCase):

    def setUp(self):
        self.endpoint = AWSServiceEndpoint(uri="http://my.service/da_endpoint")

    def test_simple_creation(self):
        endpoint = AWSServiceEndpoint()
        self.assertEquals(endpoint.scheme, "http")
        self.assertEquals(endpoint.host, "")
        self.assertEquals(endpoint.port, 80)
        self.assertEquals(endpoint.path, "/")
        self.assertEquals(endpoint.method, "GET")

    def test_custom_method(self):
        endpoint = AWSServiceEndpoint(
            uri="http://service/endpoint", method="PUT")
        self.assertEquals(endpoint.method, "PUT")

    def test_parse_uri(self):
        self.assertEquals(self.endpoint.scheme, "http")
        self.assertEquals(self.endpoint.host, "my.service")
        self.assertEquals(self.endpoint.port, 80)
        self.assertEquals(self.endpoint.path, "/da_endpoint")

    def test_parse_uri_https_and_custom_port(self):
        endpoint = AWSServiceEndpoint(uri="https://my.service:8080/endpoint")
        self.assertEquals(endpoint.scheme, "https")
        self.assertEquals(endpoint.host, "my.service")
        self.assertEquals(endpoint.port, 8080)
        self.assertEquals(endpoint.path, "/endpoint")

    def test_get_uri(self):
        uri = self.endpoint.get_uri()
        self.assertEquals(uri, "http://my.service/da_endpoint")

    def test_get_uri_custom_port(self):
        uri = "https://my.service:8080/endpoint"
        endpoint = AWSServiceEndpoint(uri=uri)
        new_uri = endpoint.get_uri()
        self.assertEquals(new_uri, uri)

    def test_set_host(self):
        self.assertEquals(self.endpoint.host, "my.service")
        self.endpoint.set_host("newhost.com")
        self.assertEquals(self.endpoint.host, "newhost.com")

    def test_get_host(self):
        self.assertEquals(self.endpoint.host, self.endpoint.get_host())

    def test_set_path(self):
        self.endpoint.set_path("/newpath")
        self.assertEquals(
            self.endpoint.get_uri(),
            "http://my.service/newpath")

    def test_set_method(self):
        self.assertEquals(self.endpoint.method, "GET")
        self.endpoint.set_method("PUT")
        self.assertEquals(self.endpoint.method, "PUT")
Example #3
0
class AWSServiceEndpointTestCase(TXAWSTestCase):
    def setUp(self):
        self.endpoint = AWSServiceEndpoint(uri="http://my.service/da_endpoint")

    def test_simple_creation(self):
        endpoint = AWSServiceEndpoint()
        self.assertEquals(endpoint.scheme, "http")
        self.assertEquals(endpoint.host, "")
        self.assertEquals(endpoint.port, 80)
        self.assertEquals(endpoint.path, "/")
        self.assertEquals(endpoint.method, "GET")

    def test_custom_method(self):
        endpoint = AWSServiceEndpoint(uri="http://service/endpoint",
                                      method="PUT")
        self.assertEquals(endpoint.method, "PUT")

    def test_parse_uri(self):
        self.assertEquals(self.endpoint.scheme, "http")
        self.assertEquals(self.endpoint.host, "my.service")
        self.assertEquals(self.endpoint.port, 80)
        self.assertEquals(self.endpoint.path, "/da_endpoint")

    def test_parse_uri_https_and_custom_port(self):
        endpoint = AWSServiceEndpoint(uri="https://my.service:8080/endpoint")
        self.assertEquals(endpoint.scheme, "https")
        self.assertEquals(endpoint.host, "my.service")
        self.assertEquals(endpoint.port, 8080)
        self.assertEquals(endpoint.path, "/endpoint")

    def test_get_uri(self):
        uri = self.endpoint.get_uri()
        self.assertEquals(uri, "http://my.service/da_endpoint")

    def test_get_uri_custom_port(self):
        uri = "https://my.service:8080/endpoint"
        endpoint = AWSServiceEndpoint(uri=uri)
        new_uri = endpoint.get_uri()
        self.assertEquals(new_uri, uri)

    def test_set_host(self):
        self.assertEquals(self.endpoint.host, "my.service")
        self.endpoint.set_host("newhost.com")
        self.assertEquals(self.endpoint.host, "newhost.com")

    def test_get_host(self):
        self.assertEquals(self.endpoint.host, self.endpoint.get_host())

    def test_set_path(self):
        self.endpoint.set_path("/newpath")
        self.assertEquals(self.endpoint.get_uri(), "http://my.service/newpath")

    def test_set_method(self):
        self.assertEquals(self.endpoint.method, "GET")
        self.endpoint.set_method("PUT")
        self.assertEquals(self.endpoint.method, "PUT")
Example #4
0
 def _validate_signature(self, request, principal, args, params):
     """Validate the signature."""
     creds = AWSCredentials(principal.access_key, principal.secret_key)
     endpoint = AWSServiceEndpoint()
     endpoint.set_method(request.method)
     endpoint.set_canonical_host(request.getHeader("Host"))
     path = request.path
     if self.path is not None:
         path = "%s/%s" % (self.path.rstrip("/"), path.lstrip("/"))
     endpoint.set_path(path)
     params.pop("Signature")
     signature = Signature(creds, endpoint, params)
     if signature.compute() != args.Signature:
         raise APIError(403, "SignatureDoesNotMatch",
                        "The request signature we calculated does not "
                        "match the signature you provided. Check your "
                        "key and signing method.")
Example #5
0
class AWSServiceEndpointTestCase(TestCase):

    def setUp(self):
        self.endpoint = AWSServiceEndpoint(uri="http://my.service/da_endpoint")

    def test_warning_when_verification_disabled(self):
        """
        L{AWSServiceEndpoint} emits a warning when told not to perform
        certificate verification.
        """
        self.assertWarns(
            UserWarning,
            "Operating with certificate verification disabled!",
            __file__,
            lambda: AWSServiceEndpoint(ssl_hostname_verification=False),
        )

    def test_simple_creation(self):
        endpoint = AWSServiceEndpoint()
        self.assertEquals(endpoint.scheme, "http")
        self.assertEquals(endpoint.host, "")
        self.assertEquals(endpoint.port, None)
        self.assertEquals(endpoint.path, "/")
        self.assertEquals(endpoint.method, "GET")

    def test_custom_method(self):
        endpoint = AWSServiceEndpoint(
            uri="http://service/endpoint", method="PUT")
        self.assertEquals(endpoint.method, "PUT")

    def test_parse_uri(self):
        self.assertEquals(self.endpoint.scheme, "http")
        self.assertEquals(self.endpoint.host, "my.service")
        self.assertIdentical(self.endpoint.port, None)
        self.assertEquals(self.endpoint.path, "/da_endpoint")

    def test_parse_uri_https_and_custom_port(self):
        endpoint = AWSServiceEndpoint(uri="https://my.service:8080/endpoint")
        self.assertEquals(endpoint.scheme, "https")
        self.assertEquals(endpoint.host, "my.service")
        self.assertEquals(endpoint.port, 8080)
        self.assertEquals(endpoint.path, "/endpoint")

    def test_get_uri(self):
        uri = self.endpoint.get_uri()
        self.assertEquals(uri, "http://my.service/da_endpoint")

    def test_get_uri_custom_port(self):
        uri = "https://my.service:8080/endpoint"
        endpoint = AWSServiceEndpoint(uri=uri)
        new_uri = endpoint.get_uri()
        self.assertEquals(new_uri, uri)

    def test_set_host(self):
        self.assertEquals(self.endpoint.host, "my.service")
        self.endpoint.set_host("newhost.com")
        self.assertEquals(self.endpoint.host, "newhost.com")

    def test_get_host(self):
        self.assertEquals(self.endpoint.host, self.endpoint.get_host())

    def test_get_canonical_host(self):
        """
        If the port is not specified the canonical host is the same as
        the host.
        """
        uri = "http://my.service/endpoint"
        endpoint = AWSServiceEndpoint(uri=uri)
        self.assertEquals("my.service", endpoint.get_canonical_host())

    def test_get_canonical_host_with_non_default_port(self):
        """
        If the port is not the default, the canonical host includes it.
        """
        uri = "http://my.service:99/endpoint"
        endpoint = AWSServiceEndpoint(uri=uri)
        self.assertEquals("my.service:99", endpoint.get_canonical_host())

    def test_get_canonical_host_is_lower_case(self):
        """
        The canonical host is guaranteed to be lower case.
        """
        uri = "http://MY.SerVice:99/endpoint"
        endpoint = AWSServiceEndpoint(uri=uri)
        self.assertEquals("my.service:99", endpoint.get_canonical_host())

    def test_set_canonical_host(self):
        """
        The canonical host is converted to lower case.
        """
        endpoint = AWSServiceEndpoint()
        endpoint.set_canonical_host("My.Service")
        self.assertEquals("my.service", endpoint.host)
        self.assertIdentical(None, endpoint.port)

    def test_set_canonical_host_with_port(self):
        """
        The canonical host can optionally have a port.
        """
        endpoint = AWSServiceEndpoint()
        endpoint.set_canonical_host("my.service:99")
        self.assertEquals("my.service", endpoint.host)
        self.assertEquals(99, endpoint.port)

    def test_set_canonical_host_with_empty_port(self):
        """
        The canonical host can also have no port.
        """
        endpoint = AWSServiceEndpoint()
        endpoint.set_canonical_host("my.service:")
        self.assertEquals("my.service", endpoint.host)
        self.assertIdentical(None, endpoint.port)

    def test_set_path(self):
        self.endpoint.set_path("/newpath")
        self.assertEquals(
            self.endpoint.get_uri(),
            "http://my.service/newpath")

    def test_set_method(self):
        self.assertEquals(self.endpoint.method, "GET")
        self.endpoint.set_method("PUT")
        self.assertEquals(self.endpoint.method, "PUT")
Example #6
0
class Query(BaseQuery):
    """A query for submission to the S3 service."""

    def __init__(self, bucket=None, object_name=None, data="",
                 content_type=None, metadata={}, amz_headers={}, *args,
                 **kwargs):
        super(Query, self).__init__(*args, **kwargs)
        self.bucket = bucket
        self.object_name = object_name
        self.data = data
        self.content_type = content_type
        self.metadata = metadata
        self.amz_headers = amz_headers
        self.date = datetimeToString()
        if not self.endpoint or not self.endpoint.host:
            self.endpoint = AWSServiceEndpoint(S3_ENDPOINT)
        self.endpoint.set_method(self.action)

    def set_content_type(self):
        """
        Set the content type based on the file extension used in the object
        name.
        """
        if self.object_name and not self.content_type:
            # XXX nothing is currently done with the encoding... we may
            # need to in the future
            self.content_type, encoding = mimetypes.guess_type(
                self.object_name, strict=False)

    def get_headers(self):
        """
        Build the list of headers needed in order to perform S3 operations.
        """
        headers = {"Content-Length": len(self.data),
                   "Content-MD5": calculate_md5(self.data),
                   "Date": self.date}
        for key, value in self.metadata.iteritems():
            headers["x-amz-meta-" + key] = value
        for key, values in self.amz_headers.iteritems():
            if isinstance(values, tuple):
                headers["x-amz-" + key] = ",".join(values)
            else:
                headers["x-amz-" + key] = values
        # Before we check if the content type is set, let's see if we can set
        # it by guessing the the mimetype.
        self.set_content_type()
        if self.content_type is not None:
            headers["Content-Type"] = self.content_type
        if self.creds is not None:
            signature = self.sign(headers)
            headers["Authorization"] = "AWS %s:%s" % (
                self.creds.access_key, signature)
        return headers

    def get_canonicalized_amz_headers(self, headers):
        """
        Get the headers defined by Amazon S3.
        """
        headers = [
            (name.lower(), value) for name, value in headers.iteritems()
            if name.lower().startswith("x-amz-")]
        headers.sort()
        # XXX missing spec implementation:
        # txAWS doesn't currently unfold long headers
        def represent(n, vs):
            if isinstance(vs, tuple):
                return "".join(["%s:%s\n" % (n, vs) for v in vs])
            else:
                return "%s:%s\n" % (n, vs)

        return "".join([represent(name, value) for name, value in headers])

    def get_canonicalized_resource(self):
        """
        Get an S3 resource path.
        """

        # As <http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAuthentication.html>
        # says, if there is a subresource (e.g. ?acl), it is included, but other query
        # parameters (e.g. ?prefix=... in a GET Bucket request) are not included.
        # Yes, that makes no sense in terms of either security or consistency.
        resource = self.object_name
        if resource:
            q = resource.find('?')
            if q >= 0:
                # There can be both a subresource and other parameters, for example
                # '?versions&prefix=foo'. "You are in a maze of twisty edge cases..."
                firstparam = resource[q:].partition('&')[0]  # includes the initial '?'
                resource = resource[:q]                      # strip the query
                if '=' not in firstparam:
                    resource += firstparam                   # add back '?subresource' if present

        path = "/"
        if self.bucket is not None:
            path += self.bucket
        if self.bucket is not None and resource:
            if not resource.startswith("/"):
                path += "/"
            path += resource
        elif self.bucket is not None and not path.endswith("/"):
            path += "/"
        return path

    def sign(self, headers):
        """Sign this query using its built in credentials."""
        text = (self.action + "\n" +
                headers.get("Content-MD5", "") + "\n" +
                headers.get("Content-Type", "") + "\n" +
                headers.get("Date", "") + "\n" +
                self.get_canonicalized_amz_headers(headers) +
                self.get_canonicalized_resource())
        return self.creds.sign(text, hash_type="sha1")

    def submit(self, url_context=None):
        """Submit this query.

        @return: A deferred from get_page
        """
        if not url_context:
            url_context = URLContext(
                self.endpoint, self.bucket, self.object_name)
        d = self.get_page(
            url_context.get_url(), method=self.action, postdata=self.data,
            headers=self.get_headers())
        return d.addErrback(s3_error_wrapper)
Example #7
0
class Query(BaseQuery):
    """A query for submission to the S3 service."""

    def __init__(self, bucket=None, object_name=None, data="",
                 content_type=None, metadata={}, amz_headers={},
                 body_producer=None, *args, **kwargs):
        super(Query, self).__init__(*args, **kwargs)

        # data might be None or "", alas.
        if data and body_producer is not None:
            raise ValueError("data and body_producer are mutually exclusive.")

        self.bucket = bucket
        self.object_name = object_name
        self.data = data
        self.body_producer = body_producer
        self.content_type = content_type
        self.metadata = metadata
        self.amz_headers = amz_headers
        self._date = datetimeToString()
        if not self.endpoint or not self.endpoint.host:
            self.endpoint = AWSServiceEndpoint(S3_ENDPOINT)
        self.endpoint.set_method(self.action)

    @property
    def date(self):
        """
        Return the date and emit a deprecation warning.
        """
        warnings.warn("txaws.s3.client.Query.date is a deprecated attribute",
                      DeprecationWarning,
                      stacklevel=2)
        return self._date

    @date.setter
    def date(self, value):
        """
        Set the date.

        @param value: The new date for this L{Query}.
        @type value: L{str}
        """
        self._date = value

    def set_content_type(self):
        """
        Set the content type based on the file extension used in the object
        name.
        """
        if self.object_name and not self.content_type:
            # XXX nothing is currently done with the encoding... we may
            # need to in the future
            self.content_type, encoding = mimetypes.guess_type(
                self.object_name, strict=False)

    def get_headers(self, instant):
        """
        Build the list of headers needed in order to perform S3 operations.
        """
        headers = {'x-amz-date': _auth_v4.makeAMZDate(instant)}
        if self.body_producer is None:
            data = self.data
            if data is None:
                data = b""
            headers["x-amz-content-sha256"] = hashlib.sha256(data).hexdigest()
        else:
            data = None
            headers["x-amz-content-sha256"] = b"UNSIGNED-PAYLOAD"
        for key, value in self.metadata.iteritems():
            headers["x-amz-meta-" + key] = value
        for key, value in self.amz_headers.iteritems():
            headers["x-amz-" + key] = value

        # Before we check if the content type is set, let's see if we can set
        # it by guessing the the mimetype.
        self.set_content_type()
        if self.content_type is not None:
            headers["Content-Type"] = self.content_type
        if self.creds is not None:
            headers["Authorization"] = self.sign(
                headers,
                data,
                s3_url_context(self.endpoint, self.bucket, self.object_name),
                instant,
                method=self.action)
        return headers

    def sign(self, headers, data, url_context, instant, method,
             region=REGION_US_EAST_1):
        """Sign this query using its built in credentials."""
        headers["host"] = url_context.get_encoded_host()

        if data is None:
            request = _auth_v4._CanonicalRequest.from_request_components(
                method=method,
                url=url_context.get_encoded_path(),
                headers=headers,
                headers_to_sign=('host', 'x-amz-date'),
                payload_hash=None,
            )
        else:
            request = _auth_v4._CanonicalRequest.from_request_components_and_payload(
                method=method,
                url=url_context.get_encoded_path(),
                headers=headers,
                headers_to_sign=('host', 'x-amz-date'),
                payload=data,
            )

        return _auth_v4._make_authorization_header(
            region=region,
            service="s3",
            canonical_request=request,
            credentials=self.creds,
            instant=instant)

    def submit(self, url_context=None, utcnow=datetime.datetime.utcnow):
        """Submit this query.

        @return: A deferred from get_page
        """
        if not url_context:
            url_context = s3_url_context(
                self.endpoint, self.bucket, self.object_name)
        d = self.get_page(
            url_context.get_encoded_url(),
            method=self.action,
            postdata=self.data or b"",
            headers=self.get_headers(utcnow()),
        )

        return d.addErrback(s3_error_wrapper)
Example #8
0
class Query(BaseQuery):
    """A query for submission to the S3 service."""

    def __init__(self, bucket=None, object_name=None, data="",
                 content_type=None, metadata={}, amz_headers={},
                 body_producer=None, *args, **kwargs):
        super(Query, self).__init__(*args, **kwargs)

        # data might be None or "", alas.
        if data and body_producer is not None:
            raise ValueError("data and body_producer are mutually exclusive.")

        self.bucket = bucket
        self.object_name = object_name
        self.data = data
        self.body_producer = body_producer
        self.content_type = content_type
        self.metadata = metadata
        self.amz_headers = amz_headers
        self._date = datetimeToString()
        if not self.endpoint or not self.endpoint.host:
            self.endpoint = AWSServiceEndpoint(S3_ENDPOINT)
        self.endpoint.set_method(self.action)

    @property
    def date(self):
        """
        Return the date and emit a deprecation warning.
        """
        warnings.warn("txaws.s3.client.Query.date is a deprecated attribute",
                      DeprecationWarning,
                      stacklevel=2)
        return self._date

    @date.setter
    def date(self, value):
        """
        Set the date.

        @param value: The new date for this L{Query}.
        @type value: L{str}
        """
        self._date = value

    def set_content_type(self):
        """
        Set the content type based on the file extension used in the object
        name.
        """
        if self.object_name and not self.content_type:
            # XXX nothing is currently done with the encoding... we may
            # need to in the future
            self.content_type, encoding = mimetypes.guess_type(
                self.object_name, strict=False)

    def get_headers(self, instant):
        """
        Build the list of headers needed in order to perform S3 operations.
        """
        headers = {'x-amz-date': _auth_v4.makeAMZDate(instant)}
        if self.body_producer is None:
            data = self.data
            if data is None:
                data = b""
            headers["x-amz-content-sha256"] = hashlib.sha256(data).hexdigest()
        else:
            data = None
            headers["x-amz-content-sha256"] = b"UNSIGNED-PAYLOAD"
        for key, value in self.metadata.items():
            headers["x-amz-meta-" + key] = value
        for key, value in self.amz_headers.items():
            headers["x-amz-" + key] = value

        # Before we check if the content type is set, let's see if we can set
        # it by guessing the the mimetype.
        self.set_content_type()
        if self.content_type is not None:
            headers["Content-Type"] = self.content_type
        if self.creds is not None:
            headers["Authorization"] = self.sign(
                headers,
                data,
                s3_url_context(self.endpoint, self.bucket, self.object_name),
                instant,
                method=self.action)
        return headers

    def sign(self, headers, data, url_context, instant, method,
             region=REGION_US_EAST_1):
        """Sign this query using its built in credentials."""
        headers["host"] = url_context.get_encoded_host()

        if data is None:
            request = _auth_v4._CanonicalRequest.from_request_components(
                method=method,
                url=url_context.get_encoded_path(),
                headers=headers,
                headers_to_sign=('host', 'x-amz-date'),
                payload_hash=None,
            )
        else:
            request = _auth_v4._CanonicalRequest.from_request_components_and_payload(
                method=method,
                url=url_context.get_encoded_path(),
                headers=headers,
                headers_to_sign=('host', 'x-amz-date'),
                payload=data,
            )

        return _auth_v4._make_authorization_header(
            region=region,
            service="s3",
            canonical_request=request,
            credentials=self.creds,
            instant=instant)

    def submit(self, url_context=None, utcnow=datetime.datetime.utcnow):
        """Submit this query.

        @return: A deferred from get_page
        """
        if not url_context:
            url_context = s3_url_context(
                self.endpoint, self.bucket, self.object_name)
        d = self.get_page(
            url_context.get_encoded_url(),
            method=self.action,
            postdata=self.data or b"",
            headers=self.get_headers(utcnow()),
        )

        return d.addErrback(s3_error_wrapper)
Example #9
0
class Query(BaseQuery):
    """A query for submission to the S3 service."""

    def __init__(self, bucket=None, object_name=None, data="",
                 content_type=None, metadata={}, amz_headers={}, *args,
                 **kwargs):
        super(Query, self).__init__(*args, **kwargs)
        self.bucket = bucket
        self.object_name = object_name
        self.data = data
        self.content_type = content_type
        self.metadata = metadata
        self.amz_headers = amz_headers
        self.date = datetimeToString()
        if not self.endpoint or not self.endpoint.host:
            self.endpoint = AWSServiceEndpoint(S3_ENDPOINT)
        self.endpoint.set_method(self.action)

    def set_content_type(self):
        """
        Set the content type based on the file extension used in the object
        name.
        """
        if self.object_name and not self.content_type:
            # XXX nothing is currently done with the encoding... we may
            # need to in the future
            self.content_type, encoding = mimetypes.guess_type(
                self.object_name, strict=False)

    def get_headers(self):
        """
        Build the list of headers needed in order to perform S3 operations.
        """
        headers = {"Content-Length": len(self.data),
                   "Content-MD5": calculate_md5(self.data),
                   "Date": self.date}
        for key, value in self.metadata.iteritems():
            headers["x-amz-meta-" + key] = value
        for key, values in self.amz_headers.iteritems():
            if isinstance(values, tuple):
                headers["x-amz-" + key] = ",".join(values)
            else:
                headers["x-amz-" + key] = values
        # Before we check if the content type is set, let's see if we can set
        # it by guessing the the mimetype.
        self.set_content_type()
        if self.content_type is not None:
            headers["Content-Type"] = self.content_type
        if self.creds is not None:
            signature = self.sign(headers)
            headers["Authorization"] = "AWS %s:%s" % (
                self.creds.access_key, signature)
        return headers

    def get_canonicalized_amz_headers(self, headers):
        """
        Get the headers defined by Amazon S3.
        """
        headers = [
            (name.lower(), value) for name, value in headers.iteritems()
            if name.lower().startswith("x-amz-")]
        headers.sort()
        # XXX missing spec implementation:
        # txAWS doesn't currently unfold long headers
        def represent(n, vs):
            if isinstance(vs, tuple):
                return "".join(["%s:%s\n" % (n, vs) for v in vs])
            else:
                return "%s:%s\n" % (n, vs)

        return "".join([represent(name, value) for name, value in headers])

    def get_canonicalized_resource(self):
        """
        Get an S3 resource path.
        """
        path = "/"
        if self.bucket is not None:
            path += self.bucket
        if self.bucket is not None and self.object_name:
            if not self.object_name.startswith("/"):
                path += "/"
            path += self.object_name
        elif self.bucket is not None and not path.endswith("/"):
            path += "/"
        return path

    def sign(self, headers):
        """Sign this query using its built in credentials."""
        text = (self.action + "\n" +
                headers.get("Content-MD5", "") + "\n" +
                headers.get("Content-Type", "") + "\n" +
                headers.get("Date", "") + "\n" +
                self.get_canonicalized_amz_headers(headers) +
                self.get_canonicalized_resource())
        return self.creds.sign(text, hash_type="sha1")

    def submit(self, url_context=None):
        """Submit this query.

        @return: A deferred from get_page
        """
        if not url_context:
            url_context = URLContext(
                self.endpoint, self.bucket, self.object_name)
        d = self.get_page(
            url_context.get_url(), method=self.action, postdata=self.data,
            headers=self.get_headers())
        return d.addErrback(s3_error_wrapper)
Example #10
0
class Query(BaseQuery):
    """A query for submission to the S3 service."""

    def __init__(self, bucket=None, object_name=None, data="",
                 content_type=None, metadata={}, amz_headers={}, *args,
                 **kwargs):
        super(Query, self).__init__(*args, **kwargs)
        self.bucket = bucket
        self.object_name = object_name
        self.data = data
        self.content_type = content_type
        self.metadata = metadata
        self.amz_headers = amz_headers
        self.date = datetimeToString()
        if not self.endpoint or not self.endpoint.host:
            self.endpoint = AWSServiceEndpoint(S3_ENDPOINT)
        self.endpoint.set_method(self.action)

    def set_content_type(self):
        """
        Set the content type based on the file extension used in the object
        name.
        """
        if self.object_name and not self.content_type:
            # XXX nothing is currently done with the encoding... we may
            # need to in the future
            self.content_type, encoding = mimetypes.guess_type(
                self.object_name, strict=False)

    def get_headers(self):
        """
        Build the list of headers needed in order to perform S3 operations.
        """
        headers = {"Content-Length": len(self.data),
                   "Content-MD5": calculate_md5(self.data),
                   "Date": self.date}
        for key, value in self.metadata.iteritems():
            headers["x-amz-meta-" + key] = value
        for key, value in self.amz_headers.iteritems():
            headers["x-amz-" + key] = value
        # Before we check if the content type is set, let's see if we can set
        # it by guessing the the mimetype.
        self.set_content_type()
        if self.content_type is not None:
            headers["Content-Type"] = self.content_type
        if self.creds is not None:
            signature = self.sign(headers)
            headers["Authorization"] = "AWS %s:%s" % (
                self.creds.access_key, signature)
        return headers

    def get_canonicalized_amz_headers(self, headers):
        """
        Get the headers defined by Amazon S3.
        """
        headers = [
            (name.lower(), value) for name, value in headers.iteritems()
            if name.lower().startswith("x-amz-")]
        headers.sort()
        # XXX missing spec implementation:
        # 1) txAWS doesn't currently combine headers with the same name
        # 2) txAWS doesn't currently unfold long headers
        return "".join("%s:%s\n" % (name, value) for name, value in headers)

    def get_canonicalized_resource(self):
        """
        Get an S3 resource path.
        """
        path = "/"
        if self.bucket is not None:
            path += self.bucket
        if self.bucket is not None and self.object_name:
            if not self.object_name.startswith("/"):
                path += "/"
            path += self.object_name
        elif self.bucket is not None and not path.endswith("/"):
            path += "/"
        return path

    def sign(self, headers):
        """Sign this query using its built in credentials."""
        text = (self.action + "\n" +
                headers.get("Content-MD5", "") + "\n" +
                headers.get("Content-Type", "") + "\n" +
                headers.get("Date", "") + "\n" +
                self.get_canonicalized_amz_headers(headers) +
                self.get_canonicalized_resource())
        return self.creds.sign(text, hash_type="sha1")

    def submit(self, url_context=None):
        """Submit this query.

        @return: A deferred from get_page
        """
        if not url_context:
            url_context = URLContext(
                self.endpoint, self.bucket, self.object_name)
        d = self.get_page(
            url_context.get_url(), method=self.action, postdata=self.data,
            headers=self.get_headers())
        return d.addErrback(s3_error_wrapper)
Example #11
0
File: client.py Project: wiz/txaws
class Query(BaseQuery):
    """A query for submission to the S3 service."""
    def __init__(self,
                 bucket=None,
                 object_name=None,
                 data="",
                 content_type=None,
                 metadata={},
                 amz_headers={},
                 *args,
                 **kwargs):
        super(Query, self).__init__(*args, **kwargs)
        self.bucket = bucket
        self.object_name = object_name
        self.data = data
        self.content_type = content_type
        self.metadata = metadata
        self.amz_headers = amz_headers
        self.date = datetimeToString()
        if not self.endpoint or not self.endpoint.host:
            self.endpoint = AWSServiceEndpoint(S3_ENDPOINT)
        self.endpoint.set_method(self.action)

    def set_content_type(self):
        """
        Set the content type based on the file extension used in the object
        name.
        """
        if self.object_name and not self.content_type:
            # XXX nothing is currently done with the encoding... we may
            # need to in the future
            self.content_type, encoding = mimetypes.guess_type(
                self.object_name, strict=False)

    def get_headers(self):
        """
        Build the list of headers needed in order to perform S3 operations.
        """
        headers = {
            "Content-Length": len(self.data),
            "Content-MD5": calculate_md5(self.data),
            "Date": self.date
        }
        for key, value in self.metadata.iteritems():
            headers["x-amz-meta-" + key] = value
        for key, values in self.amz_headers.iteritems():
            if isinstance(values, tuple):
                headers["x-amz-" + key] = ",".join(values)
            else:
                headers["x-amz-" + key] = values
        # Before we check if the content type is set, let's see if we can set
        # it by guessing the the mimetype.
        self.set_content_type()
        if self.content_type is not None:
            headers["Content-Type"] = self.content_type
        if self.creds is not None:
            signature = self.sign(headers)
            headers["Authorization"] = "AWS %s:%s" % (self.creds.access_key,
                                                      signature)
        return headers

    def get_canonicalized_amz_headers(self, headers):
        """
        Get the headers defined by Amazon S3.
        """
        headers = [(name.lower(), value)
                   for name, value in headers.iteritems()
                   if name.lower().startswith("x-amz-")]
        headers.sort()

        # XXX missing spec implementation:
        # txAWS doesn't currently unfold long headers
        def represent(n, vs):
            if isinstance(vs, tuple):
                return "".join(["%s:%s\n" % (n, vs) for v in vs])
            else:
                return "%s:%s\n" % (n, vs)

        return "".join([represent(name, value) for name, value in headers])

    def get_canonicalized_resource(self):
        """
        Get an S3 resource path.
        """

        # As <http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAuthentication.html>
        # says, if there is a subresource (e.g. ?acl), it is included, but other query
        # parameters (e.g. ?prefix=... in a GET Bucket request) are not included.
        # Yes, that makes no sense in terms of either security or consistency.
        resource = self.object_name
        if resource:
            q = resource.find('?')
            if q >= 0:
                # There can be both a subresource and other parameters, for example
                # '?versions&prefix=foo'. "You are in a maze of twisty edge cases..."
                firstparam = resource[q:].partition('&')[
                    0]  # includes the initial '?'
                resource = resource[:q]  # strip the query
                if '=' not in firstparam:
                    resource += firstparam  # add back '?subresource' if present

        path = "/"
        if self.bucket is not None:
            path += self.bucket
        if self.bucket is not None and resource:
            if not resource.startswith("/"):
                path += "/"
            path += resource
        elif self.bucket is not None and not path.endswith("/"):
            path += "/"
        return path

    def sign(self, headers):
        """Sign this query using its built in credentials."""
        text = (self.action + "\n" + headers.get("Content-MD5", "") + "\n" +
                headers.get("Content-Type", "") + "\n" +
                headers.get("Date", "") + "\n" +
                self.get_canonicalized_amz_headers(headers) +
                self.get_canonicalized_resource())
        return self.creds.sign(text, hash_type="sha1")

    def submit(self, url_context=None):
        """Submit this query.

        @return: A deferred from get_page
        """
        if not url_context:
            url_context = URLContext(self.endpoint, self.bucket,
                                     self.object_name)
        d = self.get_page(url_context.get_url(),
                          method=self.action,
                          postdata=self.data,
                          headers=self.get_headers())
        return d.addErrback(s3_error_wrapper)