示例#1
0
def get_playlist_with_signed_url(playlist_path):
    # Get playlist file from S3
    s3_conn = connect_s3(settings.ACCESS_KEY_ID, settings.SECRET_ACCESS_KEY)
    bucket = s3_conn.get_bucket(settings.PLAYLIST_BUCKET_NAME)
    key = bucket.get_key(playlist_path)

    if key is None:
        raise Exception("No such key was found. key={}".format(key))

    fp = StringIO()
    key.get_contents_to_file(fp)
    fp.seek(0)

    # Convert with signed url
    cf_conn = CloudFrontConnection(settings.ACCESS_KEY_ID, settings.SECRET_ACCESS_KEY)
    dist = Distribution(cf_conn)
    expire_time = int(time.time() + 60 * 60)  # 60 mins

    outlines = []
    for line in fp.readlines():
        line = line.rstrip()
        matchObj = re.search(TS_PATTERN, line)
        if matchObj is not None:
            file_name = matchObj.group()
            url = os.path.join(os.path.dirname(os.path.join(settings.CLOUDFRONT_URL_PREFIX, playlist_path)), file_name)
            signed_url = dist.create_signed_url(url, settings.CLOUDFRONT_KEYPAIR_ID, expire_time, private_key_file=settings.CLOUDFRONT_PRIVATE_KEY_FILE_LOCATION)
            outlines.append(signed_url)
        else:
            outlines.append(line)
    fp.close()
    return '\n'.join(outlines)
示例#2
0
 def get_distribution_info(self, distribution_id):
     response = self.make_request('GET', '/%s/distribution/%s' % (self.Version, distribution_id))
     body = response.read()
     if response.status >= 300:
         raise CloudFrontServerError(response.status, response.reason, body)
     d = Distribution(connection=self)
     response_headers = response.msg
     for key in response_headers.keys():
         if key.lower() == 'etag':
             d.etag = response_headers[key]
     h = handler.XmlHandler(d, self)
     xml.sax.parseString(body, h)
     return d
示例#3
0
 def setUp(self):
     self.pk_str = dedent("""
         -----BEGIN RSA PRIVATE KEY-----
         MIICXQIBAAKBgQDA7ki9gI/lRygIoOjV1yymgx6FYFlzJ+z1ATMaLo57nL57AavW
         hb68HYY8EA0GJU9xQdMVaHBogF3eiCWYXSUZCWM/+M5+ZcdQraRRScucmn6g4EvY
         2K4W2pxbqH8vmUikPxir41EeBPLjMOzKvbzzQy9e/zzIQVREKSp/7y1mywIDAQAB
         AoGABc7mp7XYHynuPZxChjWNJZIq+A73gm0ASDv6At7F8Vi9r0xUlQe/v0AQS3yc
         N8QlyR4XMbzMLYk3yjxFDXo4ZKQtOGzLGteCU2srANiLv26/imXA8FVidZftTAtL
         viWQZBVPTeYIA69ATUYPEq0a5u5wjGyUOij9OWyuy01mbPkCQQDluYoNpPOekQ0Z
         WrPgJ5rxc8f6zG37ZVoDBiexqtVShIF5W3xYuWhW5kYb0hliYfkq15cS7t9m95h3
         1QJf/xI/AkEA1v9l/WN1a1N3rOK4VGoCokx7kR2SyTMSbZgF9IWJNOugR/WZw7HT
         njipO3c9dy1Ms9pUKwUF46d7049ck8HwdQJARgrSKuLWXMyBH+/l1Dx/I4tXuAJI
         rlPyo+VmiOc7b5NzHptkSHEPfR9s1OK0VqjknclqCJ3Ig86OMEtEFBzjZQJBAKYz
         470hcPkaGk7tKYAgP48FvxRsnzeooptURW5E+M+PQ2W9iDPPOX9739+Xi02hGEWF
         B0IGbQoTRFdE4VVcPK0CQQCeS84lODlC0Y2BZv2JxW3Osv/WkUQ4dslfAQl1T303
         7uwwr7XTroMv8dIFQIPreoPhRKmd/SbJzbiKfS/4QDhU
         -----END RSA PRIVATE KEY-----
         """)
     self.pk_id = "PK123456789754"
     self.dist = Distribution()
     self.canned_policy = (
         '{"Statement":[{"Resource":'
         '"http://d604721fxaaqy9.cloudfront.net/horizon.jpg'
         '?large=yes&license=yes",'
         '"Condition":{"DateLessThan":{"AWS:EpochTime":1258237200}}}]}')
     self.custom_policy_1 = (
         '{ \n'
         '   "Statement": [{ \n'
         '      "Resource":"http://d604721fxaaqy9.cloudfront.net/training/*", \n'
         '      "Condition":{ \n'
         '         "IpAddress":{"AWS:SourceIp":"145.168.143.0/24"}, \n'
         '         "DateLessThan":{"AWS:EpochTime":1258237200}      \n'
         '      } \n'
         '   }] \n'
         '}\n')
     self.custom_policy_2 = (
         '{ \n'
         '   "Statement": [{ \n'
         '      "Resource":"http://*", \n'
         '      "Condition":{ \n'
         '         "IpAddress":{"AWS:SourceIp":"216.98.35.1/32"},\n'
         '         "DateGreaterThan":{"AWS:EpochTime":1241073790},\n'
         '         "DateLessThan":{"AWS:EpochTime":1255674716}\n'
         '      } \n'
         '   }] \n'
         '}\n')
示例#4
0
class AmazonCloudFrontDistribution:
    """
    Represents a Cloud Front Distribution and its supported actions
    """
    def __init__(self, distribution_id):
        if (cf_2_gcs.conf.AWS_ACCESS_KEY and cf_2_gcs.conf.AWS_SECRET_KEY):
            self.cf_conn = CloudFrontConnection(cf_2_gcs.conf.AWS_ACCESS_KEY, cf_2_gcs.conf.AWS_SECRET_KEY)
            try:
                self.distribution = Distribution(connection=self.cf_conn, config=None, id=distribution_id)
            except Exception:
                log.error("Unable to associate distribution %s. Is the ID correct?" % distribution_id)
                self.distribution = None

        self.distribution_id = distribution_id

    def get_root_url(self):
        """
        Returns the root URL domain for the instance distribution id
        """
        info = self.cf_conn.get_distribution_info(self.distribution_id)
        protocol = str(info.config.Protocol)
        distribution = str(info.domain_name)
        return protocol + "://" + distribution

    def get_public_url(self, path):
        """
        Returns the public URL for a path

        Args:
            path (str): The pathname for the file relative to the CloudFront distribution
                method and domain. Example: /test/video.mp4.
        """

        return self.get_root_url() + path

    def get_signed_url(self, path, expire_time=3600):
        """
        Signs an URL with a CloudFront private key (for private content)

        Args:
            path (str): The pathname for the file relative to the CloudFront distribution
                method and domain. Example: /test/video.mp4.
            expire_time (Optional|int): How many seconds the signed URL will
                be valid. Defaults to 3600 (1 hour).

        """

        # setup cloudfront trusted keypair
        keypair_id = cf_2_gcs.conf.CLOUDFRONT_KEYPAIR_ID
        private_key_file = cf_2_gcs.conf.CLOUDFRONT_PRIVATE_KEY_FILE

        # setup expire time from now
        expire_time = int(time.time()+expire_time)

        # append root domain to the url
        url = self.get_root_url() + path

        return self.distribution.create_signed_url(url=url, keypair_id=keypair_id, private_key_file=private_key_file, expire_time=expire_time)
