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)
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)
def create_signed_cookies(self, resource, expire_minutes=3): """ generate the Cloudfront download distirbution signed cookies @resource path to the file, path, or wildcard pattern to generate policy for @expire_minutes number of minutes until expiration return tuple with domain used within policy (so it matches cookie domain), and dict of cloudfront cookies you should set in request header """ http_resource = self.get_http_resource_url( resource, secure=True ) #per-file access #NOTE secure should match security settings of cloudfront distribution cloudfront_signer = CloudFrontSigner( settings.CLOUDFRONT_SIGNED_COOKIES_KEY_PAIR_ID, rsa_signer) expires = SignedCookiedCloudfrontDistribution.get_expires( expire_minutes) policy = cloudfront_signer.build_policy( http_resource, datetime.fromtimestamp(expires)) encoded_policy = cloudfront_signer._url_b64encode( policy.encode('utf-8')).decode('utf-8') #assemble the 3 Cloudfront cookies signature = rsa_signer(policy.encode('utf-8')) encoded_signature = cloudfront_signer._url_b64encode(signature).decode( 'utf-8') cookies = { "CloudFront-Policy": encoded_policy, "CloudFront-Signature": encoded_signature, "CloudFront-Key-Pair-Id": settings.CLOUDFRONT_SIGNED_COOKIES_KEY_PAIR_ID, } return cookies
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
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
def generate_cloudfront_urls_signed_parameters(resource, date_less_than): """ Generate all parameters use by a cloudfront signed url. Mainly extracted from CloudFrontSigner class. """ cloudfront_signer = CloudFrontSigner( settings.CLOUDFRONT_SIGNED_PUBLIC_KEY_ID, rsa_signer, ) policy = cloudfront_signer.build_policy( resource=resource, date_less_than=date_less_than).encode("utf8") signature = cloudfront_signer.rsa_signer(policy) return [ f"Policy={_url_b64encode(policy).decode('utf8')}", f"Signature={_url_b64encode(signature).decode('utf8')}", f"Key-Pair-Id={cloudfront_signer.key_id}", ]
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)
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)