Пример #1
0
class TestCloudfrontSigner(BaseSignerTest):
    def setUp(self):
        super(TestCloudfrontSigner, self).setUp()
        self.signer = CloudFrontSigner("MY_KEY_ID", lambda message: b'signed')
        # It helps but the long string diff will still be slightly different on
        # Python 2.6/2.7/3.x. We won't soly rely on that anyway, so it's fine.
        self.maxDiff = None

    def test_build_canned_policy(self):
        policy = self.signer.build_policy('foo', datetime.datetime(2016, 1, 1))
        expected = (
            '{"Statement":[{"Resource":"foo",'
            '"Condition":{"DateLessThan":{"AWS:EpochTime":1451606400}}}]}')
        self.assertEqual(json.loads(policy), json.loads(expected))
        self.assertEqual(policy, expected)  # This is to ensure the right order

    def test_build_custom_policy(self):
        policy = self.signer.build_policy(
            'foo', datetime.datetime(2016, 1, 1),
            date_greater_than=datetime.datetime(2015, 12, 1),
            ip_address='12.34.56.78/9')
        expected = {
            "Statement": [{
                "Resource": "foo",
                "Condition": {
                    "DateGreaterThan": {"AWS:EpochTime": 1448928000},
                    "DateLessThan": {"AWS:EpochTime": 1451606400},
                    "IpAddress": {"AWS:SourceIp": "12.34.56.78/9"}
                },
            }]
        }
        self.assertEqual(json.loads(policy), expected)

    def test_generate_presign_url_with_expire_time(self):
        signed_url = self.signer.generate_presigned_url(
            'http://test.com/foo.txt',
            date_less_than=datetime.datetime(2016, 1, 1))
        expected = (
            'http://test.com/foo.txt?Expires=1451606400&Signature=c2lnbmVk'
            '&Key-Pair-Id=MY_KEY_ID')
        assert_url_equal(signed_url, expected)

    def test_generate_presign_url_with_custom_policy(self):
        policy = self.signer.build_policy(
            'foo', datetime.datetime(2016, 1, 1),
            date_greater_than=datetime.datetime(2015, 12, 1),
            ip_address='12.34.56.78/9')
        signed_url = self.signer.generate_presigned_url(
            'http://test.com/index.html?foo=bar', policy=policy)
        expected = (
            'http://test.com/index.html?foo=bar'
            '&Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiZm9vIiwiQ29uZ'
            'Gl0aW9uIjp7IkRhdGVMZXNzVGhhbiI6eyJBV1M6RXBvY2hUaW1lIj'
            'oxNDUxNjA2NDAwfSwiSXBBZGRyZXNzIjp7IkFXUzpTb3VyY2VJcCI'
            '6IjEyLjM0LjU2Ljc4LzkifSwiRGF0ZUdyZWF0ZXJUaGFuIjp7IkFX'
            'UzpFcG9jaFRpbWUiOjE0NDg5MjgwMDB9fX1dfQ__'
            '&Signature=c2lnbmVk&Key-Pair-Id=MY_KEY_ID')
        assert_url_equal(signed_url, expected)
Пример #2
0
class TestCloudfrontSigner(BaseSignerTest):
    def setUp(self):
        super(TestCloudfrontSigner, self).setUp()
        self.signer = CloudFrontSigner("MY_KEY_ID", lambda message: b'signed')
        # It helps but the long string diff will still be slightly different on
        # Python 2.6/2.7/3.x. We won't soly rely on that anyway, so it's fine.
        self.maxDiff = None

    def test_build_canned_policy(self):
        policy = self.signer.build_policy('foo', datetime.datetime(2016, 1, 1))
        expected = (
            '{"Statement":[{"Resource":"foo",'
            '"Condition":{"DateLessThan":{"AWS:EpochTime":1451606400}}}]}')
        self.assertEqual(json.loads(policy), json.loads(expected))
        self.assertEqual(policy, expected)  # This is to ensure the right order

    def test_build_custom_policy(self):
        policy = self.signer.build_policy(
            'foo', datetime.datetime(2016, 1, 1),
            date_greater_than=datetime.datetime(2015, 12, 1),
            ip_address='12.34.56.78/9')
        expected = {
            "Statement": [{
                "Resource": "foo",
                "Condition": {
                    "DateGreaterThan": {"AWS:EpochTime": 1448928000},
                    "DateLessThan": {"AWS:EpochTime": 1451606400},
                    "IpAddress": {"AWS:SourceIp": "12.34.56.78/9"}
                },
            }]
        }
        self.assertEqual(json.loads(policy), expected)

    def test_generate_presign_url_with_expire_time(self):
        signed_url = self.signer.generate_presigned_url(
            'http://test.com/foo.txt',
            date_less_than=datetime.datetime(2016, 1, 1))
        expected = (
            'http://test.com/foo.txt?Expires=1451606400&Signature=c2lnbmVk'
            '&Key-Pair-Id=MY_KEY_ID')
        self.assert_url_equal(signed_url, expected)

    def test_generate_presign_url_with_custom_policy(self):
        policy = self.signer.build_policy(
            'foo', datetime.datetime(2016, 1, 1),
            date_greater_than=datetime.datetime(2015, 12, 1),
            ip_address='12.34.56.78/9')
        signed_url = self.signer.generate_presigned_url(
            'http://test.com/index.html?foo=bar', policy=policy)
        expected = (
            'http://test.com/index.html?foo=bar'
            '&Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiZm9vIiwiQ29uZ'
            'Gl0aW9uIjp7IkRhdGVMZXNzVGhhbiI6eyJBV1M6RXBvY2hUaW1lIj'
            'oxNDUxNjA2NDAwfSwiSXBBZGRyZXNzIjp7IkFXUzpTb3VyY2VJcCI'
            '6IjEyLjM0LjU2Ljc4LzkifSwiRGF0ZUdyZWF0ZXJUaGFuIjp7IkFX'
            'UzpFcG9jaFRpbWUiOjE0NDg5MjgwMDB9fX1dfQ__'
            '&Signature=c2lnbmVk&Key-Pair-Id=MY_KEY_ID')
        self.assert_url_equal(signed_url, expected)
