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

Side by Side 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, 1 month 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « boto/cloudfront/__init__.py ('k') | boto/cloudfront/invalidation.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (c) 2006-2009 Mitch Garnaat http://garnaat.org/ 1 # Copyright (c) 2006-2009 Mitch Garnaat http://garnaat.org/
2 # 2 #
3 # Permission is hereby granted, free of charge, to any person obtaining a 3 # Permission is hereby granted, free of charge, to any person obtaining a
4 # copy of this software and associated documentation files (the 4 # copy of this software and associated documentation files (the
5 # "Software"), to deal in the Software without restriction, including 5 # "Software"), to deal in the Software without restriction, including
6 # without limitation the rights to use, copy, modify, merge, publish, dis- 6 # without limitation the rights to use, copy, modify, merge, publish, dis-
7 # tribute, sublicense, and/or sell copies of the Software, and to permit 7 # tribute, sublicense, and/or sell copies of the Software, and to permit
8 # persons to whom the Software is furnished to do so, subject to the fol- 8 # persons to whom the Software is furnished to do so, subject to the fol-
9 # lowing conditions: 9 # lowing conditions:
10 # 10 #
11 # The above copyright notice and this permission notice shall be included 11 # The above copyright notice and this permission notice shall be included
12 # in all copies or substantial portions of the Software. 12 # in all copies or substantial portions of the Software.
13 # 13 #
14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- 15 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
16 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 16 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
17 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 17 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 # IN THE SOFTWARE. 20 # IN THE SOFTWARE.
21 21
22 import uuid 22 import uuid
23 import base64
24 import json
23 from boto.cloudfront.identity import OriginAccessIdentity 25 from boto.cloudfront.identity import OriginAccessIdentity
24 from boto.cloudfront.object import Object, StreamingObject 26 from boto.cloudfront.object import Object, StreamingObject
25 from boto.cloudfront.signers import ActiveTrustedSigners, TrustedSigners 27 from boto.cloudfront.signers import ActiveTrustedSigners, TrustedSigners
26 from boto.cloudfront.logging import LoggingInfo 28 from boto.cloudfront.logging import LoggingInfo
27 from boto.cloudfront.origin import S3Origin, CustomOrigin 29 from boto.cloudfront.origin import S3Origin, CustomOrigin
28 from boto.s3.acl import ACL 30 from boto.s3.acl import ACL
29 31
30 class DistributionConfig: 32 class DistributionConfig:
31 33
32 def __init__(self, connection=None, origin=None, enabled=False, 34 def __init__(self, connection=None, origin=None, enabled=False,
(...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after
279 class Distribution: 281 class Distribution:
280 282
281 def __init__(self, connection=None, config=None, domain_name='', 283 def __init__(self, connection=None, config=None, domain_name='',
282 id='', last_modified_time=None, status=''): 284 id='', last_modified_time=None, status=''):
283 self.connection = connection 285 self.connection = connection
284 self.config = config 286 self.config = config
285 self.domain_name = domain_name 287 self.domain_name = domain_name
286 self.id = id 288 self.id = id
287 self.last_modified_time = last_modified_time 289 self.last_modified_time = last_modified_time
288 self.status = status 290 self.status = status
291 self.in_progress_invalidation_batches = 0
289 self.active_signers = None 292 self.active_signers = None
290 self.etag = None 293 self.etag = None
291 self._bucket = None 294 self._bucket = None
292 self._object_class = Object 295 self._object_class = Object
293 296
294 def startElement(self, name, attrs, connection): 297 def startElement(self, name, attrs, connection):
295 if name == 'DistributionConfig': 298 if name == 'DistributionConfig':
296 self.config = DistributionConfig() 299 self.config = DistributionConfig()
297 return self.config 300 return self.config
298 elif name == 'ActiveTrustedSigners': 301 elif name == 'ActiveTrustedSigners':
299 self.active_signers = ActiveTrustedSigners() 302 self.active_signers = ActiveTrustedSigners()
300 return self.active_signers 303 return self.active_signers
301 else: 304 else:
302 return None 305 return None
303 306
304 def endElement(self, name, value, connection): 307 def endElement(self, name, value, connection):
305 if name == 'Id': 308 if name == 'Id':
306 self.id = value 309 self.id = value
307 elif name == 'LastModifiedTime': 310 elif name == 'LastModifiedTime':
308 self.last_modified_time = value 311 self.last_modified_time = value
309 elif name == 'Status': 312 elif name == 'Status':
310 self.status = value 313 self.status = value
314 elif name == 'InProgressInvalidationBatches':
315 self.in_progress_invalidation_batches = int(value)
311 elif name == 'DomainName': 316 elif name == 'DomainName':
312 self.domain_name = value 317 self.domain_name = value
313 else: 318 else:
314 setattr(self, name, value) 319 setattr(self, name, value)
315 320
316 def update(self, enabled=None, cnames=None, comment=None): 321 def update(self, enabled=None, cnames=None, comment=None):
317 """ 322 """
318 Update the configuration of the Distribution. The only values 323 Update the configuration of the Distribution. The only values
319 of the DistributionConfig that can be updated are: 324 of the DistributionConfig that can be directly updated are:
320 325
321 * CNAMES 326 * CNAMES
322 * Comment 327 * Comment
323 * Whether the Distribution is enabled or not 328 * Whether the Distribution is enabled or not
324 329
330 Any changes to the ``trusted_signers`` or ``origin`` properties of
331 this distribution's current config object will also be included in
332 the update. Therefore, to set the origin access identity for this
333 distribution, set ``Distribution.config.origin.origin_access_identity``
334 before calling this update method.
335
325 :type enabled: bool 336 :type enabled: bool
326 :param enabled: Whether the Distribution is active or not. 337 :param enabled: Whether the Distribution is active or not.
327 338
328 :type cnames: list of str 339 :type cnames: list of str
329 :param cnames: The DNS CNAME's associated with this 340 :param cnames: The DNS CNAME's associated with this
330 Distribution. Maximum of 10 values. 341 Distribution. Maximum of 10 values.
331 342
332 :type comment: str or unicode 343 :type comment: str or unicode
333 :param comment: The comment associated with the Distribution. 344 :param comment: The comment associated with the Distribution.
334 345
(...skipping 29 matching lines...) Expand all
364 375
365 def delete(self): 376 def delete(self):
366 """ 377 """
367 Delete this CloudFront Distribution. The content 378 Delete this CloudFront Distribution. The content
368 associated with the Distribution is not deleted from 379 associated with the Distribution is not deleted from
369 the underlying Origin bucket in S3. 380 the underlying Origin bucket in S3.
370 """ 381 """
371 self.connection.delete_distribution(self.id, self.etag) 382 self.connection.delete_distribution(self.id, self.etag)
372 383
373 def _get_bucket(self): 384 def _get_bucket(self):
374 if not self._bucket: 385 if isinstance(self.config.origin, S3Origin):
375 bucket_name = self.config.origin.replace('.s3.amazonaws.com', '') 386 if not self._bucket:
376 from boto.s3.connection import S3Connection 387 bucket_dns_name = self.config.origin.dns_name
377 s3 = S3Connection(self.connection.aws_access_key_id, 388 bucket_name = bucket_dns_name.replace('.s3.amazonaws.com', '')
378 self.connection.aws_secret_access_key, 389 from boto.s3.connection import S3Connection
379 proxy=self.connection.proxy, 390 s3 = S3Connection(self.connection.aws_access_key_id,
380 proxy_port=self.connection.proxy_port, 391 self.connection.aws_secret_access_key,
381 proxy_user=self.connection.proxy_user, 392 proxy=self.connection.proxy,
382 proxy_pass=self.connection.proxy_pass) 393 proxy_port=self.connection.proxy_port,
383 self._bucket = s3.get_bucket(bucket_name) 394 proxy_user=self.connection.proxy_user,
384 self._bucket.distribution = self 395 proxy_pass=self.connection.proxy_pass)
385 self._bucket.set_key_class(self._object_class) 396 self._bucket = s3.get_bucket(bucket_name)
386 return self._bucket 397 self._bucket.distribution = self
398 self._bucket.set_key_class(self._object_class)
399 return self._bucket
400 else:
401 raise NotImplementedError('Unable to get_objects on CustomOrigin')
387 402
388 def get_objects(self): 403 def get_objects(self):
389 """ 404 """
390 Return a list of all content objects in this distribution. 405 Return a list of all content objects in this distribution.
391 406
392 :rtype: list of :class:`boto.cloudfront.object.Object` 407 :rtype: list of :class:`boto.cloudfront.object.Object`
393 :return: The content objects 408 :return: The content objects
394 """ 409 """
395 bucket = self._get_bucket() 410 bucket = self._get_bucket()
396 objs = [] 411 objs = []
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
462 for the new object. 477 for the new object.
463 478
464 :type headers: dict 479 :type headers: dict
465 :param headers: A dictionary containing additional headers 480 :param headers: A dictionary containing additional headers
466 you would like associated with the new 481 you would like associated with the new
467 object in S3. 482 object in S3.
468 483
469 :rtype: :class:`boto.cloudfront.object.Object` 484 :rtype: :class:`boto.cloudfront.object.Object`
470 :return: The newly created object. 485 :return: The newly created object.
471 """ 486 """
472 if self.config.origin_access_identity: 487 if self.config.origin.origin_access_identity:
473 policy = 'private' 488 policy = 'private'
474 else: 489 else:
475 policy = 'public-read' 490 policy = 'public-read'
476 bucket = self._get_bucket() 491 bucket = self._get_bucket()
477 object = bucket.new_key(name) 492 object = bucket.new_key(name)
478 object.set_contents_from_file(content, headers=headers, policy=policy) 493 object.set_contents_from_file(content, headers=headers, policy=policy)
479 if self.config.origin_access_identity: 494 if self.config.origin.origin_access_identity:
480 self.set_permissions(object, replace) 495 self.set_permissions(object, replace)
481 return object 496 return object
482 497
498 def create_signed_url(self, url, keypair_id,
499 expire_time=None, valid_after_time=None,
500 ip_address=None, policy_url=None,
501 private_key_file=None, private_key_string=None):
502 """
503 Creates a signed CloudFront URL that is only valid within the specified
504 parameters.
505
506 :type url: str
507 :param url: The URL of the protected object.
508
509 :type keypair_id: str
510 :param keypair_id: The keypair ID of the Amazon KeyPair used to sign
511 theURL. This ID MUST correspond to the private key
512 specified with private_key_file or
513 private_key_string.
514
515 :type expire_time: int
516 :param expire_time: The expiry time of the URL. If provided, the URL
517 will expire after the time has passed. If not
518 provided the URL will never expire. Format is a
519 unix epoch. Use time.time() + duration_in_sec.
520
521 :type valid_after_time: int
522 :param valid_after_time: If provided, the URL will not be valid until
523 after valid_after_time. Format is a unix
524 epoch. Use time.time() + secs_until_valid.
525
526 :type ip_address: str
527 :param ip_address: If provided, only allows access from the specified
528 IP address. Use '192.168.0.10' for a single IP or
529 use '192.168.0.0/24' CIDR notation for a subnet.
530
531 :type policy_url: str
532 :param policy_url: If provided, allows the signature to contain
533 wildcard globs in the URL. For example, you could
534 provide: 'http://example.com/media/*' and the policy
535 and signature would allow access to all contents of
536 the media subdirectory. If not specified, only
537 allow access to the exact url provided in 'url'.
538
539 :type private_key_file: str or file object.
540 :param private_key_file: If provided, contains the filename of the
541 private key file used for signing or an open
542 file object containing the private key
543 contents. Only one of private_key_file or
544 private_key_string can be provided.
545
546 :type private_key_string: str
547 :param private_key_string: If provided, contains the private key string
548 used for signing. Only one of
549 private_key_file or private_key_string can
550 be provided.
551
552 :rtype: str
553 :return: The signed URL.
554 """
555 # Get the required parameters
556 params = self._create_signing_params(
557 url=url, keypair_id=keypair_id, expire_time=expire_time,
558 valid_after_time=valid_after_time, ip_address=ip_address,
559 policy_url=policy_url, private_key_file=private_key_file,
560 private_key_string=private_key_string)
561
562 #combine these into a full url
563 if "?" in url:
564 sep = "&"
565 else:
566 sep = "?"
567 signed_url_params = []
568 for key in ["Expires", "Policy", "Signature", "Key-Pair-Id"]:
569 if key in params:
570 param = "%s=%s" % (key, params[key])
571 signed_url_params.append(param)
572 signed_url = url + sep + "&".join(signed_url_params)
573 return signed_url
574
575 def _create_signing_params(self, url, keypair_id,
576 expire_time=None, valid_after_time=None,
577 ip_address=None, policy_url=None,
578 private_key_file=None, private_key_string=None):
579 """
580 Creates the required URL parameters for a signed URL.
581 """
582 params = {}
583 # Check if we can use a canned policy
584 if expire_time and not valid_after_time and not ip_address and not polic y_url:
585 # we manually construct this policy string to ensure formatting
586 # matches signature
587 policy = self._canned_policy(url, expire_time)
588 params["Expires"] = str(expire_time)
589 else:
590 # If no policy_url is specified, default to the full url.
591 if policy_url is None:
592 policy_url = url
593 # Can't use canned policy
594 policy = self._custom_policy(policy_url, expires=None,
595 valid_after=None,
596 ip_address=None)
597 encoded_policy = self._url_base64_encode(policy)
598 params["Policy"] = encoded_policy
599 #sign the policy
600 signature = self._sign_string(policy, private_key_file, private_key_stri ng)
601 #now base64 encode the signature (URL safe as well)
602 encoded_signature = self._url_base64_encode(signature)
603 params["Signature"] = encoded_signature
604 params["Key-Pair-Id"] = keypair_id
605 return params
606
607 @staticmethod
608 def _canned_policy(resource, expires):
609 """
610 Creates a canned policy string.
611 """
612 policy = ('{"Statement":[{"Resource":"%(resource)s",'
613 '"Condition":{"DateLessThan":{"AWS:EpochTime":'
614 '%(expires)s}}}]}' % locals())
615 return policy
616
617 @staticmethod
618 def _custom_policy(resource, expires=None, valid_after=None, ip_address=None ):
619 """
620 Creates a custom policy string based on the supplied parameters.
621 """
622 condition = {}
623 if expires:
624 condition["DateLessThan"] = {"AWS:EpochTime": expires}
625 if valid_after:
626 condition["DateGreaterThan"] = {"AWS:EpochTime": valid_after}
627 if ip_address:
628 if '/' not in ip_address:
629 ip_address += "/32"
630 condition["IpAddress"] = {"AWS:SourceIp": ip_address}
631 policy = {"Statement": [{
632 "Resource": resource,
633 "Condition": condition}]}
634 return json.dumps(policy, separators=(",", ":"))
635
636 @staticmethod
637 def _sign_string(message, private_key_file=None, private_key_string=None):
638 """
639 Signs a string for use with Amazon CloudFront. Requires the M2Crypto
640 library be installed.
641 """
642 try:
643 from M2Crypto import EVP
644 except ImportError:
645 raise NotImplementedError("Boto depends on the python M2Crypto "
646 "library to generate signed URLs for "
647 "CloudFront")
648 # Make sure only one of private_key_file and private_key_string is set
649 if private_key_file and private_key_string:
650 raise ValueError("Only specify the private_key_file or the private_k ey_string not both")
651 if not private_key_file and not private_key_string:
652 raise ValueError("You must specify one of private_key_file or privat e_key_string")
653 # if private_key_file is a file object read the key string from there
654 if isinstance(private_key_file, file):
655 private_key_string = private_key_file.read()
656 # Now load key and calculate signature
657 if private_key_string:
658 key = EVP.load_key_string(private_key_string)
659 else:
660 key = EVP.load_key(private_key_file)
661 key.reset_context(md='sha1')
662 key.sign_init()
663 key.sign_update(str(message))
664 signature = key.sign_final()
665 return signature
666
667 @staticmethod
668 def _url_base64_encode(msg):
669 """
670 Base64 encodes a string using the URL-safe characters specified by
671 Amazon.
672 """
673 msg_base64 = base64.b64encode(msg)
674 msg_base64 = msg_base64.replace('+', '-')
675 msg_base64 = msg_base64.replace('=', '_')
676 msg_base64 = msg_base64.replace('/', '~')
677 return msg_base64
678
483 class StreamingDistribution(Distribution): 679 class StreamingDistribution(Distribution):
484 680
485 def __init__(self, connection=None, config=None, domain_name='', 681 def __init__(self, connection=None, config=None, domain_name='',
486 id='', last_modified_time=None, status=''): 682 id='', last_modified_time=None, status=''):
487 Distribution.__init__(self, connection, config, domain_name, 683 Distribution.__init__(self, connection, config, domain_name,
488 id, last_modified_time, status) 684 id, last_modified_time, status)
489 self._object_class = StreamingObject 685 self._object_class = StreamingObject
490 686
491 def startElement(self, name, attrs, connection): 687 def startElement(self, name, attrs, connection):
492 if name == 'StreamingDistributionConfig': 688 if name == 'StreamingDistributionConfig':
493 self.config = StreamingDistributionConfig() 689 self.config = StreamingDistributionConfig()
494 return self.config 690 return self.config
495 else: 691 else:
496 return Distribution.startElement(self, name, attrs, connection) 692 return Distribution.startElement(self, name, attrs, connection)
497 693
498 def update(self, enabled=None, cnames=None, comment=None): 694 def update(self, enabled=None, cnames=None, comment=None):
499 """ 695 """
500 Update the configuration of the StreamingDistribution. The only values 696 Update the configuration of the StreamingDistribution. The only values
501 of the StreamingDistributionConfig that can be updated are: 697 of the StreamingDistributionConfig that can be directly updated are:
502 698
503 * CNAMES 699 * CNAMES
504 * Comment 700 * Comment
505 * Whether the Distribution is enabled or not 701 * Whether the Distribution is enabled or not
506 702
703 Any changes to the ``trusted_signers`` or ``origin`` properties of
704 this distribution's current config object will also be included in
705 the update. Therefore, to set the origin access identity for this
706 distribution, set
707 ``StreamingDistribution.config.origin.origin_access_identity``
708 before calling this update method.
709
507 :type enabled: bool 710 :type enabled: bool
508 :param enabled: Whether the StreamingDistribution is active or not. 711 :param enabled: Whether the StreamingDistribution is active or not.
509 712
510 :type cnames: list of str 713 :type cnames: list of str
511 :param cnames: The DNS CNAME's associated with this 714 :param cnames: The DNS CNAME's associated with this
512 Distribution. Maximum of 10 values. 715 Distribution. Maximum of 10 values.
513 716
514 :type comment: str or unicode 717 :type comment: str or unicode
515 :param comment: The comment associated with the Distribution. 718 :param comment: The comment associated with the Distribution.
516 719
(...skipping 14 matching lines...) Expand all
531 self.etag = self.connection.set_streaming_distribution_config(self.id, 734 self.etag = self.connection.set_streaming_distribution_config(self.id,
532 self.etag, 735 self.etag,
533 new_config ) 736 new_config )
534 self.config = new_config 737 self.config = new_config
535 self._object_class = StreamingObject 738 self._object_class = StreamingObject
536 739
537 def delete(self): 740 def delete(self):
538 self.connection.delete_streaming_distribution(self.id, self.etag) 741 self.connection.delete_streaming_distribution(self.id, self.etag)
539 742
540 743
OLDNEW
« 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