示例#5
0
    def __init__(self, settings):
        super(Repository, self).__init__(settings)

        self.type = "cloudfront"
        self.checkSettings()

        # Needed for creating signed url
        self.distribution = Distribution()
        self.checkVcdbFile()
示例#6
0
    def __init__(self, distribution_id):
        if (cf_2_gcs.conf.AWS_ACCESS_KEY and cf_2_gcs.conf.AWS_SECRET_KEY):
            self.cf_conn = CloudFrontConnection(cf_2_gcs.conf.AWS_ACCESS_KEY, cf_2_gcs.conf.AWS_SECRET_KEY)
            try:
                self.distribution = Distribution(connection=self.cf_conn, config=None, id=distribution_id)
            except Exception:
                log.error("Unable to associate distribution %s. Is the ID correct?" % distribution_id)
                self.distribution = None

        self.distribution_id = distribution_id
示例#7
0
def cloudfront_sign(asset_path, expiry=1485864000):
    cache_key = (u"%s_%s" % (asset_path, expiry)).encode("utf-8")
    cached_url = cloudfront_url_cache.get(cache_key, None)
    if cached_url:
        return cached_url
    cached_url = persistent_cloudfront_url_cache.get(cache_key, None)
    if cached_url:
        cloudfront_url_cache[cache_key] = cached_url
        return cached_url

    dist = Distribution(domain_name='d1gy1csjh89rhq.cloudfront.net')
    key_pair_id = 'APKAIQD6FQE7UOMX3R2Q'

    http_resource = 'https://assets.monsters-et-manus.com/' + asset_path
    http_signed_url = dist.create_signed_url(http_resource, key_pair_id, expiry, private_key_string=blueprint.cloudfront_private_key)

    cloudfront_url_cache[cache_key] = http_signed_url
    persistent_cloudfront_url_cache[cache_key] = http_signed_url

    return http_signed_url
示例#8
0
def get_playlist_with_signed_url(playlist_path):
    # Get playlist file from S3
    s3_conn = connect_s3(settings.ACCESS_KEY_ID, settings.SECRET_ACCESS_KEY)
    bucket = s3_conn.get_bucket(settings.PLAYLIST_BUCKET_NAME)
    key = bucket.get_key(playlist_path)

    if key is None:
        raise Exception("No such key was found. key={}".format(key))

    fp = StringIO()
    key.get_contents_to_file(fp)
    fp.seek(0)

    # Convert with signed url
    cf_conn = CloudFrontConnection(settings.ACCESS_KEY_ID,
                                   settings.SECRET_ACCESS_KEY)
    dist = Distribution(cf_conn)
    expire_time = int(time.time() + 60 * 60)  # 60 mins

    outlines = []
    for line in fp.readlines():
        line = line.rstrip()
        matchObj = re.search(TS_PATTERN, line)
        if matchObj is not None:
            file_name = matchObj.group()
            url = os.path.join(
                os.path.dirname(
                    os.path.join(settings.CLOUDFRONT_URL_PREFIX,
                                 playlist_path)), file_name)
            signed_url = dist.create_signed_url(
                url,
                settings.CLOUDFRONT_KEYPAIR_ID,
                expire_time,
                private_key_file=settings.CLOUDFRONT_PRIVATE_KEY_FILE_LOCATION)
            outlines.append(signed_url)
        else:
            outlines.append(line)
    fp.close()
    return '\n'.join(outlines)
示例#9
0
文件: s3.py 项目: dalou/django-workon
 def url(self, name):
     if name.startswith('http://') or name.startswith('https://'):
         return name
     if self.secure_urls:
         # Get crypted url from cloudfront, ex : https://media.cbien.com
         name = self._normalize_name(self._clean_name(name))
         url = "%s//%s" % (self.url_protocol,
                             os.path.join(self.custom_domain, self.custom_path, name))
         dist = Distribution()
         return dist.create_signed_url(
             url=url,
             keypair_id=settings.CLOUDFRONT_KEY_PAIR_ID,
             private_key_string=settings.CLOUDFRONT_PRIVATE_KEY,
             expire_time=int(time.time() + int(settings.CLOUDFRONT_ROTATION_SECONDS))
         )
     elif self.signed_urls:
         name = os.path.join(self.location,  name)
         return self.connection.generate_url(self.querystring_expire,
             method='GET', bucket=self.bucket.name, key=self._encode_name(name),
             query_auth=self.querystring_auth, force_http=not self.secure_urls)
     else:
         name = self._normalize_name(self._clean_name(name))
         return "%s//%s" % (self.url_protocol,
                               os.path.join(self.custom_domain, name))
