| 
									
										
										
										
											2021-02-10 02:06:03 -07:00
										 |  |  | import io | 
					
						
							| 
									
										
										
										
											2021-07-26 07:40:39 +01:00
										 |  |  | from urllib.parse import urlparse, parse_qs | 
					
						
							| 
									
										
										
										
											2022-03-11 20:28:45 -01:00
										 |  |  | import sure  # noqa # pylint: disable=unused-import | 
					
						
							| 
									
										
										
										
											2023-03-03 21:40:55 -01:00
										 |  |  | import requests | 
					
						
							| 
									
										
										
										
											2022-11-22 22:41:02 -01:00
										 |  |  | import pytest | 
					
						
							|  |  |  | import xmltodict | 
					
						
							| 
									
										
										
										
											2013-03-05 08:14:43 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-16 06:38:40 -07:00
										 |  |  | from flask.testing import FlaskClient | 
					
						
							| 
									
										
										
										
											2013-03-05 08:14:43 -05:00
										 |  |  | import moto.server as server | 
					
						
							| 
									
										
										
										
											2023-03-03 21:40:55 -01:00
										 |  |  | from moto.moto_server.threaded_moto_server import ThreadedMotoServer | 
					
						
							| 
									
										
										
										
											2021-08-04 17:24:26 +01:00
										 |  |  | from unittest.mock import patch | 
					
						
							| 
									
										
										
										
											2013-03-05 08:14:43 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-31 08:44:26 -07:00
										 |  |  | """
 | 
					
						
							| 
									
										
										
										
											2013-03-05 08:14:43 -05:00
										 |  |  | Test the different server responses | 
					
						
							| 
									
										
										
										
											2019-10-31 08:44:26 -07:00
										 |  |  | """
 | 
					
						
							| 
									
										
										
										
											2013-03-05 08:14:43 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-16 06:38:40 -07:00
										 |  |  | class AuthenticatedClient(FlaskClient): | 
					
						
							|  |  |  |     def open(self, *args, **kwargs): | 
					
						
							| 
									
										
										
										
											2019-10-31 08:44:26 -07:00
										 |  |  |         kwargs["headers"] = kwargs.get("headers", {}) | 
					
						
							|  |  |  |         kwargs["headers"]["Authorization"] = "Any authorization header" | 
					
						
							|  |  |  |         kwargs["content_length"] = 0  # Fixes content-length complaints. | 
					
						
							| 
									
										
										
										
											2021-12-01 22:06:58 -01:00
										 |  |  |         return super().open(*args, **kwargs) | 
					
						
							| 
									
										
										
										
											2017-09-16 06:38:40 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def authenticated_client(): | 
					
						
							| 
									
										
										
										
											2013-12-28 20:15:37 -05:00
										 |  |  |     backend = server.create_backend_app("s3") | 
					
						
							| 
									
										
										
										
											2017-09-16 06:38:40 -07:00
										 |  |  |     backend.test_client_class = AuthenticatedClient | 
					
						
							|  |  |  |     return backend.test_client() | 
					
						
							| 
									
										
										
										
											2013-12-28 20:15:37 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-16 06:38:40 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | def test_s3_server_get(): | 
					
						
							|  |  |  |     test_client = authenticated_client() | 
					
						
							| 
									
										
										
										
											2019-10-31 08:44:26 -07:00
										 |  |  |     res = test_client.get("/") | 
					
						
							| 
									
										
										
										
											2013-03-05 08:14:43 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-31 08:44:26 -07:00
										 |  |  |     res.data.should.contain(b"ListAllMyBucketsResult") | 
					
						
							| 
									
										
										
										
											2013-03-05 08:14:43 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-22 22:41:02 -01:00
										 |  |  | @pytest.mark.parametrize("key_name", ["bar_baz", "bar+baz", "baz bar"]) | 
					
						
							|  |  |  | def test_s3_server_bucket_create(key_name): | 
					
						
							| 
									
										
										
										
											2017-09-16 06:38:40 -07:00
										 |  |  |     test_client = authenticated_client() | 
					
						
							| 
									
										
										
										
											2013-12-28 20:15:37 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-31 08:44:26 -07:00
										 |  |  |     res = test_client.put("/", "http://foobaz.localhost:5000/") | 
					
						
							| 
									
										
										
										
											2013-03-05 08:14:43 -05:00
										 |  |  |     res.status_code.should.equal(200) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-31 08:44:26 -07:00
										 |  |  |     res = test_client.get("/") | 
					
						
							|  |  |  |     res.data.should.contain(b"<Name>foobaz</Name>") | 
					
						
							| 
									
										
										
										
											2013-03-05 08:14:43 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-31 08:44:26 -07:00
										 |  |  |     res = test_client.get("/", "http://foobaz.localhost:5000/") | 
					
						
							| 
									
										
										
										
											2013-03-05 08:14:43 -05:00
										 |  |  |     res.status_code.should.equal(200) | 
					
						
							| 
									
										
										
										
											2014-08-26 13:25:50 -04:00
										 |  |  |     res.data.should.contain(b"ListBucketResult") | 
					
						
							| 
									
										
										
										
											2013-03-05 08:14:43 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-22 22:41:02 -01:00
										 |  |  |     res = test_client.put( | 
					
						
							|  |  |  |         f"/{key_name}", "http://foobaz.localhost:5000/", data="test value" | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     res.status_code.should.equal(200) | 
					
						
							|  |  |  |     assert "ETag" in dict(res.headers) | 
					
						
							| 
									
										
										
										
											2013-03-05 08:14:43 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-22 22:41:02 -01:00
										 |  |  |     res = test_client.get( | 
					
						
							|  |  |  |         "/", "http://foobaz.localhost:5000/", query_string={"prefix": key_name} | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     res.status_code.should.equal(200) | 
					
						
							|  |  |  |     content = xmltodict.parse(res.data)["ListBucketResult"]["Contents"] | 
					
						
							|  |  |  |     # If we receive a dict, we only received one result | 
					
						
							|  |  |  |     # If content is of type list, our call returned multiple results - which is not correct | 
					
						
							|  |  |  |     content.should.be.a(dict) | 
					
						
							|  |  |  |     content["Key"].should.equal(key_name) | 
					
						
							| 
									
										
										
										
											2022-05-05 11:06:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-23 09:13:51 -01:00
										 |  |  |     res = test_client.head("http://foobaz.localhost:5000") | 
					
						
							|  |  |  |     assert res.status_code == 200 | 
					
						
							|  |  |  |     assert res.headers.get("x-amz-bucket-region") == "us-east-1" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-22 22:41:02 -01:00
										 |  |  |     res = test_client.get(f"/{key_name}", "http://foobaz.localhost:5000/") | 
					
						
							|  |  |  |     res.status_code.should.equal(200) | 
					
						
							|  |  |  |     res.data.should.equal(b"test value") | 
					
						
							| 
									
										
										
										
											2023-03-24 10:55:56 -01:00
										 |  |  |     assert res.headers.get("AcceptRanges") == "bytes" | 
					
						
							| 
									
										
										
										
											2013-05-17 19:41:39 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-26 17:51:19 +01:00
										 |  |  | def test_s3_server_ignore_subdomain_for_bucketnames(): | 
					
						
							| 
									
										
										
										
											2021-11-17 20:02:14 -01:00
										 |  |  |     with patch("moto.settings.S3_IGNORE_SUBDOMAIN_BUCKETNAME", True): | 
					
						
							| 
									
										
										
										
											2021-03-26 17:51:19 +01:00
										 |  |  |         test_client = authenticated_client() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         res = test_client.put("/mybucket", "http://foobaz.localhost:5000/") | 
					
						
							|  |  |  |         res.status_code.should.equal(200) | 
					
						
							|  |  |  |         res.data.should.contain(b"mybucket") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-22 22:13:34 +07:00
										 |  |  | def test_s3_server_bucket_versioning(): | 
					
						
							| 
									
										
										
										
											2017-09-16 06:38:40 -07:00
										 |  |  |     test_client = authenticated_client() | 
					
						
							| 
									
										
										
										
											2015-02-22 22:13:34 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-09 17:40:22 +00:00
										 |  |  |     res = test_client.put("/", "http://foobaz.localhost:5000/") | 
					
						
							|  |  |  |     res.status_code.should.equal(200) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-22 22:13:34 +07:00
										 |  |  |     # Just enough XML to enable versioning | 
					
						
							| 
									
										
										
										
											2019-10-31 08:44:26 -07:00
										 |  |  |     body = "<Status>Enabled</Status>" | 
					
						
							|  |  |  |     res = test_client.put("/?versioning", "http://foobaz.localhost:5000", data=body) | 
					
						
							| 
									
										
										
										
											2015-02-22 22:13:34 +07:00
										 |  |  |     res.status_code.should.equal(200) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-17 19:41:39 -04:00
										 |  |  | def test_s3_server_post_to_bucket(): | 
					
						
							| 
									
										
										
										
											2017-09-16 06:38:40 -07:00
										 |  |  |     test_client = authenticated_client() | 
					
						
							| 
									
										
										
										
											2013-12-28 20:15:37 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-31 08:44:26 -07:00
										 |  |  |     res = test_client.put("/", "http://tester.localhost:5000/") | 
					
						
							| 
									
										
										
										
											2013-05-17 19:41:39 -04:00
										 |  |  |     res.status_code.should.equal(200) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-31 08:44:26 -07:00
										 |  |  |     test_client.post( | 
					
						
							|  |  |  |         "/", | 
					
						
							|  |  |  |         "https://tester.localhost:5000/", | 
					
						
							|  |  |  |         data={"key": "the-key", "file": "nothing"}, | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2013-05-17 19:41:39 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-31 08:44:26 -07:00
										 |  |  |     res = test_client.get("/the-key", "http://tester.localhost:5000/") | 
					
						
							| 
									
										
										
										
											2013-05-17 19:41:39 -04:00
										 |  |  |     res.status_code.should.equal(200) | 
					
						
							| 
									
										
										
										
											2014-08-26 13:25:50 -04:00
										 |  |  |     res.data.should.equal(b"nothing") | 
					
						
							| 
									
										
										
										
											2017-05-01 12:13:12 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-10 02:06:03 -07:00
										 |  |  | def test_s3_server_post_to_bucket_redirect(): | 
					
						
							|  |  |  |     test_client = authenticated_client() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     res = test_client.put("/", "http://tester.localhost:5000/") | 
					
						
							|  |  |  |     res.status_code.should.equal(200) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     redirect_base = "https://redirect.com/success/" | 
					
						
							|  |  |  |     filecontent = "nothing" | 
					
						
							|  |  |  |     filename = "test_filename.txt" | 
					
						
							|  |  |  |     res = test_client.post( | 
					
						
							|  |  |  |         "/", | 
					
						
							|  |  |  |         "https://tester.localhost:5000/", | 
					
						
							|  |  |  |         data={ | 
					
						
							|  |  |  |             "key": "asdf/the-key/${filename}", | 
					
						
							|  |  |  |             "file": (io.BytesIO(filecontent.encode("utf8")), filename), | 
					
						
							|  |  |  |             "success_action_redirect": redirect_base, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2022-11-17 21:41:08 -01:00
										 |  |  |     real_key = f"asdf/the-key/{filename}" | 
					
						
							| 
									
										
										
										
											2021-02-10 02:06:03 -07:00
										 |  |  |     res.status_code.should.equal(303) | 
					
						
							|  |  |  |     redirect = res.headers["location"] | 
					
						
							|  |  |  |     assert redirect.startswith(redirect_base) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     parts = urlparse(redirect) | 
					
						
							|  |  |  |     args = parse_qs(parts.query) | 
					
						
							|  |  |  |     assert args["key"][0] == real_key | 
					
						
							|  |  |  |     assert args["bucket"][0] == "tester" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-17 21:41:08 -01:00
										 |  |  |     res = test_client.get(f"/{real_key}", "http://tester.localhost:5000/") | 
					
						
							| 
									
										
										
										
											2021-02-10 02:06:03 -07:00
										 |  |  |     res.status_code.should.equal(200) | 
					
						
							|  |  |  |     res.data.should.equal(filecontent.encode("utf8")) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-01 12:13:12 -07:00
										 |  |  | def test_s3_server_post_without_content_length(): | 
					
						
							| 
									
										
										
										
											2017-09-16 06:38:40 -07:00
										 |  |  |     test_client = authenticated_client() | 
					
						
							| 
									
										
										
										
											2017-05-01 12:13:12 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-31 08:44:26 -07:00
										 |  |  |     res = test_client.put( | 
					
						
							|  |  |  |         "/", "http://tester.localhost:5000/", environ_overrides={"CONTENT_LENGTH": ""} | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2017-05-01 12:13:12 -07:00
										 |  |  |     res.status_code.should.equal(411) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-31 08:44:26 -07:00
										 |  |  |     res = test_client.post( | 
					
						
							|  |  |  |         "/", "https://tester.localhost:5000/", environ_overrides={"CONTENT_LENGTH": ""} | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2017-05-01 12:13:12 -07:00
										 |  |  |     res.status_code.should.equal(411) | 
					
						
							| 
									
										
										
										
											2017-08-05 20:29:40 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def test_s3_server_post_unicode_bucket_key(): | 
					
						
							|  |  |  |     # Make sure that we can deal with non-ascii characters in request URLs (e.g., S3 object names) | 
					
						
							|  |  |  |     dispatcher = server.DomainDispatcherApplication(server.create_backend_app) | 
					
						
							| 
									
										
										
										
											2019-10-31 08:44:26 -07:00
										 |  |  |     backend_app = dispatcher.get_application( | 
					
						
							|  |  |  |         {"HTTP_HOST": "s3.amazonaws.com", "PATH_INFO": "/test-bucket/test-object-てすと"} | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2017-08-05 20:29:40 +10:00
										 |  |  |     assert backend_app | 
					
						
							| 
									
										
										
										
											2019-10-31 08:44:26 -07:00
										 |  |  |     backend_app = dispatcher.get_application( | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             "HTTP_HOST": "s3.amazonaws.com", | 
					
						
							|  |  |  |             "PATH_INFO": "/test-bucket/test-object-てすと".encode("utf-8"), | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2017-08-05 20:29:40 +10:00
										 |  |  |     assert backend_app | 
					
						
							| 
									
										
										
										
											2020-09-19 11:07:17 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def test_s3_server_post_cors(): | 
					
						
							| 
									
										
										
										
											2021-10-30 12:02:30 +02:00
										 |  |  |     """Test default CORS headers set by flask-cors plugin""" | 
					
						
							| 
									
										
										
										
											2020-09-19 11:07:17 +02:00
										 |  |  |     test_client = authenticated_client() | 
					
						
							| 
									
										
										
										
											2021-10-30 12:02:30 +02:00
										 |  |  |     # Create the bucket | 
					
						
							|  |  |  |     test_client.put("/", "http://tester.localhost:5000/") | 
					
						
							| 
									
										
										
										
											2020-09-19 11:07:17 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     preflight_headers = { | 
					
						
							|  |  |  |         "Access-Control-Request-Method": "POST", | 
					
						
							|  |  |  |         "Access-Control-Request-Headers": "origin, x-requested-with", | 
					
						
							|  |  |  |         "Origin": "https://localhost:9000", | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     res = test_client.options( | 
					
						
							|  |  |  |         "/", "http://tester.localhost:5000/", headers=preflight_headers | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     assert res.status_code in [200, 204] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     expected_methods = set(["DELETE", "PATCH", "PUT", "GET", "HEAD", "POST", "OPTIONS"]) | 
					
						
							|  |  |  |     assert ( | 
					
						
							|  |  |  |         set(res.headers["Access-Control-Allow-Methods"].split(", ")) == expected_methods | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     res.headers.should.have.key("Access-Control-Allow-Origin").which.should.equal( | 
					
						
							|  |  |  |         "https://localhost:9000" | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     res.headers.should.have.key("Access-Control-Allow-Headers").which.should.equal( | 
					
						
							|  |  |  |         "origin, x-requested-with" | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2021-10-30 12:02:30 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def test_s3_server_post_cors_exposed_header(): | 
					
						
							|  |  |  |     """Test that we can override default CORS headers with custom bucket rules""" | 
					
						
							| 
									
										
										
										
											2023-01-07 11:35:14 +00:00
										 |  |  |     # github.com/getmoto/moto/issues/4220 | 
					
						
							| 
									
										
										
										
											2021-10-30 12:02:30 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     cors_config_payload = """<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
 | 
					
						
							|  |  |  |   <CORSRule> | 
					
						
							|  |  |  |     <AllowedOrigin>https://example.org</AllowedOrigin> | 
					
						
							|  |  |  |     <AllowedMethod>HEAD</AllowedMethod> | 
					
						
							|  |  |  |     <AllowedMethod>GET</AllowedMethod> | 
					
						
							|  |  |  |     <AllowedMethod>PUT</AllowedMethod> | 
					
						
							|  |  |  |     <AllowedMethod>POST</AllowedMethod> | 
					
						
							|  |  |  |     <AllowedMethod>DELETE</AllowedMethod> | 
					
						
							|  |  |  |     <AllowedHeader>*</AllowedHeader> | 
					
						
							|  |  |  |     <ExposeHeader>ETag</ExposeHeader> | 
					
						
							|  |  |  |     <MaxAgeSeconds>3000</MaxAgeSeconds> | 
					
						
							|  |  |  |   </CORSRule> | 
					
						
							|  |  |  | </CORSConfiguration> | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     test_client = authenticated_client() | 
					
						
							|  |  |  |     preflight_headers = { | 
					
						
							|  |  |  |         "Access-Control-Request-Method": "POST", | 
					
						
							|  |  |  |         "Access-Control-Request-Headers": "origin, x-requested-with", | 
					
						
							| 
									
										
										
										
											2023-03-03 21:40:55 -01:00
										 |  |  |         "Origin": "https://example.org", | 
					
						
							| 
									
										
										
										
											2021-10-30 12:02:30 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |     # Returns 403 on non existing bucket | 
					
						
							|  |  |  |     preflight_response = test_client.options( | 
					
						
							|  |  |  |         "/", "http://testcors.localhost:5000/", headers=preflight_headers | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     assert preflight_response.status_code == 403 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Create the bucket | 
					
						
							|  |  |  |     test_client.put("/", "http://testcors.localhost:5000/") | 
					
						
							|  |  |  |     res = test_client.put( | 
					
						
							|  |  |  |         "/?cors", "http://testcors.localhost:5000", data=cors_config_payload | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     assert res.status_code == 200 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     cors_res = test_client.get("/?cors", "http://testcors.localhost:5000") | 
					
						
							|  |  |  |     assert b"<ExposedHeader>ETag</ExposedHeader>" in cors_res.data | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-05 14:19:06 +01:00
										 |  |  |     # Test OPTIONS bucket response and key response | 
					
						
							|  |  |  |     for key_name in ("/", "/test"): | 
					
						
							|  |  |  |         preflight_response = test_client.options( | 
					
						
							|  |  |  |             key_name, "http://testcors.localhost:5000/", headers=preflight_headers | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         assert preflight_response.status_code == 200 | 
					
						
							|  |  |  |         expected_cors_headers = { | 
					
						
							|  |  |  |             "Access-Control-Allow-Methods": "HEAD, GET, PUT, POST, DELETE", | 
					
						
							|  |  |  |             "Access-Control-Allow-Origin": "https://example.org", | 
					
						
							|  |  |  |             "Access-Control-Allow-Headers": "*", | 
					
						
							|  |  |  |             "Access-Control-Expose-Headers": "ETag", | 
					
						
							|  |  |  |             "Access-Control-Max-Age": "3000", | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         for header_name, header_value in expected_cors_headers.items(): | 
					
						
							|  |  |  |             assert header_name in preflight_response.headers | 
					
						
							|  |  |  |             assert preflight_response.headers[header_name] == header_value | 
					
						
							| 
									
										
										
										
											2023-03-03 21:40:55 -01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def test_s3_server_post_cors_multiple_origins(): | 
					
						
							|  |  |  |     """Test that Moto only responds with the Origin that we that hosts the server""" | 
					
						
							|  |  |  |     # github.com/getmoto/moto/issues/6003 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     cors_config_payload = """<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
 | 
					
						
							|  |  |  |   <CORSRule> | 
					
						
							|  |  |  |     <AllowedOrigin>https://example.org</AllowedOrigin> | 
					
						
							|  |  |  |     <AllowedOrigin>https://localhost:6789</AllowedOrigin> | 
					
						
							|  |  |  |     <AllowedMethod>POST</AllowedMethod> | 
					
						
							|  |  |  |   </CORSRule> | 
					
						
							|  |  |  | </CORSConfiguration> | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     thread = ThreadedMotoServer(port="6789", verbose=False) | 
					
						
							|  |  |  |     thread.start() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Create the bucket | 
					
						
							|  |  |  |     requests.put("http://testcors.localhost:6789/") | 
					
						
							|  |  |  |     requests.put("http://testcors.localhost:6789/?cors", data=cors_config_payload) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Test only our requested origin is returned | 
					
						
							|  |  |  |     preflight_response = requests.options( | 
					
						
							|  |  |  |         "http://testcors.localhost:6789/test", | 
					
						
							|  |  |  |         headers={ | 
					
						
							|  |  |  |             "Access-Control-Request-Method": "POST", | 
					
						
							|  |  |  |             "Origin": "https://localhost:6789", | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     assert preflight_response.status_code == 200 | 
					
						
							|  |  |  |     assert ( | 
					
						
							|  |  |  |         preflight_response.headers["Access-Control-Allow-Origin"] | 
					
						
							|  |  |  |         == "https://localhost:6789" | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     assert preflight_response.content == b"" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Verify a request with unknown origin fails | 
					
						
							|  |  |  |     preflight_response = requests.options( | 
					
						
							|  |  |  |         "http://testcors.localhost:6789/test", | 
					
						
							|  |  |  |         headers={ | 
					
						
							|  |  |  |             "Access-Control-Request-Method": "POST", | 
					
						
							|  |  |  |             "Origin": "https://unknown.host", | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     assert preflight_response.status_code == 403 | 
					
						
							|  |  |  |     assert b"<Code>AccessForbidden</Code>" in preflight_response.content | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Verify we can use a wildcard anywhere in the origin | 
					
						
							|  |  |  |     cors_config_payload = """<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><CORSRule>
 | 
					
						
							|  |  |  |             <AllowedOrigin>https://*.google.com</AllowedOrigin> | 
					
						
							|  |  |  |             <AllowedMethod>POST</AllowedMethod> | 
					
						
							|  |  |  |           </CORSRule></CORSConfiguration>"""
 | 
					
						
							|  |  |  |     requests.put("http://testcors.localhost:6789/?cors", data=cors_config_payload) | 
					
						
							|  |  |  |     for origin in ["https://sth.google.com", "https://a.google.com"]: | 
					
						
							|  |  |  |         preflight_response = requests.options( | 
					
						
							|  |  |  |             "http://testcors.localhost:6789/test", | 
					
						
							|  |  |  |             headers={"Access-Control-Request-Method": "POST", "Origin": origin}, | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         assert preflight_response.status_code == 200 | 
					
						
							|  |  |  |         assert preflight_response.headers["Access-Control-Allow-Origin"] == origin | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Non-matching requests throw an error though - it does not act as a full wildcard | 
					
						
							|  |  |  |     preflight_response = requests.options( | 
					
						
							|  |  |  |         "http://testcors.localhost:6789/test", | 
					
						
							|  |  |  |         headers={ | 
					
						
							|  |  |  |             "Access-Control-Request-Method": "POST", | 
					
						
							|  |  |  |             "Origin": "sth.microsoft.com", | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     assert preflight_response.status_code == 403 | 
					
						
							|  |  |  |     assert b"<Code>AccessForbidden</Code>" in preflight_response.content | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Verify we can use a wildcard as the origin | 
					
						
							|  |  |  |     cors_config_payload = """<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><CORSRule>
 | 
					
						
							|  |  |  |                 <AllowedOrigin>*</AllowedOrigin> | 
					
						
							|  |  |  |                 <AllowedMethod>POST</AllowedMethod> | 
					
						
							|  |  |  |               </CORSRule></CORSConfiguration>"""
 | 
					
						
							|  |  |  |     requests.put("http://testcors.localhost:6789/?cors", data=cors_config_payload) | 
					
						
							|  |  |  |     for origin in ["https://a.google.com", "http://b.microsoft.com", "any"]: | 
					
						
							|  |  |  |         preflight_response = requests.options( | 
					
						
							|  |  |  |             "http://testcors.localhost:6789/test", | 
					
						
							|  |  |  |             headers={"Access-Control-Request-Method": "POST", "Origin": origin}, | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         assert preflight_response.status_code == 200 | 
					
						
							|  |  |  |         assert preflight_response.headers["Access-Control-Allow-Origin"] == origin | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     thread.stop() |