Пример #3
0
    def get_urls(self, obj):
        """Urls of the video for each type of encoding and in each resolution.

        Parameters
        ----------
        obj : Type[models.Video]
            The video that we want to serialize

        Returns
        -------
        Dictionary or None
            A dictionary of all urls for:
                - mp4 encodings of the video in each resolution
                - jpeg thumbnails of the video in each resolution
            None if the video is still not uploaded to S3 with success

        """
        if obj.uploaded_on is None or obj.state != Video.READY:
            return None

        urls = {"mp4": {}, "thumbnails": {}}
        base = "{cloudfront:s}/{playlist!s}/{video!s}".format(
            cloudfront=settings.CLOUDFRONT_URL,
            playlist=obj.playlist.id,
            video=obj.id)

        date_less_than = timezone.now() + timedelta(
            seconds=settings.CLOUDFRONT_SIGNED_URLS_VALIDITY)
        for resolution in settings.VIDEO_RESOLUTIONS:
            # MP4
            mp4_url = "{base:s}/videos/{stamp:s}_{resolution:d}.mp4".format(
                base=base, stamp=obj.active_stamp, resolution=resolution)

            # Thumbnails
            thumbnail_url = "{base:s}/thumbnails/{stamp:s}_{resolution:d}.0000000.jpg".format(
                base=base, stamp=obj.active_stamp, resolution=resolution)

            # Sign urls if the functionality is activated
            if settings.CLOUDFRONT_SIGNED_URLS_ACTIVE:
                cloudfront_signer = CloudFrontSigner(
                    settings.CLOUDFRONT_ACCESS_KEY_ID,
                    cloudfront_utils.rsa_signer)
                mp4_url = cloudfront_signer.generate_presigned_url(
                    mp4_url, date_less_than=date_less_than)
                thumbnail_url = cloudfront_signer.generate_presigned_url(
                    thumbnail_url, date_less_than=date_less_than)

            urls["mp4"][resolution] = mp4_url
            urls["thumbnails"][resolution] = thumbnail_url

        return json.dumps(urls)
Пример #4
0
def get_cdn_presigned(file_name):
    print 'Getting presigned url for {} from CDN - {}'.format(file_name, cdn_name)
    cdn_key_name, cdn_key_data = _get_cdn_data()
    print 'Got key name {} data {}'.format(cdn_key_name, cdn_key_data)

    def _rsa_signer(message):
        private_key = serialization.load_pem_private_key(cdn_key_data, password=None, backend=default_backend())
        signer = private_key.signer(padding.PKCS1v15(), hashes.SHA1())
        signer.update(message)
        return signer.finalize()

    if not hasattr(get_cdn_presigned, 'cdn_domain'):
        client = boto3.client('cloudfront')
        response = client.get_distribution(Id=cdn_name)
        print 'CDN {} got distribution info {}'.format(cdn_name, response)

        get_cdn_presigned.cdn_domain = response.get('Distribution',{}).get('DomainName')

    if not get_cdn_presigned.cdn_domain:
        print 'No domain on cdn {} to get signed url from'.format(cdn_name)

    current_time = datetime.utcnow()
    expire_time = current_time + timedelta(seconds=_cloudfront_url_duration())

    cloudfront_signer = CloudFrontSigner(cdn_key_name, _rsa_signer)

    url = 'https://' + get_cdn_presigned.cdn_domain + '/' + file_name
    print 'Retrieving signed url for {}'.format(url)

    signed_url = cloudfront_signer.generate_presigned_url(url, date_less_than=expire_time)

    print 'Got signed url of {}'.format(signed_url)
    return signed_url
Пример #5
0
def lambda_handler(event, context):
    if 'recordingPath' not in event or not event['recordingPath'] or event['recordingPath'] == 'null':
        logger.info("No recordingPath in event; returning.")
        return None
    # retrieve secrets
    logger.info("Retrieving cloudfront credentials")
    session = boto3.session.Session()
    client = session.client(service_name='secretsmanager')
    sf_credentials_secrets_manager_arn = get_arg(os.environ,
            'SF_CREDENTIALS_SECRETS_MANAGER_ARN')
    secrets = json.loads(client.get_secret_value(SecretId=sf_credentials_secrets_manager_arn)['SecretString'])
    private_key = secrets['CloudFrontPrivateKey']
    access_key_id = secrets['CloudFrontAccessKeyID']
    logger.info("Cloudfront credentials retrieved")

    # construct url to audio recording
    recordingPath = event['recordingPath'] # need to remove bucket name, connect dir from path
    if("/connect/" in recordingPath):
        recordingPath = "connect/" + recordingPath.split("/connect/", 1)[1]
    elif("/Analysis/" in recordingPath):
        recordingPath = "Analysis/" + recordingPath.split("/Analysis/", 1)[1]
    cloudfront_domain = get_arg(os.environ, 'CLOUDFRONT_DISTRIBUTION_DOMAIN_NAME')
    url = 'https://' + cloudfront_domain + '/' + recordingPath
    logger.info('Unsigned audio recording url: %s' % url)

    # sign url
    expire_date = datetime.datetime.utcnow() + datetime.timedelta(minutes=60)
    cloudfront_signer = CloudFrontSigner(access_key_id, rsa_signer(private_key))
    signed_url = cloudfront_signer.generate_presigned_url(
        url, date_less_than=expire_date)
    logger.info('Signed audio recording url: %s' % signed_url)
    return signed_url
Пример #6
0
    def get_url(self, obj):
        """Url of the Document.

        Parameters
        ----------
        obj : Type[models.Document]
            The document that we want to serialize

        Returns
        -------
        String or None
            the url to fetch the document on CloudFront
            None if the document is still not uploaded to S3 with success

        """
        if obj.uploaded_on is None:
            return None

        url = (
            f"{settings.AWS_S3_URL_PROTOCOL}://{settings.CLOUDFRONT_DOMAIN}/{obj.pk}/document/"
            f"{time_utils.to_timestamp(obj.uploaded_on)}{self._get_extension_string(obj)}?response"
            f"-content-disposition={quote_plus('attachment; filename=' + self.get_filename(obj))}"
        )

        # Sign the document urls only if the functionality is activated
        if settings.CLOUDFRONT_SIGNED_URLS_ACTIVE:
            date_less_than = timezone.now() + timedelta(
                seconds=settings.CLOUDFRONT_SIGNED_URLS_VALIDITY)
            cloudfront_signer = CloudFrontSigner(
                settings.CLOUDFRONT_SIGNED_PUBLIC_KEY_ID,
                cloudfront_utils.rsa_signer)
            url = cloudfront_signer.generate_presigned_url(
                url, date_less_than=date_less_than)

        return url