示例#10
0
 def setUp(self):
     self.pk_str = dedent("""
         -----BEGIN RSA PRIVATE KEY-----
         MIICXQIBAAKBgQDA7ki9gI/lRygIoOjV1yymgx6FYFlzJ+z1ATMaLo57nL57AavW
         hb68HYY8EA0GJU9xQdMVaHBogF3eiCWYXSUZCWM/+M5+ZcdQraRRScucmn6g4EvY
         2K4W2pxbqH8vmUikPxir41EeBPLjMOzKvbzzQy9e/zzIQVREKSp/7y1mywIDAQAB
         AoGABc7mp7XYHynuPZxChjWNJZIq+A73gm0ASDv6At7F8Vi9r0xUlQe/v0AQS3yc
         N8QlyR4XMbzMLYk3yjxFDXo4ZKQtOGzLGteCU2srANiLv26/imXA8FVidZftTAtL
         viWQZBVPTeYIA69ATUYPEq0a5u5wjGyUOij9OWyuy01mbPkCQQDluYoNpPOekQ0Z
         WrPgJ5rxc8f6zG37ZVoDBiexqtVShIF5W3xYuWhW5kYb0hliYfkq15cS7t9m95h3
         1QJf/xI/AkEA1v9l/WN1a1N3rOK4VGoCokx7kR2SyTMSbZgF9IWJNOugR/WZw7HT
         njipO3c9dy1Ms9pUKwUF46d7049ck8HwdQJARgrSKuLWXMyBH+/l1Dx/I4tXuAJI
         rlPyo+VmiOc7b5NzHptkSHEPfR9s1OK0VqjknclqCJ3Ig86OMEtEFBzjZQJBAKYz
         470hcPkaGk7tKYAgP48FvxRsnzeooptURW5E+M+PQ2W9iDPPOX9739+Xi02hGEWF
         B0IGbQoTRFdE4VVcPK0CQQCeS84lODlC0Y2BZv2JxW3Osv/WkUQ4dslfAQl1T303
         7uwwr7XTroMv8dIFQIPreoPhRKmd/SbJzbiKfS/4QDhU
         -----END RSA PRIVATE KEY-----
         """)
     self.pk_id = "PK123456789754"
     self.dist = Distribution()
     self.canned_policy = (
         '{"Statement":[{"Resource":'
         '"http://d604721fxaaqy9.cloudfront.net/horizon.jpg'
         '?large=yes&license=yes",'
         '"Condition":{"DateLessThan":{"AWS:EpochTime":1258237200}}}]}')
     self.custom_policy_1 = (
         '{ \n'
         '   "Statement": [{ \n'
         '      "Resource":"http://d604721fxaaqy9.cloudfront.net/training/*", \n'
         '      "Condition":{ \n'
         '         "IpAddress":{"AWS:SourceIp":"145.168.143.0/24"}, \n'
         '         "DateLessThan":{"AWS:EpochTime":1258237200}      \n'
         '      } \n'
         '   }] \n'
         '}\n')
     self.custom_policy_2 = (
         '{ \n'
         '   "Statement": [{ \n'
         '      "Resource":"http://*", \n'
         '      "Condition":{ \n'
         '         "IpAddress":{"AWS:SourceIp":"216.98.35.1/32"},\n'
         '         "DateGreaterThan":{"AWS:EpochTime":1241073790},\n'
         '         "DateLessThan":{"AWS:EpochTime":1255674716}\n'
         '      } \n'
         '   }] \n'
         '}\n')
示例#11
0
class Repository(BaseRepository):
    def __init__(self, settings):
        super(Repository, self).__init__(settings)

        self.type = "cloudfront"
        self.checkSettings()

        # Needed for creating signed url
        self.distribution = Distribution()
        self.checkVcdbFile()

    def checkSettings(self):

        try:
            self.repository_url = self.settings["repository_url"]
        except KeyError:
            self.abort(
                'Check repository_settings{} in configuration file. Missing  \"repository_url\".'
            )

        try:
            self.keypair_id = self.settings['keypair_id']
        except KeyError:
            self.abort(
                'Check repository_settings{} in configuration file. Missing  \"keypair_id\".'
            )

        try:
            self.private_key_file = self.settings['private_key_file']
        except KeyError:
            self.abort(
                'Check repository_settings{} in configuration file. Missing  \"private_key_file\".'
            )

    def getRemoteFielPath(self, fname):
        rpath = os.path.join(self.repository_url, fname)
        rpath = self.create_signed_url(rpath)
        return rpath

    def create_signed_url(self, url):
        """ Create signed url with no expiration """
        return self.distribution.create_signed_url(
            url=url,
            keypair_id=self.keypair_id,
            private_key_file=self.private_key_file)
示例#12
0
class Repository(BaseRepository):

    def __init__(self, settings):
        super(Repository, self).__init__(settings)

        self.type = "cloudfront"
        self.checkSettings()

        # Needed for creating signed url
        self.distribution = Distribution()
        self.checkVcdbFile()

    def checkSettings(self):

        try:
            self.repository_url = self.settings["repository_url"]
        except KeyError:
            self.abort('Check repository_settings{} in configuration file. Missing  \"repository_url\".' )

        try:
            self.keypair_id = self.settings['keypair_id']
        except KeyError:
            self.abort('Check repository_settings{} in configuration file. Missing  \"keypair_id\".' )

        try:
            self.private_key_file = self.settings['private_key_file']
        except KeyError:
            self.abort('Check repository_settings{} in configuration file. Missing  \"private_key_file\".' )


    def getRemoteFielPath(self, fname):
        rpath =  os.path.join(self.repository_url, fname)
        rpath =  self.create_signed_url(rpath)
        return rpath

    def create_signed_url(self, url):
        """ Create signed url with no expiration """
        return self.distribution.create_signed_url(
            url=url,
            keypair_id=self.keypair_id,
            private_key_file=self.private_key_file
        )
示例#13
0
 def create_distribution(self,
                         origin,
                         enabled,
                         caller_reference='',
                         cnames=None,
                         comment=''):
     config = DistributionConfig(origin=origin,
                                 enabled=enabled,
                                 caller_reference=caller_reference,
                                 cnames=cnames,
                                 comment=comment)
     response = self.make_request('POST',
                                  '/%s/distribution' % self.Version,
                                  {'Content-Type': 'text/xml'},
                                  data=config.to_xml())
     body = response.read()
     if response.status == 201:
         d = Distribution(connection=self)
         h = handler.XmlHandler(d, self)
         xml.sax.parseString(body, h)
         return d
     else:
         raise CloudFrontServerError(response.status, response.reason, body)
