| 
									
										
										
										
											2021-09-25 17:25:27 +00:00
										 |  |  | import json | 
					
						
							| 
									
										
										
										
											2023-08-07 12:48:48 -04:00
										 |  |  | from unittest import SkipTest | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import boto3 | 
					
						
							| 
									
										
										
										
											2021-08-29 14:49:05 +01:00
										 |  |  | import pytest | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from botocore.exceptions import ClientError | 
					
						
							| 
									
										
										
										
											2022-05-12 13:59:47 +00:00
										 |  |  | from moto import mock_iam, mock_s3, mock_sts, settings | 
					
						
							| 
									
										
										
										
											2022-08-13 09:49:43 +00:00
										 |  |  | from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID, set_initial_no_auth_action_count | 
					
						
							| 
									
										
										
										
											2021-08-29 14:49:05 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_s3 | 
					
						
							|  |  |  | @set_initial_no_auth_action_count(0) | 
					
						
							|  |  |  | def test_load_unexisting_object_without_auth_should_return_403(): | 
					
						
							|  |  |  |     if settings.TEST_SERVER_MODE: | 
					
						
							|  |  |  |         raise SkipTest("Auth decorator does not work in server mode") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-07 12:48:48 -04:00
										 |  |  |     # Head an S3 object we should have no access to. | 
					
						
							| 
									
										
										
										
											2021-08-29 14:49:05 +01:00
										 |  |  |     resource = boto3.resource("s3", region_name="us-east-1") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     obj = resource.Object("myfakebucket", "myfakekey") | 
					
						
							|  |  |  |     with pytest.raises(ClientError) as ex: | 
					
						
							|  |  |  |         obj.load() | 
					
						
							|  |  |  |     err = ex.value.response["Error"] | 
					
						
							| 
									
										
										
										
											2023-08-07 12:48:48 -04:00
										 |  |  |     assert err["Code"] == "InvalidAccessKeyId" | 
					
						
							|  |  |  |     assert err["Message"] == ( | 
					
						
							| 
									
										
										
										
											2021-08-29 14:49:05 +01:00
										 |  |  |         "The AWS Access Key Id you provided does not exist in our records." | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2021-09-25 17:25:27 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @set_initial_no_auth_action_count(4) | 
					
						
							|  |  |  | @mock_s3 | 
					
						
							|  |  |  | def test_head_bucket_with_correct_credentials(): | 
					
						
							|  |  |  |     if settings.TEST_SERVER_MODE: | 
					
						
							|  |  |  |         raise SkipTest("Auth decorator does not work in server mode") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # These calls are all unauthenticated | 
					
						
							|  |  |  |     iam_keys = create_user_with_access_key_and_policy() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # This S3-client has correct credentials | 
					
						
							| 
									
										
										
										
											2023-08-07 12:48:48 -04:00
										 |  |  |     s3_client = boto3.client( | 
					
						
							| 
									
										
										
										
											2021-09-25 17:25:27 +00:00
										 |  |  |         "s3", | 
					
						
							|  |  |  |         aws_access_key_id=iam_keys["AccessKeyId"], | 
					
						
							|  |  |  |         aws_secret_access_key=iam_keys["SecretAccessKey"], | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2023-08-07 12:48:48 -04:00
										 |  |  |     s3_client.create_bucket(Bucket="mock_bucket") | 
					
						
							| 
									
										
										
										
											2021-09-25 17:25:27 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Calling head_bucket with the correct credentials works | 
					
						
							|  |  |  |     my_head_bucket( | 
					
						
							|  |  |  |         "mock_bucket", | 
					
						
							|  |  |  |         aws_access_key_id=iam_keys["AccessKeyId"], | 
					
						
							|  |  |  |         aws_secret_access_key=iam_keys["SecretAccessKey"], | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-07 12:48:48 -04:00
										 |  |  |     # Verify we can make calls that contain a querystring.  Specifically, | 
					
						
							|  |  |  |     # verify that we are able to build/match the AWS signature for a URL | 
					
						
							|  |  |  |     # with a querystring. | 
					
						
							|  |  |  |     s3_client.get_bucket_location(Bucket="mock_bucket") | 
					
						
							|  |  |  |     s3_client.list_objects_v2(Bucket="mock_bucket") | 
					
						
							| 
									
										
										
										
											2023-02-20 19:48:59 -01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-25 17:25:27 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | @set_initial_no_auth_action_count(4) | 
					
						
							|  |  |  | @mock_s3 | 
					
						
							|  |  |  | def test_head_bucket_with_incorrect_credentials(): | 
					
						
							|  |  |  |     if settings.TEST_SERVER_MODE: | 
					
						
							|  |  |  |         raise SkipTest("Auth decorator does not work in server mode") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # These calls are all authenticated | 
					
						
							|  |  |  |     iam_keys = create_user_with_access_key_and_policy() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Create the bucket with correct credentials | 
					
						
							| 
									
										
										
										
											2023-08-07 12:48:48 -04:00
										 |  |  |     s3_client = boto3.client( | 
					
						
							| 
									
										
										
										
											2021-09-25 17:25:27 +00:00
										 |  |  |         "s3", | 
					
						
							|  |  |  |         aws_access_key_id=iam_keys["AccessKeyId"], | 
					
						
							|  |  |  |         aws_secret_access_key=iam_keys["SecretAccessKey"], | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2023-08-07 12:48:48 -04:00
										 |  |  |     s3_client.create_bucket(Bucket="mock_bucket") | 
					
						
							| 
									
										
										
										
											2021-09-25 17:25:27 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Call head_bucket with incorrect credentials | 
					
						
							|  |  |  |     with pytest.raises(ClientError) as ex: | 
					
						
							|  |  |  |         my_head_bucket( | 
					
						
							|  |  |  |             "mock_bucket", | 
					
						
							|  |  |  |             aws_access_key_id=iam_keys["AccessKeyId"], | 
					
						
							|  |  |  |             aws_secret_access_key="invalid", | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |     err = ex.value.response["Error"] | 
					
						
							| 
									
										
										
										
											2023-08-07 12:48:48 -04:00
										 |  |  |     assert err["Code"] == "SignatureDoesNotMatch" | 
					
						
							|  |  |  |     assert err["Message"] == ( | 
					
						
							| 
									
										
										
										
											2021-09-25 17:25:27 +00:00
										 |  |  |         "The request signature we calculated does not match the signature you provided. " | 
					
						
							|  |  |  |         "Check your key and signing method." | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def my_head_bucket(bucket, aws_access_key_id, aws_secret_access_key): | 
					
						
							|  |  |  |     s3_client = boto3.client( | 
					
						
							|  |  |  |         "s3", | 
					
						
							|  |  |  |         aws_access_key_id=aws_access_key_id, | 
					
						
							|  |  |  |         aws_secret_access_key=aws_secret_access_key, | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     s3_client.head_bucket(Bucket=bucket) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_iam | 
					
						
							|  |  |  | def create_user_with_access_key_and_policy(user_name="test-user"): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Should create a user with attached policy allowing read/write operations on S3. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     policy_document = { | 
					
						
							|  |  |  |         "Version": "2012-10-17", | 
					
						
							|  |  |  |         "Statement": [{"Effect": "Allow", "Action": "s3:*", "Resource": "*"}], | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Create client and user | 
					
						
							|  |  |  |     client = boto3.client("iam", region_name="us-east-1") | 
					
						
							|  |  |  |     client.create_user(UserName=user_name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Create and attach the policy | 
					
						
							|  |  |  |     policy_arn = client.create_policy( | 
					
						
							|  |  |  |         PolicyName="policy1", PolicyDocument=json.dumps(policy_document) | 
					
						
							|  |  |  |     )["Policy"]["Arn"] | 
					
						
							|  |  |  |     client.attach_user_policy(UserName=user_name, PolicyArn=policy_arn) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Return the access keys | 
					
						
							|  |  |  |     return client.create_access_key(UserName=user_name)["AccessKey"] | 
					
						
							| 
									
										
										
										
											2022-05-12 13:59:47 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_iam | 
					
						
							|  |  |  | @mock_sts | 
					
						
							|  |  |  | def create_role_with_attached_policy_and_assume_it( | 
					
						
							|  |  |  |     role_name, | 
					
						
							|  |  |  |     trust_policy_document, | 
					
						
							|  |  |  |     policy_document, | 
					
						
							|  |  |  |     session_name="session1", | 
					
						
							|  |  |  |     policy_name="policy1", | 
					
						
							|  |  |  | ): | 
					
						
							|  |  |  |     iam_client = boto3.client("iam", region_name="us-east-1") | 
					
						
							|  |  |  |     sts_client = boto3.client("sts", region_name="us-east-1") | 
					
						
							|  |  |  |     role_arn = iam_client.create_role( | 
					
						
							|  |  |  |         RoleName=role_name, AssumeRolePolicyDocument=json.dumps(trust_policy_document) | 
					
						
							|  |  |  |     )["Role"]["Arn"] | 
					
						
							|  |  |  |     policy_arn = iam_client.create_policy( | 
					
						
							|  |  |  |         PolicyName=policy_name, PolicyDocument=json.dumps(policy_document) | 
					
						
							|  |  |  |     )["Policy"]["Arn"] | 
					
						
							|  |  |  |     iam_client.attach_role_policy(RoleName=role_name, PolicyArn=policy_arn) | 
					
						
							|  |  |  |     return sts_client.assume_role(RoleArn=role_arn, RoleSessionName=session_name)[ | 
					
						
							|  |  |  |         "Credentials" | 
					
						
							|  |  |  |     ] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @set_initial_no_auth_action_count(7) | 
					
						
							|  |  |  | @mock_iam | 
					
						
							|  |  |  | @mock_s3 | 
					
						
							|  |  |  | @mock_sts | 
					
						
							|  |  |  | def test_delete_objects_without_access_throws_custom_error(): | 
					
						
							|  |  |  |     if settings.TEST_SERVER_MODE: | 
					
						
							|  |  |  |         raise SkipTest("Auth decorator does not work in server mode") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     role_name = "some-test-role" | 
					
						
							|  |  |  |     bucket_name = "some-test-bucket" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Setup Bucket | 
					
						
							|  |  |  |     client = boto3.client("s3", region_name="us-east-1") | 
					
						
							|  |  |  |     client.create_bucket(Bucket=bucket_name) | 
					
						
							|  |  |  |     client.put_object(Bucket=bucket_name, Key="some/prefix/test_file.txt") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     trust_policy_document = { | 
					
						
							|  |  |  |         "Version": "2012-10-17", | 
					
						
							|  |  |  |         "Statement": { | 
					
						
							|  |  |  |             "Effect": "Allow", | 
					
						
							|  |  |  |             "Principal": {"AWS": f"arn:aws:iam::{ACCOUNT_ID}:root"}, | 
					
						
							|  |  |  |             "Action": "sts:AssumeRole", | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Setup User with the correct access | 
					
						
							|  |  |  |     policy_document = { | 
					
						
							|  |  |  |         "Version": "2012-10-17", | 
					
						
							|  |  |  |         "Statement": [ | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 "Effect": "Allow", | 
					
						
							|  |  |  |                 "Action": ["s3:ListBucket", "s3:GetBucketLocation"], | 
					
						
							|  |  |  |                 "Resource": f"arn:aws:s3:::{bucket_name}", | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 "Effect": "Allow", | 
					
						
							|  |  |  |                 "Action": ["s3:PutObject", "s3:GetObject"], | 
					
						
							|  |  |  |                 "Resource": f"arn:aws:s3:::{bucket_name}/*", | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |         ], | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     credentials = create_role_with_attached_policy_and_assume_it( | 
					
						
							|  |  |  |         role_name, trust_policy_document, policy_document | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     session = boto3.session.Session(region_name="us-east-1") | 
					
						
							|  |  |  |     s3_resource = session.resource( | 
					
						
							|  |  |  |         "s3", | 
					
						
							|  |  |  |         aws_access_key_id=credentials["AccessKeyId"], | 
					
						
							|  |  |  |         aws_secret_access_key=credentials["SecretAccessKey"], | 
					
						
							|  |  |  |         aws_session_token=credentials["SessionToken"], | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     bucket = s3_resource.Bucket(bucket_name) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-07 12:48:48 -04:00
										 |  |  |     # This action is not allowed. | 
					
						
							|  |  |  |     # It should return a 200-response, with the body indicating that we | 
					
						
							|  |  |  |     # do not have access. | 
					
						
							| 
									
										
										
										
											2022-05-12 13:59:47 +00:00
										 |  |  |     response = bucket.objects.filter(Prefix="some/prefix").delete()[0] | 
					
						
							| 
									
										
										
										
											2023-08-07 12:48:48 -04:00
										 |  |  |     assert len(response["Errors"]) == 1 | 
					
						
							| 
									
										
										
										
											2022-05-12 13:59:47 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     error = response["Errors"][0] | 
					
						
							| 
									
										
										
										
											2023-08-07 12:48:48 -04:00
										 |  |  |     assert error["Key"] == "some/prefix/test_file.txt" | 
					
						
							|  |  |  |     assert error["Code"] == "AccessDenied" | 
					
						
							|  |  |  |     assert error["Message"] == "Access Denied" |