Пример #7
0
def create_cloudfront_signed_url(
        object_name: str, expiration_date: datetime) -> str:
    """Generate a cloudfront signed URL to share an s3 object

    Arguments:
        object_name {str} -- Required. s3 object to share
        expiration_date {datetime} -- Required. Expiration datetime object of
                                        some future datetime

    Returns:
        str -- cloudfront signed URL
    """
    key_id = environ.get('AWS_CLOUDFRONT_USER_ACCESS_ID')
    url = '{cloudfront_domain}/{object_name}'.format(
        cloudfront_domain=environ.get('AWS_CLOUDFRONT_DOMAIN'),
        object_name=object_name
    )

    cloudfront_signer = CloudFrontSigner(key_id, rsa_signer)

    # Create a signed url that will be valid until the specfic expiry date
    # provided using a canned policy.
    signed_url = cloudfront_signer.generate_presigned_url(
        url, date_less_than=expiration_date)
    return signed_url
Пример #8
0
def get_presigned_url() -> str:
    """
    Two query parameters are required:
    * url: Base URL for the file
    * resource: URL or stream name of the file

    This code uses botocore.signers.CloudFrontSigner to generate presigned URLs
    using a custom policy.

    See Amazon CloudFront documentation for more details on the signing process:
    https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-custom-policy.html

    Example using cURL:
    curl 'http://localhost:8000/presigned-url?url=https://www.example.com/images/image.jpg&resource=https://www.example.com/images/*'

    :return str: The signed URL
    """
    config_file_path = os.path.join(os.path.dirname(__file__), 'config.json')
    with open(config_file_path) as config_file:
        config = json.load(config_file)
    private_key_id = config['private_key_id']

    request = app.current_request
    url = request.query_params['url']
    resource = request.query_params['resource']

    expire_date = datetime.now() + timedelta(minutes=+5)

    cf_signer = CloudFrontSigner(private_key_id, rsa_signer)
    cf_policy = cf_signer.build_policy(resource=resource,
                                       date_less_than=expire_date)
    presigned_url = cf_signer.generate_presigned_url(url, policy=cf_policy)

    return presigned_url
Пример #9
0
 def _run_main(self, args, parsed_globals):
     signer = CloudFrontSigner(
         args.key_pair_id, RSASigner(args.private_key).sign)
     date_less_than = parse_to_aware_datetime(args.date_less_than)
     date_greater_than = args.date_greater_than
     if date_greater_than is not None:
         date_greater_than = parse_to_aware_datetime(date_greater_than)
     if date_greater_than is not None or args.ip_address is not None:
         policy = signer.build_policy(
             args.url, date_less_than, date_greater_than=date_greater_than,
             ip_address=args.ip_address)
         sys.stdout.write(signer.generate_presigned_url(
             args.url, policy=policy))
     else:
         sys.stdout.write(signer.generate_presigned_url(
             args.url, date_less_than=date_less_than))
     return 0
Пример #10
0
def main():
    key_id = 'KF4ZWB2OGIN4A'
    url = 'https://d1za37rregmkz7.cloudfront.net/app1/var3.json'
    current_time = datetime.datetime.utcnow()
    expire_date = current_time + datetime.timedelta(minutes=2)
    cloudfront_signer = CloudFrontSigner(key_id, rsa_signer)
    signed_url = cloudfront_signer.generate_presigned_url(
        url, date_less_than=expire_date)
    print(signed_url)
Пример #11
0
def get_image(event, context):
    event_string = json.dumps(event)
    print(event_string)
    image_name = event["queryStringParameters"]["image_name"]
    user_name = event["queryStringParameters"]["user_name"]
    if user_name != 'king-kong':
        return {
            "statusCode": 404,
            "body": json.dumps({
                "message": "You are trying to access premium property images, you need to subscribe for premium membership.",
            }),
              "headers":{ 'Access-Control-Allow-Origin' : '*' }
        }

    bucket_name = getImagesBucketName()
    print("Bucket name is {0}".format(bucket_name))
    
    try:
        #key_id = 'xxxxxxxxxxxxxxxxxxx'
        # reading value from parameter store
        key_id = getCloudFrontKeyId()
        print("Cloudfront Access key id is {0}".format(key_id))

        # reading value from parameter store    
        cf_distribution_name = getCloudFrontDistributionName()
        print("Cloudfront distribution name is {0}".format(cf_distribution_name))

        url = "https://" + cf_distribution_name + "/" + image_name
        expire_date = datetime.datetime(2020, 10, 10)
        cloudfront_signer = CloudFrontSigner(key_id, rsa_signer)

        # Create a signed url that will be valid until the specfic expiry date
        # provided using a canned policy.
        signed_url = cloudfront_signer.generate_presigned_url(url, date_less_than=expire_date)
        print("Pre-signed URL is {0}".format(signed_url))
                
        s3 = boto3.resource('s3')
        object = s3.Object(bucket_name,image_name).load()
        msg = "Image with name : " + image_name + " exists"
        return {
            "statusCode": 200,
            "body": json.dumps({
                "message": signed_url,
            }),
            "headers":{ 'Access-Control-Allow-Origin' : '*' }
        }
    except BaseException as error:
        print("*** Failure to retrieve Car Image - Please check your request ***")
        return {
            "statusCode": 404,
            "body": json.dumps({
                "message": str(error),
            }),
            "headers":{ 'Access-Control-Allow-Origin' : '*' }
        }
        
Пример #12
0
def sign_url(url, expiry, key_id):

    cloudfront_signer = CloudFrontSigner(key_id, rsa_signer)

    # Create a signed url that will be valid until the specfic expiry date
    # provided using a canned policy.
    signed_url = cloudfront_signer.generate_presigned_url(
        url=url, date_less_than=expiry)
    
    return signed_url
Пример #13
0
class CloudFrontS3Storage(S3Storage):
    """ Storage backend that uses S3 and CloudFront """
    def __init__(self,
                 request=None,
                 domain=None,
                 crypto_pk=None,
                 key_id=None,
                 **kwargs):
        super(CloudFrontS3Storage, self).__init__(request, **kwargs)
        self.domain = domain
        self.crypto_pk = crypto_pk
        self.key_id = key_id

        self.cf_signer = None
        if key_id is not None:
            self.cf_signer = CloudFrontSigner(self.key_id, self._rsa_signer)

        self.client = boto3.client("cloudfront")

    @classmethod
    def configure(cls, settings):
        kwargs = super(CloudFrontS3Storage, cls).configure(settings)
        kwargs["domain"] = settings["storage.cloud_front_domain"]
        kwargs["key_id"] = settings.get("storage.cloud_front_key_id")
        private_key = settings.get("storage.cloud_front_key_string")
        if private_key is None:
            key_file = settings.get("storage.cloud_front_key_file")
            if key_file:
                with open(key_file, "rb") as ifile:
                    private_key = ifile.read()
        else:
            private_key = private_key.encode("utf-8")
        crypto_pk = serialization.load_pem_private_key(
            private_key, password=None, backend=default_backend())
        kwargs["crypto_pk"] = crypto_pk

        return kwargs

    def _rsa_signer(self, message):
        """ Generate a RSA signature for a message """
        return self.crypto_pk.sign(message, padding.PKCS1v15(), hashes.SHA1())

    def _generate_url(self, package):
        """ Get the fully-qualified CloudFront path for a package """
        path = self.get_path(package)
        url = self.domain + "/" + quote(path)

        # No key id, no signer, so we don't have to sign the URL
        if self.cf_signer is None:
            return url

        # To sign with a canned policy:
        expires = utcnow() + timedelta(seconds=self.expire_after)
        return self.cf_signer.generate_presigned_url(url,
                                                     date_less_than=expires)