示例#14
0
class CloudfrontSignedUrlsTest(unittest.TestCase):

    cloudfront = True
    notdefault = True

    def setUp(self):
        self.pk_str = dedent("""
            -----BEGIN RSA PRIVATE KEY-----
            MIICXQIBAAKBgQDA7ki9gI/lRygIoOjV1yymgx6FYFlzJ+z1ATMaLo57nL57AavW
            hb68HYY8EA0GJU9xQdMVaHBogF3eiCWYXSUZCWM/+M5+ZcdQraRRScucmn6g4EvY
            2K4W2pxbqH8vmUikPxir41EeBPLjMOzKvbzzQy9e/zzIQVREKSp/7y1mywIDAQAB
            AoGABc7mp7XYHynuPZxChjWNJZIq+A73gm0ASDv6At7F8Vi9r0xUlQe/v0AQS3yc
            N8QlyR4XMbzMLYk3yjxFDXo4ZKQtOGzLGteCU2srANiLv26/imXA8FVidZftTAtL
            viWQZBVPTeYIA69ATUYPEq0a5u5wjGyUOij9OWyuy01mbPkCQQDluYoNpPOekQ0Z
            WrPgJ5rxc8f6zG37ZVoDBiexqtVShIF5W3xYuWhW5kYb0hliYfkq15cS7t9m95h3
            1QJf/xI/AkEA1v9l/WN1a1N3rOK4VGoCokx7kR2SyTMSbZgF9IWJNOugR/WZw7HT
            njipO3c9dy1Ms9pUKwUF46d7049ck8HwdQJARgrSKuLWXMyBH+/l1Dx/I4tXuAJI
            rlPyo+VmiOc7b5NzHptkSHEPfR9s1OK0VqjknclqCJ3Ig86OMEtEFBzjZQJBAKYz
            470hcPkaGk7tKYAgP48FvxRsnzeooptURW5E+M+PQ2W9iDPPOX9739+Xi02hGEWF
            B0IGbQoTRFdE4VVcPK0CQQCeS84lODlC0Y2BZv2JxW3Osv/WkUQ4dslfAQl1T303
            7uwwr7XTroMv8dIFQIPreoPhRKmd/SbJzbiKfS/4QDhU
            -----END RSA PRIVATE KEY-----
            """)
        self.pk_id = "PK123456789754"
        self.dist = Distribution()
        self.canned_policy = (
            '{"Statement":[{"Resource":'
            '"http://d604721fxaaqy9.cloudfront.net/horizon.jpg'
            '?large=yes&license=yes",'
            '"Condition":{"DateLessThan":{"AWS:EpochTime":1258237200}}}]}')
        self.custom_policy_1 = (
            '{ \n'
            '   "Statement": [{ \n'
            '      "Resource":"http://d604721fxaaqy9.cloudfront.net/training/*", \n'
            '      "Condition":{ \n'
            '         "IpAddress":{"AWS:SourceIp":"145.168.143.0/24"}, \n'
            '         "DateLessThan":{"AWS:EpochTime":1258237200}      \n'
            '      } \n'
            '   }] \n'
            '}\n')
        self.custom_policy_2 = (
            '{ \n'
            '   "Statement": [{ \n'
            '      "Resource":"http://*", \n'
            '      "Condition":{ \n'
            '         "IpAddress":{"AWS:SourceIp":"216.98.35.1/32"},\n'
            '         "DateGreaterThan":{"AWS:EpochTime":1241073790},\n'
            '         "DateLessThan":{"AWS:EpochTime":1255674716}\n'
            '      } \n'
            '   }] \n'
            '}\n')

    def test_encode_custom_policy_1(self):
        """
        Test base64 encoding custom policy 1 from Amazon's documentation.
        """
        expected = ("eyAKICAgIlN0YXRlbWVudCI6IFt7IAogICAgICAiUmVzb3VyY2Ui"
                    "OiJodHRwOi8vZDYwNDcyMWZ4YWFxeTkuY2xvdWRmcm9udC5uZXQv"
                    "dHJhaW5pbmcvKiIsIAogICAgICAiQ29uZGl0aW9uIjp7IAogICAg"
                    "ICAgICAiSXBBZGRyZXNzIjp7IkFXUzpTb3VyY2VJcCI6IjE0NS4x"
                    "NjguMTQzLjAvMjQifSwgCiAgICAgICAgICJEYXRlTGVzc1RoYW4i"
                    "OnsiQVdTOkVwb2NoVGltZSI6MTI1ODIzNzIwMH0gICAgICAKICAg"
                    "ICAgfSAKICAgfV0gCn0K")
        encoded = self.dist._url_base64_encode(self.custom_policy_1)
        self.assertEqual(expected, encoded)

    def test_encode_custom_policy_2(self):
        """
        Test base64 encoding custom policy 2 from Amazon's documentation.
        """
        expected = ("eyAKICAgIlN0YXRlbWVudCI6IFt7IAogICAgICAiUmVzb3VyY2Ui"
                    "OiJodHRwOi8vKiIsIAogICAgICAiQ29uZGl0aW9uIjp7IAogICAg"
                    "ICAgICAiSXBBZGRyZXNzIjp7IkFXUzpTb3VyY2VJcCI6IjIxNi45"
                    "OC4zNS4xLzMyIn0sCiAgICAgICAgICJEYXRlR3JlYXRlclRoYW4i"
                    "OnsiQVdTOkVwb2NoVGltZSI6MTI0MTA3Mzc5MH0sCiAgICAgICAg"
                    "ICJEYXRlTGVzc1RoYW4iOnsiQVdTOkVwb2NoVGltZSI6MTI1NTY3"
                    "NDcxNn0KICAgICAgfSAKICAgfV0gCn0K")
        encoded = self.dist._url_base64_encode(self.custom_policy_2)
        self.assertEqual(expected, encoded)

    def test_sign_canned_policy(self):
        """
        Test signing the canned policy from amazon's cloudfront documentation.
        """
        expected = ("Nql641NHEUkUaXQHZINK1FZ~SYeUSoBJMxjdgqrzIdzV2gyEXPDN"
                    "v0pYdWJkflDKJ3xIu7lbwRpSkG98NBlgPi4ZJpRRnVX4kXAJK6td"
                    "Nx6FucDB7OVqzcxkxHsGFd8VCG1BkC-Afh9~lOCMIYHIaiOB6~5j"
                    "t9w2EOwi6sIIqrg_")
        sig = self.dist._sign_string(self.canned_policy, private_key_string=self.pk_str)
        encoded_sig = self.dist._url_base64_encode(sig)
        self.assertEqual(expected, encoded_sig)

    def test_sign_canned_policy_pk_file(self):
        """
        Test signing the canned policy from amazon's cloudfront documentation
        with a file object.
        """
        expected = ("Nql641NHEUkUaXQHZINK1FZ~SYeUSoBJMxjdgqrzIdzV2gyEXPDN"
                    "v0pYdWJkflDKJ3xIu7lbwRpSkG98NBlgPi4ZJpRRnVX4kXAJK6td"
                    "Nx6FucDB7OVqzcxkxHsGFd8VCG1BkC-Afh9~lOCMIYHIaiOB6~5j"
                    "t9w2EOwi6sIIqrg_")
        pk_file = tempfile.TemporaryFile()
        pk_file.write(self.pk_str)
        pk_file.seek(0)
        sig = self.dist._sign_string(self.canned_policy, private_key_file=pk_file)
        encoded_sig = self.dist._url_base64_encode(sig)
        self.assertEqual(expected, encoded_sig)

    def test_sign_canned_policy_pk_file_name(self):
        """
        Test signing the canned policy from amazon's cloudfront documentation
        with a file name.
        """
        expected = ("Nql641NHEUkUaXQHZINK1FZ~SYeUSoBJMxjdgqrzIdzV2gyEXPDN"
                    "v0pYdWJkflDKJ3xIu7lbwRpSkG98NBlgPi4ZJpRRnVX4kXAJK6td"
                    "Nx6FucDB7OVqzcxkxHsGFd8VCG1BkC-Afh9~lOCMIYHIaiOB6~5j"
                    "t9w2EOwi6sIIqrg_")
        pk_file = tempfile.NamedTemporaryFile()
        pk_file.write(self.pk_str)
        pk_file.flush()
        sig = self.dist._sign_string(self.canned_policy, private_key_file=pk_file.name)
        encoded_sig = self.dist._url_base64_encode(sig)
        self.assertEqual(expected, encoded_sig)

    def test_sign_canned_policy_unicode(self):
        """
        Test signing the canned policy from amazon's cloudfront documentation.
        """
        expected = ("Nql641NHEUkUaXQHZINK1FZ~SYeUSoBJMxjdgqrzIdzV2gyEXPDN"
                    "v0pYdWJkflDKJ3xIu7lbwRpSkG98NBlgPi4ZJpRRnVX4kXAJK6td"
                    "Nx6FucDB7OVqzcxkxHsGFd8VCG1BkC-Afh9~lOCMIYHIaiOB6~5j"
                    "t9w2EOwi6sIIqrg_")
        unicode_policy = unicode(self.canned_policy)
        sig = self.dist._sign_string(unicode_policy, private_key_string=self.pk_str)
        encoded_sig = self.dist._url_base64_encode(sig)
        self.assertEqual(expected, encoded_sig)

    def test_sign_custom_policy_1(self):
        """
        Test signing custom policy 1 from amazon's cloudfront documentation.
        """
        expected = ("cPFtRKvUfYNYmxek6ZNs6vgKEZP6G3Cb4cyVt~FjqbHOnMdxdT7e"
                    "T6pYmhHYzuDsFH4Jpsctke2Ux6PCXcKxUcTIm8SO4b29~1QvhMl-"
                    "CIojki3Hd3~Unxjw7Cpo1qRjtvrimW0DPZBZYHFZtiZXsaPt87yB"
                    "P9GWnTQoaVysMxQ_")
        sig = self.dist._sign_string(self.custom_policy_1, private_key_string=self.pk_str)
        encoded_sig = self.dist._url_base64_encode(sig)
        self.assertEqual(expected, encoded_sig)

    def test_sign_custom_policy_2(self):
        """
        Test signing custom policy 2 from amazon's cloudfront documentation.
        """
        expected = ("rc~5Qbbm8EJXjUTQ6Cn0LAxR72g1DOPrTmdtfbWVVgQNw0q~KHUA"
                    "mBa2Zv1Wjj8dDET4XSL~Myh44CLQdu4dOH~N9huH7QfPSR~O4tIO"
                    "S1WWcP~2JmtVPoQyLlEc8YHRCuN3nVNZJ0m4EZcXXNAS-0x6Zco2"
                    "SYx~hywTRxWR~5Q_")
        sig = self.dist._sign_string(self.custom_policy_2, private_key_string=self.pk_str)
        encoded_sig = self.dist._url_base64_encode(sig)
        self.assertEqual(expected, encoded_sig)

    def test_create_canned_policy(self):
        """
        Test that a canned policy is generated correctly.
        """
        url = "http://1234567.cloudfront.com/test_resource.mp3?dog=true"
        expires = 999999
        policy = self.dist._canned_policy(url, expires)
        policy = json.loads(policy)

        self.assertEqual(1, len(policy.keys()))
        statements = policy["Statement"]
        self.assertEqual(1, len(statements))
        statement = statements[0]
        resource = statement["Resource"]
        self.assertEqual(url, resource)
        condition = statement["Condition"]
        self.assertEqual(1, len(condition.keys()))
        date_less_than = condition["DateLessThan"]
        self.assertEqual(1, len(date_less_than.keys()))
        aws_epoch_time = date_less_than["AWS:EpochTime"]
        self.assertEqual(expires, aws_epoch_time)
        
    def test_custom_policy_expires_and_policy_url(self):
        """
        Test that a custom policy can be created with an expire time and an
        arbitrary URL.
        """
        url = "http://1234567.cloudfront.com/*"
        expires = 999999
        policy = self.dist._custom_policy(url, expires=expires)
        policy = json.loads(policy)

        self.assertEqual(1, len(policy.keys()))
        statements = policy["Statement"]
        self.assertEqual(1, len(statements))
        statement = statements[0]
        resource = statement["Resource"]
        self.assertEqual(url, resource)
        condition = statement["Condition"]
        self.assertEqual(1, len(condition.keys()))
        date_less_than = condition["DateLessThan"]
        self.assertEqual(1, len(date_less_than.keys()))
        aws_epoch_time = date_less_than["AWS:EpochTime"]
        self.assertEqual(expires, aws_epoch_time)

    def test_custom_policy_valid_after(self):
        """
        Test that a custom policy can be created with a valid-after time and
        an arbitrary URL.
        """
        url = "http://1234567.cloudfront.com/*"
        valid_after = 999999
        policy = self.dist._custom_policy(url, valid_after=valid_after)
        policy = json.loads(policy)

        self.assertEqual(1, len(policy.keys()))
        statements = policy["Statement"]
        self.assertEqual(1, len(statements))
        statement = statements[0]
        resource = statement["Resource"]
        self.assertEqual(url, resource)
        condition = statement["Condition"]
        self.assertEqual(2, len(condition.keys()))
        date_less_than = condition["DateLessThan"]
        date_greater_than = condition["DateGreaterThan"]
        self.assertEqual(1, len(date_greater_than.keys()))
        aws_epoch_time = date_greater_than["AWS:EpochTime"]
        self.assertEqual(valid_after, aws_epoch_time)

    def test_custom_policy_ip_address(self):
        """
        Test that a custom policy can be created with an IP address and
        an arbitrary URL.
        """
        url = "http://1234567.cloudfront.com/*"
        ip_range = "192.168.0.1"
        policy = self.dist._custom_policy(url, ip_address=ip_range)
        policy = json.loads(policy)

        self.assertEqual(1, len(policy.keys()))
        statements = policy["Statement"]
        self.assertEqual(1, len(statements))
        statement = statements[0]
        resource = statement["Resource"]
        self.assertEqual(url, resource)
        condition = statement["Condition"]
        self.assertEqual(2, len(condition.keys()))
        ip_address = condition["IpAddress"]
        self.assertTrue("DateLessThan" in condition)
        self.assertEqual(1, len(ip_address.keys()))
        source_ip = ip_address["AWS:SourceIp"]
        self.assertEqual("%s/32" % ip_range, source_ip)

    def test_custom_policy_ip_range(self):
        """
        Test that a custom policy can be created with an IP address and
        an arbitrary URL.
        """
        url = "http://1234567.cloudfront.com/*"
        ip_range = "192.168.0.0/24"
        policy = self.dist._custom_policy(url, ip_address=ip_range)
        policy = json.loads(policy)

        self.assertEqual(1, len(policy.keys()))
        statements = policy["Statement"]
        self.assertEqual(1, len(statements))
        statement = statements[0]
        resource = statement["Resource"]
        self.assertEqual(url, resource)
        condition = statement["Condition"]
        self.assertEqual(2, len(condition.keys()))
        self.assertTrue("DateLessThan" in condition)
        ip_address = condition["IpAddress"]
        self.assertEqual(1, len(ip_address.keys()))
        source_ip = ip_address["AWS:SourceIp"]
        self.assertEqual(ip_range, source_ip)

    def test_custom_policy_all(self):
        """
        Test that a custom policy can be created with an IP address and
        an arbitrary URL.
        """
        url = "http://1234567.cloudfront.com/test.txt"
        expires = 999999
        valid_after = 111111
        ip_range = "192.168.0.0/24"
        policy = self.dist._custom_policy(url, expires=expires,
                                          valid_after=valid_after,
                                          ip_address=ip_range)
        policy = json.loads(policy)

        self.assertEqual(1, len(policy.keys()))
        statements = policy["Statement"]
        self.assertEqual(1, len(statements))
        statement = statements[0]
        resource = statement["Resource"]
        self.assertEqual(url, resource)
        condition = statement["Condition"]
        self.assertEqual(3, len(condition.keys()))
        #check expires condition
        date_less_than = condition["DateLessThan"]
        self.assertEqual(1, len(date_less_than.keys()))
        aws_epoch_time = date_less_than["AWS:EpochTime"]
        self.assertEqual(expires, aws_epoch_time)
        #check valid_after condition
        date_greater_than = condition["DateGreaterThan"]
        self.assertEqual(1, len(date_greater_than.keys()))
        aws_epoch_time = date_greater_than["AWS:EpochTime"]
        self.assertEqual(valid_after, aws_epoch_time)
        #check source ip address condition
        ip_address = condition["IpAddress"]
        self.assertEqual(1, len(ip_address.keys()))
        source_ip = ip_address["AWS:SourceIp"]
        self.assertEqual(ip_range, source_ip)

    def test_params_canned_policy(self):
        """
        Test the correct params are generated for a canned policy.
        """
        url = "http://d604721fxaaqy9.cloudfront.net/horizon.jpg?large=yes&license=yes"
        expire_time = 1258237200
        expected_sig = ("Nql641NHEUkUaXQHZINK1FZ~SYeUSoBJMxjdgqrzIdzV2gyE"
                        "XPDNv0pYdWJkflDKJ3xIu7lbwRpSkG98NBlgPi4ZJpRRnVX4"
                        "kXAJK6tdNx6FucDB7OVqzcxkxHsGFd8VCG1BkC-Afh9~lOCM"
                        "IYHIaiOB6~5jt9w2EOwi6sIIqrg_")
        signed_url_params = self.dist._create_signing_params(url, self.pk_id, expire_time, private_key_string=self.pk_str)
        self.assertEqual(3, len(signed_url_params))
        self.assertEqual(signed_url_params["Expires"], "1258237200")
        self.assertEqual(signed_url_params["Signature"], expected_sig)
        self.assertEqual(signed_url_params["Key-Pair-Id"], "PK123456789754")

    def test_canned_policy(self):
        """
        Generate signed url from the Example Canned Policy in Amazon's
        documentation.
        """
        url = "http://d604721fxaaqy9.cloudfront.net/horizon.jpg?large=yes&license=yes"
        expire_time = 1258237200
        expected_url = "http://d604721fxaaqy9.cloudfront.net/horizon.jpg?large=yes&license=yes&Expires=1258237200&Signature=Nql641NHEUkUaXQHZINK1FZ~SYeUSoBJMxjdgqrzIdzV2gyEXPDNv0pYdWJkflDKJ3xIu7lbwRpSkG98NBlgPi4ZJpRRnVX4kXAJK6tdNx6FucDB7OVqzcxkxHsGFd8VCG1BkC-Afh9~lOCMIYHIaiOB6~5jt9w2EOwi6sIIqrg_&Key-Pair-Id=PK123456789754"
        signed_url = self.dist.create_signed_url(
            url, self.pk_id, expire_time, private_key_string=self.pk_str)
        self.assertEqual(expected_url, signed_url)
