Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2830)

Unified Diff: boto/cloudfront/distribution.py

Issue 8386013: Merging in latest boto. (Closed) Base URL: svn://svn.chromium.org/boto
Patch Set: Redoing vendor drop by deleting and then merging. Created 9 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « boto/cloudfront/__init__.py ('k') | boto/cloudfront/invalidation.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: boto/cloudfront/distribution.py
diff --git a/boto/cloudfront/distribution.py b/boto/cloudfront/distribution.py
index ed245cbc4e951e0a510366991f9025736b83edb7..01ceed490ea760fab9b504695ba91453e8858e50 100644
--- a/boto/cloudfront/distribution.py
+++ b/boto/cloudfront/distribution.py
@@ -20,6 +20,8 @@
# IN THE SOFTWARE.
import uuid
+import base64
+import json
from boto.cloudfront.identity import OriginAccessIdentity
from boto.cloudfront.object import Object, StreamingObject
from boto.cloudfront.signers import ActiveTrustedSigners, TrustedSigners
@@ -286,6 +288,7 @@ class Distribution:
self.id = id
self.last_modified_time = last_modified_time
self.status = status
+ self.in_progress_invalidation_batches = 0
self.active_signers = None
self.etag = None
self._bucket = None
@@ -308,6 +311,8 @@ class Distribution:
self.last_modified_time = value
elif name == 'Status':
self.status = value
+ elif name == 'InProgressInvalidationBatches':
+ self.in_progress_invalidation_batches = int(value)
elif name == 'DomainName':
self.domain_name = value
else:
@@ -316,12 +321,18 @@ class Distribution:
def update(self, enabled=None, cnames=None, comment=None):
"""
Update the configuration of the Distribution. The only values
- of the DistributionConfig that can be updated are:
+ of the DistributionConfig that can be directly updated are:
* CNAMES
* Comment
* Whether the Distribution is enabled or not
+ Any changes to the ``trusted_signers`` or ``origin`` properties of
+ this distribution's current config object will also be included in
+ the update. Therefore, to set the origin access identity for this
+ distribution, set ``Distribution.config.origin.origin_access_identity``
+ before calling this update method.
+
:type enabled: bool
:param enabled: Whether the Distribution is active or not.
@@ -371,19 +382,23 @@ class Distribution:
self.connection.delete_distribution(self.id, self.etag)
def _get_bucket(self):
- if not self._bucket:
- bucket_name = self.config.origin.replace('.s3.amazonaws.com', '')
- from boto.s3.connection import S3Connection
- s3 = S3Connection(self.connection.aws_access_key_id,
- self.connection.aws_secret_access_key,
- proxy=self.connection.proxy,
- proxy_port=self.connection.proxy_port,
- proxy_user=self.connection.proxy_user,
- proxy_pass=self.connection.proxy_pass)
- self._bucket = s3.get_bucket(bucket_name)
- self._bucket.distribution = self
- self._bucket.set_key_class(self._object_class)
- return self._bucket
+ if isinstance(self.config.origin, S3Origin):
+ if not self._bucket:
+ bucket_dns_name = self.config.origin.dns_name
+ bucket_name = bucket_dns_name.replace('.s3.amazonaws.com', '')
+ from boto.s3.connection import S3Connection
+ s3 = S3Connection(self.connection.aws_access_key_id,
+ self.connection.aws_secret_access_key,
+ proxy=self.connection.proxy,
+ proxy_port=self.connection.proxy_port,
+ proxy_user=self.connection.proxy_user,
+ proxy_pass=self.connection.proxy_pass)
+ self._bucket = s3.get_bucket(bucket_name)
+ self._bucket.distribution = self
+ self._bucket.set_key_class(self._object_class)
+ return self._bucket
+ else:
+ raise NotImplementedError('Unable to get_objects on CustomOrigin')
def get_objects(self):
"""
@@ -469,17 +484,198 @@ class Distribution:
:rtype: :class:`boto.cloudfront.object.Object`
:return: The newly created object.
"""
- if self.config.origin_access_identity:
+ if self.config.origin.origin_access_identity:
policy = 'private'
else:
policy = 'public-read'
bucket = self._get_bucket()
object = bucket.new_key(name)
object.set_contents_from_file(content, headers=headers, policy=policy)
- if self.config.origin_access_identity:
+ if self.config.origin.origin_access_identity:
self.set_permissions(object, replace)
return object
-
+
+ def create_signed_url(self, url, keypair_id,
+ expire_time=None, valid_after_time=None,
+ ip_address=None, policy_url=None,
+ private_key_file=None, private_key_string=None):
+ """
+ Creates a signed CloudFront URL that is only valid within the specified
+ parameters.
+
+ :type url: str
+ :param url: The URL of the protected object.
+
+ :type keypair_id: str
+ :param keypair_id: The keypair ID of the Amazon KeyPair used to sign
+ theURL. This ID MUST correspond to the private key
+ specified with private_key_file or
+ private_key_string.
+
+ :type expire_time: int
+ :param expire_time: The expiry time of the URL. If provided, the URL
+ will expire after the time has passed. If not
+ provided the URL will never expire. Format is a
+ unix epoch. Use time.time() + duration_in_sec.
+
+ :type valid_after_time: int
+ :param valid_after_time: If provided, the URL will not be valid until
+ after valid_after_time. Format is a unix
+ epoch. Use time.time() + secs_until_valid.
+
+ :type ip_address: str
+ :param ip_address: If provided, only allows access from the specified
+ IP address. Use '192.168.0.10' for a single IP or
+ use '192.168.0.0/24' CIDR notation for a subnet.
+
+ :type policy_url: str
+ :param policy_url: If provided, allows the signature to contain
+ wildcard globs in the URL. For example, you could
+ provide: 'http://example.com/media/*' and the policy
+ and signature would allow access to all contents of
+ the media subdirectory. If not specified, only
+ allow access to the exact url provided in 'url'.
+
+ :type private_key_file: str or file object.
+ :param private_key_file: If provided, contains the filename of the
+ private key file used for signing or an open
+ file object containing the private key
+ contents. Only one of private_key_file or
+ private_key_string can be provided.
+
+ :type private_key_string: str
+ :param private_key_string: If provided, contains the private key string
+ used for signing. Only one of
+ private_key_file or private_key_string can
+ be provided.
+
+ :rtype: str
+ :return: The signed URL.
+ """
+ # Get the required parameters
+ params = self._create_signing_params(
+ url=url, keypair_id=keypair_id, expire_time=expire_time,
+ valid_after_time=valid_after_time, ip_address=ip_address,
+ policy_url=policy_url, private_key_file=private_key_file,
+ private_key_string=private_key_string)
+
+ #combine these into a full url
+ if "?" in url:
+ sep = "&"
+ else:
+ sep = "?"
+ signed_url_params = []
+ for key in ["Expires", "Policy", "Signature", "Key-Pair-Id"]:
+ if key in params:
+ param = "%s=%s" % (key, params[key])
+ signed_url_params.append(param)
+ signed_url = url + sep + "&".join(signed_url_params)
+ return signed_url
+
+ def _create_signing_params(self, url, keypair_id,
+ expire_time=None, valid_after_time=None,
+ ip_address=None, policy_url=None,
+ private_key_file=None, private_key_string=None):
+ """
+ Creates the required URL parameters for a signed URL.
+ """
+ params = {}
+ # Check if we can use a canned policy
+ if expire_time and not valid_after_time and not ip_address and not policy_url:
+ # we manually construct this policy string to ensure formatting
+ # matches signature
+ policy = self._canned_policy(url, expire_time)
+ params["Expires"] = str(expire_time)
+ else:
+ # If no policy_url is specified, default to the full url.
+ if policy_url is None:
+ policy_url = url
+ # Can't use canned policy
+ policy = self._custom_policy(policy_url, expires=None,
+ valid_after=None,
+ ip_address=None)
+ encoded_policy = self._url_base64_encode(policy)
+ params["Policy"] = encoded_policy
+ #sign the policy
+ signature = self._sign_string(policy, private_key_file, private_key_string)
+ #now base64 encode the signature (URL safe as well)
+ encoded_signature = self._url_base64_encode(signature)
+ params["Signature"] = encoded_signature
+ params["Key-Pair-Id"] = keypair_id
+ return params
+
+ @staticmethod
+ def _canned_policy(resource, expires):
+ """
+ Creates a canned policy string.
+ """
+ policy = ('{"Statement":[{"Resource":"%(resource)s",'
+ '"Condition":{"DateLessThan":{"AWS:EpochTime":'
+ '%(expires)s}}}]}' % locals())
+ return policy
+
+ @staticmethod
+ def _custom_policy(resource, expires=None, valid_after=None, ip_address=None):
+ """
+ Creates a custom policy string based on the supplied parameters.
+ """
+ condition = {}
+ if expires:
+ condition["DateLessThan"] = {"AWS:EpochTime": expires}
+ if valid_after:
+ condition["DateGreaterThan"] = {"AWS:EpochTime": valid_after}
+ if ip_address:
+ if '/' not in ip_address:
+ ip_address += "/32"
+ condition["IpAddress"] = {"AWS:SourceIp": ip_address}
+ policy = {"Statement": [{
+ "Resource": resource,
+ "Condition": condition}]}
+ return json.dumps(policy, separators=(",", ":"))
+
+ @staticmethod
+ def _sign_string(message, private_key_file=None, private_key_string=None):
+ """
+ Signs a string for use with Amazon CloudFront. Requires the M2Crypto
+ library be installed.
+ """
+ try:
+ from M2Crypto import EVP
+ except ImportError:
+ raise NotImplementedError("Boto depends on the python M2Crypto "
+ "library to generate signed URLs for "
+ "CloudFront")
+ # Make sure only one of private_key_file and private_key_string is set
+ if private_key_file and private_key_string:
+ raise ValueError("Only specify the private_key_file or the private_key_string not both")
+ if not private_key_file and not private_key_string:
+ raise ValueError("You must specify one of private_key_file or private_key_string")
+ # if private_key_file is a file object read the key string from there
+ if isinstance(private_key_file, file):
+ private_key_string = private_key_file.read()
+ # Now load key and calculate signature
+ if private_key_string:
+ key = EVP.load_key_string(private_key_string)
+ else:
+ key = EVP.load_key(private_key_file)
+ key.reset_context(md='sha1')
+ key.sign_init()
+ key.sign_update(str(message))
+ signature = key.sign_final()
+ return signature
+
+ @staticmethod
+ def _url_base64_encode(msg):
+ """
+ Base64 encodes a string using the URL-safe characters specified by
+ Amazon.
+ """
+ msg_base64 = base64.b64encode(msg)
+ msg_base64 = msg_base64.replace('+', '-')
+ msg_base64 = msg_base64.replace('=', '_')
+ msg_base64 = msg_base64.replace('/', '~')
+ return msg_base64
+
class StreamingDistribution(Distribution):
def __init__(self, connection=None, config=None, domain_name='',
@@ -498,12 +694,19 @@ class StreamingDistribution(Distribution):
def update(self, enabled=None, cnames=None, comment=None):
"""
Update the configuration of the StreamingDistribution. The only values
- of the StreamingDistributionConfig that can be updated are:
+ of the StreamingDistributionConfig that can be directly updated are:
* CNAMES
* Comment
* Whether the Distribution is enabled or not
+ Any changes to the ``trusted_signers`` or ``origin`` properties of
+ this distribution's current config object will also be included in
+ the update. Therefore, to set the origin access identity for this
+ distribution, set
+ ``StreamingDistribution.config.origin.origin_access_identity``
+ before calling this update method.
+
:type enabled: bool
:param enabled: Whether the StreamingDistribution is active or not.
« no previous file with comments | « boto/cloudfront/__init__.py ('k') | boto/cloudfront/invalidation.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698