Пример #14
0
class CloudFrontS3Storage(S3Storage):
    """ Storage backend that uses S3 and CloudFront """
    def __init__(self,
                 request=None,
                 domain=None,
                 private_key=None,
                 key_id=None,
                 **kwargs):
        super(CloudFrontS3Storage, self).__init__(request, **kwargs)
        self.domain = domain
        self.private_key = private_key
        self.key_id = key_id
        self.private_key = private_key

        self.cf_signer = None
        if key_id is not None:
            self.cf_signer = CloudFrontSigner(self.key_id, self._rsa_signer)

        self.client = boto3.client('cloudfront')

    @classmethod
    def configure(cls, settings):
        kwargs = super(CloudFrontS3Storage, cls).configure(settings)
        kwargs['domain'] = settings['storage.cloud_front_domain']
        kwargs['key_id'] = settings.get('storage.cloud_front_key_id')
        private_key = settings.get('storage.cloud_front_key_string')
        if private_key is None:
            key_file = settings.get('storage.cloud_front_key_file')
            if key_file:
                with open(key_file, 'r') as ifile:
                    private_key = ifile.read()
        kwargs['private_key'] = private_key

        return kwargs

    def _rsa_signer(self, message):
        """ Generate a RSA signature for a message """
        return rsa.sign(message,
                        rsa.PrivateKey.load_pkcs1(
                            self.private_key.encode('utf8')),
                        'SHA-1')  # CloudFront requires SHA-1 hash

    def _generate_url(self, package):
        """ Get the fully-qualified CloudFront path for a package """
        path = self.get_path(package)
        url = self.domain + '/' + quote(path)

        # No key id, no signer, so we don't have to sign the URL
        if self.cf_signer is None:
            return url

        # To sign with a canned policy:
        expires = datetime.utcnow() + timedelta(seconds=self.expire_after)
        return self.cf_signer.generate_presigned_url(url,
                                                     date_less_than=expires)
Пример #15
0
class CloudFrontS3Storage(S3Storage):

    """ Storage backend that uses S3 and CloudFront """

    def __init__(
        self, request=None, domain=None, crypto_pk=None, key_id=None, **kwargs
    ):
        super(CloudFrontS3Storage, self).__init__(request, **kwargs)
        self.domain = domain
        self.crypto_pk = crypto_pk
        self.key_id = key_id

        self.cf_signer = None
        if key_id is not None:
            self.cf_signer = CloudFrontSigner(self.key_id, self._rsa_signer)

        self.client = boto3.client("cloudfront")

    @classmethod
    def configure(cls, settings):
        kwargs = super(CloudFrontS3Storage, cls).configure(settings)
        kwargs["domain"] = settings["storage.cloud_front_domain"]
        kwargs["key_id"] = settings.get("storage.cloud_front_key_id")
        private_key = settings.get("storage.cloud_front_key_string")
        if private_key is None:
            key_file = settings.get("storage.cloud_front_key_file")
            if key_file:
                with open(key_file, "rb") as ifile:
                    private_key = ifile.read()
        else:
            private_key = private_key.encode("utf-8")
        crypto_pk = serialization.load_pem_private_key(
            private_key, password=None, backend=default_backend()
        )
        kwargs["crypto_pk"] = crypto_pk

        return kwargs

    def _rsa_signer(self, message):
        """ Generate a RSA signature for a message """
        return self.crypto_pk.sign(message, padding.PKCS1v15(), hashes.SHA1())

    def _generate_url(self, package):
        """ Get the fully-qualified CloudFront path for a package """
        path = self.get_path(package)
        url = self.domain + "/" + quote(path)

        # No key id, no signer, so we don't have to sign the URL
        if self.cf_signer is None:
            return url

        # To sign with a canned policy:
        expires = datetime.utcnow() + timedelta(seconds=self.expire_after)
        return self.cf_signer.generate_presigned_url(url, date_less_than=expires)
Пример #16
0
    def get_urls(self, obj):
        """urls for each media's pages."""

        if obj.uploaded_on is None or obj.nb_pages is None:
            return None

        urls = {}

        stamp = time_utils.to_timestamp(obj.uploaded_on)
        base = (
            f"{settings.AWS_S3_URL_PROTOCOL}://{settings.CLOUDFRONT_DOMAIN}/"
            f"{obj.video.pk}/sharedlivemedia/{obj.pk}/{stamp}")

        cloudfront_signer = None
        if settings.CLOUDFRONT_SIGNED_URLS_ACTIVE:
            date_less_than = timezone.now() + timedelta(
                seconds=settings.CLOUDFRONT_SIGNED_URLS_VALIDITY)
            cloudfront_signer = CloudFrontSigner(
                settings.CLOUDFRONT_ACCESS_KEY_ID, cloudfront_utils.rsa_signer)

        pages = {}
        for page_number in range(1, obj.nb_pages + 1):
            url = f"{base:s}_{page_number:d}.svg"
            if cloudfront_signer:
                url = cloudfront_signer.generate_presigned_url(
                    url, date_less_than=date_less_than)
            pages[page_number] = url

        urls["pages"] = pages

        # Downloadable link can be generated only when cloudfront request is signed
        if (self.context.get("is_admin")
                or obj.show_download) and cloudfront_signer:
            extension = f".{obj.extension}" if obj.extension else ""
            urls["media"] = cloudfront_signer.generate_presigned_url(
                f"{base}/{stamp}/{stamp}{extension}?response-content-disposition="
                f"{quote_plus('attachment; filename=' + self.get_filename(obj))}",
                date_less_than=date_less_than,
            )

        return urls
Пример #17
0
def get_cdn_presigned_url(url, expiration):
    
    PUBLIC_KEY_ID = config('CF_SIGNER_PUBLIC_KEY')

    cloudront_signer = CloudFrontSigner(PUBLIC_KEY_ID, rsa_signer)

    signed_url = cloudront_signer.generate_presigned_url(
        url,
        date_less_than=expiration
    )

    return signed_url