示例#15
0
class CloudfrontSignedUrlsTest(unittest.TestCase):
    def setUp(self):
        self.pk_str = dedent("""
            -----BEGIN RSA PRIVATE KEY-----
            MIICXQIBAAKBgQDA7ki9gI/lRygIoOjV1yymgx6FYFlzJ+z1ATMaLo57nL57AavW
            hb68HYY8EA0GJU9xQdMVaHBogF3eiCWYXSUZCWM/+M5+ZcdQraRRScucmn6g4EvY
            2K4W2pxbqH8vmUikPxir41EeBPLjMOzKvbzzQy9e/zzIQVREKSp/7y1mywIDAQAB
            AoGABc7mp7XYHynuPZxChjWNJZIq+A73gm0ASDv6At7F8Vi9r0xUlQe/v0AQS3yc
            N8QlyR4XMbzMLYk3yjxFDXo4ZKQtOGzLGteCU2srANiLv26/imXA8FVidZftTAtL
            viWQZBVPTeYIA69ATUYPEq0a5u5wjGyUOij9OWyuy01mbPkCQQDluYoNpPOekQ0Z
            WrPgJ5rxc8f6zG37ZVoDBiexqtVShIF5W3xYuWhW5kYb0hliYfkq15cS7t9m95h3
            1QJf/xI/AkEA1v9l/WN1a1N3rOK4VGoCokx7kR2SyTMSbZgF9IWJNOugR/WZw7HT
            njipO3c9dy1Ms9pUKwUF46d7049ck8HwdQJARgrSKuLWXMyBH+/l1Dx/I4tXuAJI
            rlPyo+VmiOc7b5NzHptkSHEPfR9s1OK0VqjknclqCJ3Ig86OMEtEFBzjZQJBAKYz
            470hcPkaGk7tKYAgP48FvxRsnzeooptURW5E+M+PQ2W9iDPPOX9739+Xi02hGEWF
            B0IGbQoTRFdE4VVcPK0CQQCeS84lODlC0Y2BZv2JxW3Osv/WkUQ4dslfAQl1T303
            7uwwr7XTroMv8dIFQIPreoPhRKmd/SbJzbiKfS/4QDhU
            -----END RSA PRIVATE KEY-----
            """)
        self.pk_id = "PK123456789754"
        self.dist = Distribution()
        self.canned_policy = (
            '{"Statement":[{"Resource":'
            '"http://d604721fxaaqy9.cloudfront.net/horizon.jpg'
            '?large=yes&license=yes",'
            '"Condition":{"DateLessThan":{"AWS:EpochTime":1258237200}}}]}')
        self.custom_policy_1 = (
            '{ \n'
            '   "Statement": [{ \n'
            '      "Resource":"http://d604721fxaaqy9.cloudfront.net/training/*", \n'
            '      "Condition":{ \n'
            '         "IpAddress":{"AWS:SourceIp":"145.168.143.0/24"}, \n'
            '         "DateLessThan":{"AWS:EpochTime":1258237200}      \n'
            '      } \n'
            '   }] \n'
            '}\n')
        self.custom_policy_2 = (
            '{ \n'
            '   "Statement": [{ \n'
            '      "Resource":"http://*", \n'
            '      "Condition":{ \n'
            '         "IpAddress":{"AWS:SourceIp":"216.98.35.1/32"},\n'
            '         "DateGreaterThan":{"AWS:EpochTime":1241073790},\n'
            '         "DateLessThan":{"AWS:EpochTime":1255674716}\n'
            '      } \n'
            '   }] \n'
            '}\n')

    def test_encode_custom_policy_1(self):
        """
        Test base64 encoding custom policy 1 from Amazon's documentation.
        """
        expected = ("eyAKICAgIlN0YXRlbWVudCI6IFt7IAogICAgICAiUmVzb3VyY2Ui"
                    "OiJodHRwOi8vZDYwNDcyMWZ4YWFxeTkuY2xvdWRmcm9udC5uZXQv"
                    "dHJhaW5pbmcvKiIsIAogICAgICAiQ29uZGl0aW9uIjp7IAogICAg"
                    "ICAgICAiSXBBZGRyZXNzIjp7IkFXUzpTb3VyY2VJcCI6IjE0NS4x"
                    "NjguMTQzLjAvMjQifSwgCiAgICAgICAgICJEYXRlTGVzc1RoYW4i"
                    "OnsiQVdTOkVwb2NoVGltZSI6MTI1ODIzNzIwMH0gICAgICAKICAg"
                    "ICAgfSAKICAgfV0gCn0K")
        encoded = self.dist._url_base64_encode(self.custom_policy_1)
        self.assertEqual(expected, encoded)

    def test_encode_custom_policy_2(self):
        """
        Test base64 encoding custom policy 2 from Amazon's documentation.
        """
        expected = ("eyAKICAgIlN0YXRlbWVudCI6IFt7IAogICAgICAiUmVzb3VyY2Ui"
                    "OiJodHRwOi8vKiIsIAogICAgICAiQ29uZGl0aW9uIjp7IAogICAg"
                    "ICAgICAiSXBBZGRyZXNzIjp7IkFXUzpTb3VyY2VJcCI6IjIxNi45"
                    "OC4zNS4xLzMyIn0sCiAgICAgICAgICJEYXRlR3JlYXRlclRoYW4i"
                    "OnsiQVdTOkVwb2NoVGltZSI6MTI0MTA3Mzc5MH0sCiAgICAgICAg"
                    "ICJEYXRlTGVzc1RoYW4iOnsiQVdTOkVwb2NoVGltZSI6MTI1NTY3"
                    "NDcxNn0KICAgICAgfSAKICAgfV0gCn0K")
        encoded = self.dist._url_base64_encode(self.custom_policy_2)
        self.assertEqual(expected, encoded)

    def test_sign_canned_policy(self):
        """
        Test signing the canned policy from amazon's cloudfront documentation.
        """
        expected = ("Nql641NHEUkUaXQHZINK1FZ~SYeUSoBJMxjdgqrzIdzV2gyEXPDN"
                    "v0pYdWJkflDKJ3xIu7lbwRpSkG98NBlgPi4ZJpRRnVX4kXAJK6td"
                    "Nx6FucDB7OVqzcxkxHsGFd8VCG1BkC-Afh9~lOCMIYHIaiOB6~5j"
                    "t9w2EOwi6sIIqrg_")
        sig = self.dist._sign_string(self.canned_policy,
                                     private_key_string=self.pk_str)
        encoded_sig = self.dist._url_base64_encode(sig)
        self.assertEqual(expected, encoded_sig)

    def test_sign_canned_policy_unicode(self):
        """
        Test signing the canned policy from amazon's cloudfront documentation.
        """
        expected = ("Nql641NHEUkUaXQHZINK1FZ~SYeUSoBJMxjdgqrzIdzV2gyEXPDN"
                    "v0pYdWJkflDKJ3xIu7lbwRpSkG98NBlgPi4ZJpRRnVX4kXAJK6td"
                    "Nx6FucDB7OVqzcxkxHsGFd8VCG1BkC-Afh9~lOCMIYHIaiOB6~5j"
                    "t9w2EOwi6sIIqrg_")
        unicode_policy = unicode(self.canned_policy)
        sig = self.dist._sign_string(unicode_policy,
                                     private_key_string=self.pk_str)
        encoded_sig = self.dist._url_base64_encode(sig)
        self.assertEqual(expected, encoded_sig)

    def test_sign_custom_policy_1(self):
        """
        Test signing custom policy 1 from amazon's cloudfront documentation.
        """
        expected = ("cPFtRKvUfYNYmxek6ZNs6vgKEZP6G3Cb4cyVt~FjqbHOnMdxdT7e"
                    "T6pYmhHYzuDsFH4Jpsctke2Ux6PCXcKxUcTIm8SO4b29~1QvhMl-"
                    "CIojki3Hd3~Unxjw7Cpo1qRjtvrimW0DPZBZYHFZtiZXsaPt87yB"
                    "P9GWnTQoaVysMxQ_")
        sig = self.dist._sign_string(self.custom_policy_1,
                                     private_key_string=self.pk_str)
        encoded_sig = self.dist._url_base64_encode(sig)
        self.assertEqual(expected, encoded_sig)

    def test_sign_custom_policy_2(self):
        """
        Test signing custom policy 2 from amazon's cloudfront documentation.
        """
        expected = ("rc~5Qbbm8EJXjUTQ6Cn0LAxR72g1DOPrTmdtfbWVVgQNw0q~KHUA"
                    "mBa2Zv1Wjj8dDET4XSL~Myh44CLQdu4dOH~N9huH7QfPSR~O4tIO"
                    "S1WWcP~2JmtVPoQyLlEc8YHRCuN3nVNZJ0m4EZcXXNAS-0x6Zco2"
                    "SYx~hywTRxWR~5Q_")
        sig = self.dist._sign_string(self.custom_policy_2,
                                     private_key_string=self.pk_str)
        encoded_sig = self.dist._url_base64_encode(sig)
        self.assertEqual(expected, encoded_sig)

    def test_create_canned_policy(self):
        """
        Test that a canned policy is generated correctly.
        """
        url = "http://1234567.cloudfront.com/test_resource.mp3?dog=true"
        expires = 999999
        policy = self.dist._canned_policy(url, expires)
        policy = json.loads(policy)

        self.assertEqual(1, len(policy.keys()))
        statements = policy["Statement"]
        self.assertEqual(1, len(statements))
        statement = statements[0]
        resource = statement["Resource"]
        self.assertEqual(url, resource)
        condition = statement["Condition"]
        self.assertEqual(1, len(condition.keys()))
        date_less_than = condition["DateLessThan"]
        self.assertEqual(1, len(date_less_than.keys()))
        aws_epoch_time = date_less_than["AWS:EpochTime"]
        self.assertEqual(expires, aws_epoch_time)

    def test_custom_policy_expires_and_policy_url(self):
        """
        Test that a custom policy can be created with an expire time and an
        arbitrary URL.
        """
        url = "http://1234567.cloudfront.com/*"
        expires = 999999
        policy = self.dist._custom_policy(url, expires=expires)
        policy = json.loads(policy)

        self.assertEqual(1, len(policy.keys()))
        statements = policy["Statement"]
        self.assertEqual(1, len(statements))
        statement = statements[0]
        resource = statement["Resource"]
        self.assertEqual(url, resource)
        condition = statement["Condition"]
        self.assertEqual(1, len(condition.keys()))
        date_less_than = condition["DateLessThan"]
        self.assertEqual(1, len(date_less_than.keys()))
        aws_epoch_time = date_less_than["AWS:EpochTime"]
        self.assertEqual(expires, aws_epoch_time)

    def test_custom_policy_valid_after(self):
        """
        Test that a custom policy can be created with a valid-after time and
        an arbitrary URL.
        """
        url = "http://1234567.cloudfront.com/*"
        valid_after = 999999
        policy = self.dist._custom_policy(url, valid_after=valid_after)
        policy = json.loads(policy)

        self.assertEqual(1, len(policy.keys()))
        statements = policy["Statement"]
        self.assertEqual(1, len(statements))
        statement = statements[0]
        resource = statement["Resource"]
        self.assertEqual(url, resource)
        condition = statement["Condition"]
        self.assertEqual(1, len(condition.keys()))
        date_greater_than = condition["DateGreaterThan"]
        self.assertEqual(1, len(date_greater_than.keys()))
        aws_epoch_time = date_greater_than["AWS:EpochTime"]
        self.assertEqual(valid_after, aws_epoch_time)

    def test_custom_policy_ip_address(self):
        """
        Test that a custom policy can be created with an IP address and
        an arbitrary URL.
        """
        url = "http://1234567.cloudfront.com/*"
        ip_range = "192.168.0.1"
        policy = self.dist._custom_policy(url, ip_address=ip_range)
        policy = json.loads(policy)

        self.assertEqual(1, len(policy.keys()))
        statements = policy["Statement"]
        self.assertEqual(1, len(statements))
        statement = statements[0]
        resource = statement["Resource"]
        self.assertEqual(url, resource)
        condition = statement["Condition"]
        self.assertEqual(1, len(condition.keys()))
        ip_address = condition["IpAddress"]
        self.assertEqual(1, len(ip_address.keys()))
        source_ip = ip_address["AWS:SourceIp"]
        self.assertEqual("%s/32" % ip_range, source_ip)

    def test_custom_policy_ip_range(self):
        """
        Test that a custom policy can be created with an IP address and
        an arbitrary URL.
        """
        url = "http://1234567.cloudfront.com/*"
        ip_range = "192.168.0.0/24"
        policy = self.dist._custom_policy(url, ip_address=ip_range)
        policy = json.loads(policy)

        self.assertEqual(1, len(policy.keys()))
        statements = policy["Statement"]
        self.assertEqual(1, len(statements))
        statement = statements[0]
        resource = statement["Resource"]
        self.assertEqual(url, resource)
        condition = statement["Condition"]
        self.assertEqual(1, len(condition.keys()))
        ip_address = condition["IpAddress"]
        self.assertEqual(1, len(ip_address.keys()))
        source_ip = ip_address["AWS:SourceIp"]
        self.assertEqual(ip_range, source_ip)

    def test_custom_policy_all(self):
        """
        Test that a custom policy can be created with an IP address and
        an arbitrary URL.
        """
        url = "http://1234567.cloudfront.com/test.txt"
        expires = 999999
        valid_after = 111111
        ip_range = "192.168.0.0/24"
        policy = self.dist._custom_policy(url,
                                          expires=expires,
                                          valid_after=valid_after,
                                          ip_address=ip_range)
        policy = json.loads(policy)

        self.assertEqual(1, len(policy.keys()))
        statements = policy["Statement"]
        self.assertEqual(1, len(statements))
        statement = statements[0]
        resource = statement["Resource"]
        self.assertEqual(url, resource)
        condition = statement["Condition"]
        self.assertEqual(3, len(condition.keys()))
        #check expires condition
        date_less_than = condition["DateLessThan"]
        self.assertEqual(1, len(date_less_than.keys()))
        aws_epoch_time = date_less_than["AWS:EpochTime"]
        self.assertEqual(expires, aws_epoch_time)
        #check valid_after condition
        date_greater_than = condition["DateGreaterThan"]
        self.assertEqual(1, len(date_greater_than.keys()))
        aws_epoch_time = date_greater_than["AWS:EpochTime"]
        self.assertEqual(valid_after, aws_epoch_time)
        #check source ip address condition
        ip_address = condition["IpAddress"]
        self.assertEqual(1, len(ip_address.keys()))
        source_ip = ip_address["AWS:SourceIp"]
        self.assertEqual(ip_range, source_ip)

    def test_params_canned_policy(self):
        """
        Test the correct params are generated for a canned policy.
        """
        url = "http://d604721fxaaqy9.cloudfront.net/horizon.jpg?large=yes&license=yes"
        expire_time = 1258237200
        expected_sig = ("Nql641NHEUkUaXQHZINK1FZ~SYeUSoBJMxjdgqrzIdzV2gyE"
                        "XPDNv0pYdWJkflDKJ3xIu7lbwRpSkG98NBlgPi4ZJpRRnVX4"
                        "kXAJK6tdNx6FucDB7OVqzcxkxHsGFd8VCG1BkC-Afh9~lOCM"
                        "IYHIaiOB6~5jt9w2EOwi6sIIqrg_")
        signed_url_params = self.dist._create_signing_params(
            url, self.pk_id, expire_time, private_key_string=self.pk_str)
        self.assertEqual(3, len(signed_url_params))
        self.assertEqual(signed_url_params["Expires"], "1258237200")
        self.assertEqual(signed_url_params["Signature"], expected_sig)
        self.assertEqual(signed_url_params["Key-Pair-Id"], "PK123456789754")

    def test_canned_policy(self):
        """
        Generate signed url from the Example Canned Policy in Amazon's
        documentation.
        """
        url = "http://d604721fxaaqy9.cloudfront.net/horizon.jpg?large=yes&license=yes"
        expire_time = 1258237200
        expected_url = "http://d604721fxaaqy9.cloudfront.net/horizon.jpg?large=yes&license=yes&Expires=1258237200&Signature=Nql641NHEUkUaXQHZINK1FZ~SYeUSoBJMxjdgqrzIdzV2gyEXPDNv0pYdWJkflDKJ3xIu7lbwRpSkG98NBlgPi4ZJpRRnVX4kXAJK6tdNx6FucDB7OVqzcxkxHsGFd8VCG1BkC-Afh9~lOCMIYHIaiOB6~5jt9w2EOwi6sIIqrg_&Key-Pair-Id=PK123456789754"
        signed_url = self.dist.create_signed_url(
            url, self.pk_id, expire_time, private_key_string=self.pk_str)
        self.assertEqual(expected_url, signed_url)