Пример #18
0
    def create_cloudfront_signed_url(object_name, expiration_date):
        from app import app

        key_id = app.config['CF_ID']
        url = app.config['CF_DOMAIN'] + object_name

        cloudfront_signer = CloudFrontSigner(key_id,
                                             ImageStorageService.rsa_signer)
        signed_url = cloudfront_signer.generate_presigned_url(
            url, date_less_than=expiration_date)

        return signed_url
Пример #19
0
def signed_cf_url(bucket, key, expires_in_days=7):
    policy = f"""{{
       "Statement": [
          {{
             "Resource":"https://{bucket}/{key}",
             "Condition":{{
                "DateLessThan":{{"AWS:EpochTime":
                {int((datetime.datetime.today() + datetime.timedelta(days=expires_in_days)).timestamp())}}}
             }}
          }}
       ]
    }}"""
    cloudfront_signer = CloudFrontSigner(settings.CF_ACCESS_KEY, rsa_signer)
    return cloudfront_signer.generate_presigned_url(f'https://{bucket}/{key}', policy=policy)
Пример #20
0
    def generate_presigned_cloudfront_url(self, url, expire_time, keypair_id,
                                          private_key_string):
        # From https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudfront.html#generate-a-signed-url-for-amazon-cloudfront
        def rsa_signer(message):
            private_key = serialization.load_pem_private_key(
                private_key_string, password=None, backend=default_backend())
            return private_key.sign(message, padding.PKCS1v15(), hashes.SHA1())

        cloudfront_signer = CloudFrontSigner(keypair_id, rsa_signer)

        # Create a signed url that will be valid until the specfic expiry date
        # provided using a canned policy.
        return cloudfront_signer.generate_presigned_url(
            url, date_less_than=expire_time)
Пример #21
0
def main():
    sess = session.get_session()
    session.profile = SYSOP
    session.region = REGION

    # JST, not use timezone
    expire = datetime.now() + timedelta(minutes=EXPIRE) - timedelta(hours=9)

    cloudfront_signer = CloudFrontSigner(KEY_ID, get_rsa_signer)

    signed_url = cloudfront_signer.generate_presigned_url(
            URL,
            date_less_than=expire,
    )

    print(signed_url)
Пример #22
0
def get_signed_url(url, config_map):
    """ Convenience function for getting cloudfront signed URL given a saved URL

    cjshaw, Jan 7, 2015

    Follows:
        http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/
            private-content-creating-signed-url-canned-policy.html#private-
            content-creating-signed-url-canned-policy-procedure
        http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/
            PrivateContent.html
        http://boto.readthedocs.org/en/latest/ref/cloudfront.html

    May 25, 2017: Switch to boto3

    """

    # From https://stackoverflow.com/a/34322915
    def rsa_signer(message):
        private_key = open(config_map['cloudfront_private_key_file'],
                           'r').read()
        return rsa.sign(message,
                        rsa.PrivateKey.load_pkcs1(private_key.encode('utf8')),
                        'SHA-1')  # CloudFront requires SHA-1 hash

    if any(config_map[key] == '' for key in [
            's3_bucket', 'cloudfront_distro', 'cloudfront_private_key_file',
            'cloudfront_keypair_id'
    ]):
        # This is a test configuration
        return 'You are missing S3 and CF configs: https:///?Expires=X&Signature=X&Key-Pair-Id='

    expires = datetime.datetime.utcnow() + datetime.timedelta(days=7)
    s3_bucket = config_map['s3_bucket']

    url = url.replace(s3_bucket + '.s3.amazonaws.com',
                      config_map['cloudfront_distro'])

    if not AWS_CLIENT.is_aws_cf_client_set():
        cf_signer = CloudFrontSigner(config_map['cloudfront_keypair_id'],
                                     rsa_signer)
        AWS_CLIENT.set_aws_cf_client(cf_signer)
    else:
        cf_signer = AWS_CLIENT.cf
    signed_url = cf_signer.generate_presigned_url(url, date_less_than=expires)
    return signed_url
Пример #23
0
    def generate_cloudfront_url(self,
                                key,
                                cloudfront_domain,
                                cloudfront_key_id,
                                cloudfront_private_key,
                                url_expiration_time=60 * 60 * 24 * 90,
                                force_http_url=False
                                ):
        """
        Generate presigned url for object on bucket
        :param url_expiration_time: time until url expires in seconds
        :type url_expiration_time: int
        :param key:  path to object on bucket
        :return: url
        :rtype str
        """
        if key is None:
            return

        key = ensure_unicode(key)

        key = key.lstrip('/')

        url = 'https://{}/{}'.format(cloudfront_domain, key)

        def rsa_signer(message):
            private_key = cloudfront_private_key
            return rsa.sign(
                message,
                rsa.PrivateKey.load_pkcs1(private_key.encode('utf8')),
                'SHA-1'
            )  # CloudFront requires SHA-1 hash

        cloudfront_signer = CloudFrontSigner(cloudfront_key_id,
                                             rsa_signer)
        expiry_datetime = (
            datetime.utcnow() +
            timedelta(seconds=url_expiration_time)
        )
        presigned_url = cloudfront_signer.generate_presigned_url(
            url, date_less_than=expiry_datetime
        )

        if force_http_url:
            presigned_url = presigned_url.replace('https://', 'http://')
        return presigned_url
Пример #24
0
    def get_url(self, obj):
        """Url of the Document.

        Parameters
        ----------
        obj : Type[models.Document]
            The document that we want to serialize

        Returns
        -------
        String or None
            the url to fetch the document on CloudFront
            None if the document is still not uploaded to S3 with success

        """
        if obj.uploaded_on is None:
            return None

        url = (
            "{protocol:s}://{cloudfront:s}/{pk!s}/document/{stamp:s}{extension:s}"
            "?response-content-disposition={content_disposition:s}"
        ).format(
            protocol=settings.AWS_S3_URL_PROTOCOL,
            cloudfront=settings.CLOUDFRONT_DOMAIN,
            pk=obj.pk,
            stamp=time_utils.to_timestamp(obj.uploaded_on),
            content_disposition=quote_plus(
                "attachment; filename=" + self.get_filename(obj)
            ),
            extension=self._get_extension_string(obj),
        )

        # Sign the document urls only if the functionality is activated
        if settings.CLOUDFRONT_SIGNED_URLS_ACTIVE:
            date_less_than = timezone.now() + timedelta(
                seconds=settings.CLOUDFRONT_SIGNED_URLS_VALIDITY
            )
            cloudfront_signer = CloudFrontSigner(
                settings.CLOUDFRONT_ACCESS_KEY_ID, cloudfront_utils.rsa_signer
            )
            url = cloudfront_signer.generate_presigned_url(
                url, date_less_than=date_less_than
            )

        return url
Пример #25
0
    def _sign_url(self, url):
        """Generate a presigned cloudfront url.

        Parameters
        ----------
        url: string
            The url to sign

        Returns:
        string
            The signed url

        """
        date_less_than = timezone.now() + timedelta(
            seconds=settings.CLOUDFRONT_SIGNED_URLS_VALIDITY)
        cloudfront_signer = CloudFrontSigner(settings.CLOUDFRONT_ACCESS_KEY_ID,
                                             cloudfront_utils.rsa_signer)
        return cloudfront_signer.generate_presigned_url(
            url, date_less_than=date_less_than)
Пример #26
0
    def get_url(self, obj):
        """Url of the timed text track, signed with a CloudFront key if activated.

        Parameters
        ----------
        obj : Type[models.TimedTextTrack]
            The timed text track that we want to serialize

        Returns
        -------
        string or None
            The url for the timed text track converted to vtt.
            None if the timed text track is still not uploaded to S3 with success.

        """
        if obj.uploaded_on:

            base = "{protocol:s}://{cloudfront:s}/{video!s}".format(
                protocol=settings.AWS_S3_URL_PROTOCOL,
                cloudfront=settings.CLOUDFRONT_DOMAIN,
                video=obj.video.pk,
            )
            url = "{base:s}/timedtext/{stamp:s}_{language:s}{mode:s}.vtt".format(
                base=base,
                stamp=time_utils.to_timestamp(obj.uploaded_on),
                language=obj.language,
                mode="_{:s}".format(obj.mode) if obj.mode else "",
            )

            # Sign the url only if the functionality is activated
            if settings.CLOUDFRONT_SIGNED_URLS_ACTIVE:
                date_less_than = timezone.now() + timedelta(
                    seconds=settings.CLOUDFRONT_SIGNED_URLS_VALIDITY
                )
                cloudfront_signer = CloudFrontSigner(
                    settings.CLOUDFRONT_ACCESS_KEY_ID, cloudfront_utils.rsa_signer
                )
                url = cloudfront_signer.generate_presigned_url(
                    url, date_less_than=date_less_than
                )
            return url
        return None
Пример #27
0
def generate_signed_cloudfront_url(path=None,
                                   cloudfront_url=settings.CLOUDFRONT_URL,
                                   *args,
                                   **kwargs):
    if path is None:
        raise ValueError("Must provide a path for the CloudFront url to sign")

    key_id = kwargs.get('key_id') or settings.CLOUDFRONT_KEY_ID
    expiry = kwargs.get('expiry') or 120
    current_time = datetime.datetime.utcnow()
    expire_date = current_time + datetime.timedelta(seconds=expiry)
    rsa_signer_ = kwargs.get('rsa_signer') or rsa_signer

    cloudfront_signer = CloudFrontSigner(key_id, rsa_signer_)
    url = cloudfront_url + path

    # Create a signed url that will be valid until the specific expiry date provided using a canned policy
    signed_url = cloudfront_signer.generate_presigned_url(
        url, date_less_than=expire_date)
    return signed_url
Пример #28
0
def secure_cloudfront_video(request):
    """
    Generate a redirect to the AWS CloudFront resource.

    The signed resource URL must point to /secure-cloudfront-video/?key=path-to-aws-resource
    The resource URL will have a expiration time of one minute.
    Note that the resource URL must have the slash at the beginning of the string.

    **Example Requests**:

        GET lms-url/secure-cloudfront-video/?key=path-to-aws-resource

    **Responses**
        Redirect 302: If the signing process was successful and the resource exists.
        Not Found 404: If the 'key' query string or any of the Amazon Cloud Front settings is missing.
    """
    meta = request.META

    if not meta or meta.get('HTTP_HOST', '') not in meta.get(
            'HTTP_REFERER', ''):
        raise Http404

    key = request.GET.get('key', '')
    cloudfront_url = getattr(settings, 'SCV_CLOUDFRONT_URL', '')
    cloudfront_id = getattr(settings, 'SCV_CLOUDFRONT_ID', '')

    if not (key and cloudfront_url and cloudfront_id):
        raise Http404

    try:
        cloudfront_signer = CloudFrontSigner(cloudfront_id,
                                             cloudfront_rsa_signer)
        redirect_url = cloudfront_signer.generate_presigned_url(
            url='{}{}'.format(cloudfront_url, key),
            date_less_than=utc_time_plus_one_minute(),
        )
    except MissingCloudFrontInformationError as cloudfront_error:
        log.error(cloudfront_error.message)
        raise Http404

    return redirect(redirect_url)
Пример #29
0
class TestCloudfrontSigner(unittest.TestCase):
    def setUp(self):
        self.signer = CloudFrontSigner("MY_KEY_ID", lambda message: b'signed')
        # It helps but the long string diff will still be slightly different on
        # Python 2.6/2.7/3.x. We won't soly rely on that anyway, so it's fine.
        self.maxDiff = None

    def test_build_canned_policy(self):
        policy = self.signer.build_policy('foo', datetime.datetime(2016, 1, 1))
        expected = (
            '{"Statement":[{"Resource":"foo",'
            '"Condition":{"DateLessThan":{"AWS:EpochTime":1451606400}}}]}')
        self.assertEqual(json.loads(policy), json.loads(expected))
        self.assertEqual(policy, expected)  # This is to ensure the right order

    def test_build_custom_policy(self):
        policy = self.signer.build_policy(
            'foo', datetime.datetime(2016, 1, 1),
            date_greater_than=datetime.datetime(2015, 12, 1),
            ip_address='12.34.56.78/9')
        expected = {
            "Statement": [{
                "Resource": "foo",
                "Condition": {
                    "DateGreaterThan": {"AWS:EpochTime": 1448928000},
                    "DateLessThan": {"AWS:EpochTime": 1451606400},
                    "IpAddress": {"AWS:SourceIp": "12.34.56.78/9"}
                },
            }]
        }
        self.assertEqual(json.loads(policy), expected)

    def _urlparse(self, url):
        if isinstance(url, six.binary_type):
            # Not really necessary, but it helps to reduce noise on Python 2.x
            url = url.decode('utf8')
        return dict(urlparse(url)._asdict())  # Needs an unordered dict here

    def assertEqualUrl(self, url1, url2):
        # We compare long urls by their dictionary parts
        parts1 = self._urlparse(url1)
        parts2 = self._urlparse(url2)
        self.assertEqual(
            parse_qs(parts1.pop('query')), parse_qs(parts2.pop('query')))
        self.assertEqual(parts1, parts2)

    def test_generate_presign_url_with_expire_time(self):
        signed_url = self.signer.generate_presigned_url(
            'http://test.com/foo.txt',
            date_less_than=datetime.datetime(2016, 1, 1))
        expected = (
            'http://test.com/foo.txt?Expires=1451606400&Signature=c2lnbmVk'
            '&Key-Pair-Id=MY_KEY_ID')
        self.assertEqualUrl(signed_url, expected)

    def test_generate_presign_url_with_custom_policy(self):
        policy = self.signer.build_policy(
            'foo', datetime.datetime(2016, 1, 1),
            date_greater_than=datetime.datetime(2015, 12, 1),
            ip_address='12.34.56.78/9')
        signed_url = self.signer.generate_presigned_url(
            'http://test.com/index.html?foo=bar', policy=policy)
        expected = (
            'http://test.com/index.html?foo=bar'
            '&Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiZm9vIiwiQ29uZ'
            'Gl0aW9uIjp7IkRhdGVMZXNzVGhhbiI6eyJBV1M6RXBvY2hUaW1lIj'
            'oxNDUxNjA2NDAwfSwiSXBBZGRyZXNzIjp7IkFXUzpTb3VyY2VJcCI'
            '6IjEyLjM0LjU2Ljc4LzkifSwiRGF0ZUdyZWF0ZXJUaGFuIjp7IkFX'
            'UzpFcG9jaFRpbWUiOjE0NDg5MjgwMDB9fX1dfQ__'
            '&Signature=c2lnbmVk&Key-Pair-Id=MY_KEY_ID')
        self.assertEqualUrl(signed_url, expected)
Пример #30
0
    def get_urls(self, obj):
        """Urls of the video for each type of encoding.

        Parameters
        ----------
        obj : Type[models.Video]
            The video that we want to serialize

        Returns
        -------
        Dictionary or None
            A dictionary of all urls for:
                - mp4 encodings of the video in each resolution
                - jpeg thumbnails of the video in each resolution
                - manifest of the DASH encodings of the video
                - manifest of the HLS encodings of the video
            For a video in live mode only the HLS url is added
            None if the video is still not uploaded to S3 with success

        """
        if obj.live_state is not None:
            # Adaptive Bit Rate manifests
            return {
                "manifests": {
                    "hls": obj.live_info["mediapackage"]["endpoints"]["hls"]["url"],
                    "dash": None,
                },
                "mp4": {},
                "thumbnails": {},
            }

        if obj.uploaded_on is None:
            return None

        thumbnail_urls = {}
        try:
            thumbnail = obj.thumbnail
        except Thumbnail.DoesNotExist:
            pass
        else:
            if thumbnail.uploaded_on is not None:
                thumbnail_serialized = ThumbnailSerializer(thumbnail)
                thumbnail_urls.update(thumbnail_serialized.data.get("urls"))

        urls = {"mp4": {}, "thumbnails": {}}

        base = "{protocol:s}://{cloudfront:s}/{pk!s}".format(
            protocol=settings.AWS_S3_URL_PROTOCOL,
            cloudfront=settings.CLOUDFRONT_DOMAIN,
            pk=obj.pk,
        )
        stamp = time_utils.to_timestamp(obj.uploaded_on)

        date_less_than = timezone.now() + timedelta(
            seconds=settings.CLOUDFRONT_SIGNED_URLS_VALIDITY
        )
        stamp = time_utils.to_timestamp(obj.uploaded_on)
        filename = "{playlist_title:s}_{stamp:s}.mp4".format(
            playlist_title=slugify(obj.playlist.title), stamp=stamp
        )
        for resolution in obj.resolutions:
            # MP4
            mp4_url = (
                "{base:s}/mp4/{stamp:s}_{resolution:d}.mp4"
                "?response-content-disposition={content_disposition:s}"
            ).format(
                base=base,
                stamp=stamp,
                resolution=resolution,
                content_disposition=quote_plus("attachment; filename=" + filename),
            )

            # Thumbnails
            urls["thumbnails"][resolution] = thumbnail_urls.get(
                resolution,
                "{base:s}/thumbnails/{stamp:s}_{resolution:d}.0000000.jpg".format(
                    base=base, stamp=stamp, resolution=resolution
                ),
            )

            # Sign the urls of mp4 videos only if the functionality is activated
            if settings.CLOUDFRONT_SIGNED_URLS_ACTIVE:
                cloudfront_signer = CloudFrontSigner(
                    settings.CLOUDFRONT_ACCESS_KEY_ID, cloudfront_utils.rsa_signer
                )
                mp4_url = cloudfront_signer.generate_presigned_url(
                    mp4_url, date_less_than=date_less_than
                )

            urls["mp4"][resolution] = mp4_url

        # Adaptive Bit Rate manifests
        urls["manifests"] = {
            "dash": "{base:s}/cmaf/{stamp:s}.mpd".format(base=base, stamp=stamp),
            "hls": "{base:s}/cmaf/{stamp:s}.m3u8".format(base=base, stamp=stamp),
        }

        # Previews
        urls["previews"] = "{base:s}/previews/{stamp:s}_100.jpg".format(
            base=base, stamp=stamp
        )

        return urls
Пример #31
0
from botocore.signers import CloudFrontSigner
import rsa
import datetime
import sys
print ": ", sys.argv[1]


def rsa_signer(message):
    private_key = open(
        '/Users/mssalehi/git/CloudFrontSingedstuff/pk-APKAJDUHAEYL6L3T4ZUQ.cer',
        'r').read()
    return rsa.sign(message,
                    rsa.PrivateKey.load_pkcs1(private_key.encode('utf8')),
                    'SHA-1')


cf_signer = CloudFrontSigner('APKAJDUHAEYL6L3T4ZUQ', rsa_signer)
signed_url = cf_signer.generate_presigned_url(sys.argv[1],
                                              date_less_than=datetime.datetime(
                                                  2017, 12, 28))
print(signed_url)
Пример #32
0
    def get_urls(self, obj):
        """Urls of the video for each type of encoding.

        Parameters
        ----------
        obj : Type[models.Video]
            The video that we want to serialize

        Returns
        -------
        Dictionary or None
            A dictionary of all urls for:
                - mp4 encodings of the video in each resolution
                - jpeg thumbnails of the video in each resolution
                - manifest of the DASH encodings of the video
                - manifest of the HLS encodings of the video
            None if the video is still not uploaded to S3 with success

        """
        if obj.uploaded_on is None:
            return None

        urls = {"mp4": {}, "thumbnails": {}}
        base = "{cloudfront:s}/{resource!s}".format(
            cloudfront=settings.CLOUDFRONT_URL, resource=obj.resource_id
        )
        stamp = time_utils.to_timestamp(obj.uploaded_on)

        date_less_than = timezone.now() + timedelta(
            seconds=settings.CLOUDFRONT_SIGNED_URLS_VALIDITY
        )
        for resolution in settings.VIDEO_RESOLUTIONS:
            # MP4
            mp4_url = "{base:s}/mp4/{stamp:s}_{resolution:d}.mp4".format(
                base=base,
                stamp=time_utils.to_timestamp(obj.uploaded_on),
                resolution=resolution,
            )

            # Thumbnails
            urls["thumbnails"][
                resolution
            ] = "{base:s}/thumbnails/{stamp:s}_{resolution:d}.0000000.jpg".format(
                base=base, stamp=stamp, resolution=resolution
            )

            # Sign the urls of mp4 videos only if the functionality is activated
            if settings.CLOUDFRONT_SIGNED_URLS_ACTIVE:
                cloudfront_signer = CloudFrontSigner(
                    settings.CLOUDFRONT_ACCESS_KEY_ID, cloudfront_utils.rsa_signer
                )
                mp4_url = cloudfront_signer.generate_presigned_url(
                    mp4_url, date_less_than=date_less_than
                )

            urls["mp4"][resolution] = mp4_url

        # Adaptive Bit Rate manifests
        urls["manifests"] = {
            "dash": "{base:s}/cmaf/{stamp:s}.mpd".format(base=base, stamp=stamp),
            "hls": "{base:s}/cmaf/{stamp:s}.m3u8".format(base=base, stamp=stamp),
        }

        # Previews
        urls["previews"] = "{base:s}/previews/{stamp:s}_100.jpg".format(
            base=base, stamp=stamp
        )

        return urls
Пример #33
0
from botocore.signers import CloudFrontSigner
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
import datetime

from django.conf import settings


def rsa_signer(message):
    #### .pem is the private keyfile downloaded from CloudFront keypair
    with open(settings.CLOUDFRONT_PK_FILE_NAME, 'rb') as key_file:
        private_key = serialization.load_pem_private_key(
            key_file.read(), password=None, backend=default_backend())
        signer = private_key.signer(padding.PKCS1v15(), hashes.SHA1())
        signer.update(message)
        return signer.finalize()


key_id = settings.CLOUDFRONT_KEY_ID
url = 'd1on7f8lkpfomn.cloudfront.net/media/2test2.jpg'
current_time = datetime.datetime.utcnow()
expire_date = current_time + datetime.timedelta(minutes=2)
cloudfront_signer = CloudFrontSigner(key_id, rsa_signer)

# Create a signed url that will be valid until the specific expiry date provided using a canned policy
signed_url = cloudfront_signer.generate_presigned_url(
    url, date_less_than=expire_date)
print(signed_url)
Пример #34
0
class TestCloudfrontSigner(unittest.TestCase):
    def setUp(self):
        self.signer = CloudFrontSigner("MY_KEY_ID", lambda message: b'signed')
        # It helps but the long string diff will still be slightly different on
        # Python 2.6/2.7/3.x. We won't soly rely on that anyway, so it's fine.
        self.maxDiff = None

    def test_build_canned_policy(self):
        policy = self.signer.build_policy('foo', datetime.datetime(2016, 1, 1))
        expected = (
            '{"Statement":[{"Resource":"foo",'
            '"Condition":{"DateLessThan":{"AWS:EpochTime":1451606400}}}]}')
        self.assertEqual(json.loads(policy), json.loads(expected))
        self.assertEqual(policy, expected)  # This is to ensure the right order

    def test_build_custom_policy(self):
        policy = self.signer.build_policy('foo',
                                          datetime.datetime(2016, 1, 1),
                                          date_greater_than=datetime.datetime(
                                              2015, 12, 1),
                                          ip_address='12.34.56.78/9')
        expected = {
            "Statement": [{
                "Resource": "foo",
                "Condition": {
                    "DateGreaterThan": {
                        "AWS:EpochTime": 1448928000
                    },
                    "DateLessThan": {
                        "AWS:EpochTime": 1451606400
                    },
                    "IpAddress": {
                        "AWS:SourceIp": "12.34.56.78/9"
                    }
                },
            }]
        }
        self.assertEqual(json.loads(policy), expected)

    def _urlparse(self, url):
        if isinstance(url, six.binary_type):
            # Not really necessary, but it helps to reduce noise on Python 2.x
            url = url.decode('utf8')
        return dict(urlparse(url)._asdict())  # Needs an unordered dict here

    def assertEqualUrl(self, url1, url2):
        # We compare long urls by their dictionary parts
        parts1 = self._urlparse(url1)
        parts2 = self._urlparse(url2)
        self.assertEqual(parse_qs(parts1.pop('query')),
                         parse_qs(parts2.pop('query')))
        self.assertEqual(parts1, parts2)

    def test_generate_presign_url_with_expire_time(self):
        signed_url = self.signer.generate_presigned_url(
            'http://test.com/foo.txt',
            date_less_than=datetime.datetime(2016, 1, 1))
        expected = (
            'http://test.com/foo.txt?Expires=1451606400&Signature=c2lnbmVk'
            '&Key-Pair-Id=MY_KEY_ID')
        self.assertEqualUrl(signed_url, expected)

    def test_generate_presign_url_with_custom_policy(self):
        policy = self.signer.build_policy('foo',
                                          datetime.datetime(2016, 1, 1),
                                          date_greater_than=datetime.datetime(
                                              2015, 12, 1),
                                          ip_address='12.34.56.78/9')
        signed_url = self.signer.generate_presigned_url(
            'http://test.com/index.html?foo=bar', policy=policy)
        expected = ('http://test.com/index.html?foo=bar'
                    '&Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiZm9vIiwiQ29uZ'
                    'Gl0aW9uIjp7IkRhdGVMZXNzVGhhbiI6eyJBV1M6RXBvY2hUaW1lIj'
                    'oxNDUxNjA2NDAwfSwiSXBBZGRyZXNzIjp7IkFXUzpTb3VyY2VJcCI'
                    '6IjEyLjM0LjU2Ljc4LzkifSwiRGF0ZUdyZWF0ZXJUaGFuIjp7IkFX'
                    'UzpFcG9jaFRpbWUiOjE0NDg5MjgwMDB9fX1dfQ__'
                    '&Signature=c2lnbmVk&Key-Pair-Id=MY_KEY_ID')
        self.assertEqualUrl(signed_url, expected)