2022-09-16 15:59:45 +00:00
import json
2023-08-07 16:48:48 +00:00
import boto3
2022-09-16 15:59:45 +00:00
import requests
import pytest
2023-01-31 11:33:57 +00:00
from botocore . exceptions import ClientError
2022-09-16 15:59:45 +00:00
from moto . moto_server . threaded_moto_server import ThreadedMotoServer
class TestBucketPolicy :
2023-08-07 16:48:48 +00:00
@classmethod
2022-09-16 15:59:45 +00:00
def setup_class ( cls ) :
cls . server = ThreadedMotoServer ( port = " 6000 " , verbose = False )
cls . server . start ( )
2022-10-28 00:37:11 +00:00
def setup_method ( self ) - > None :
2022-09-16 15:59:45 +00:00
self . client = boto3 . client (
2023-03-17 13:05:05 +00:00
" s3 " ,
region_name = " us-east-1 " ,
endpoint_url = " http://localhost:6000 " ,
aws_access_key_id = " ak " ,
aws_secret_access_key = " sk " ,
2022-09-16 15:59:45 +00:00
)
self . client . create_bucket ( Bucket = " mybucket " )
self . client . put_object ( Bucket = " mybucket " , Key = " test_txt " , Body = b " mybytes " )
self . key_name = " http://localhost:6000/mybucket/test_txt "
2022-10-28 00:37:11 +00:00
def teardown_method ( self ) - > None :
2022-09-16 15:59:45 +00:00
self . client . delete_object ( Bucket = " mybucket " , Key = " test_txt " )
self . client . delete_bucket ( Bucket = " mybucket " )
2023-08-07 16:48:48 +00:00
@classmethod
2022-09-16 15:59:45 +00:00
def teardown_class ( cls ) :
cls . server . stop ( )
2023-09-14 12:48:20 +00:00
xfail_reason = " S3 logic for resource-based policy is not yet correctly implemented, see https://github.com/getmoto/moto/pull/6799#issuecomment-1712799688 "
2022-09-16 15:59:45 +00:00
@pytest.mark.parametrize (
" kwargs,status " ,
[
( { } , 200 ) ,
( { " resource " : " arn:aws:s3:::mybucket/test_txt " } , 200 ) ,
2023-09-14 12:48:20 +00:00
pytest . param (
{ " resource " : " arn:aws:s3:::notmybucket/* " } ,
403 ,
marks = pytest . mark . xfail ( reason = xfail_reason ) ,
) ,
pytest . param (
{ " resource " : " arn:aws:s3:::mybucket/other* " } ,
403 ,
marks = pytest . mark . xfail ( reason = xfail_reason ) ,
) ,
2023-02-07 14:24:59 +00:00
( { " resource " : [ " arn:aws:s3:::mybucket " , " arn:aws:s3:::mybucket/* " ] } , 200 ) ,
2023-09-14 12:48:20 +00:00
pytest . param (
2023-02-07 14:24:59 +00:00
{
" resource " : [
" arn:aws:s3:::notmybucket " ,
" arn:aws:s3:::notmybucket/* " ,
]
} ,
403 ,
2023-09-14 12:48:20 +00:00
marks = pytest . mark . xfail ( reason = xfail_reason ) ,
2023-02-07 14:24:59 +00:00
) ,
2023-09-14 12:48:20 +00:00
pytest . param (
2023-02-07 14:24:59 +00:00
{ " resource " : [ " arn:aws:s3:::mybucket " , " arn:aws:s3:::notmybucket/* " ] } ,
403 ,
2023-09-14 12:48:20 +00:00
marks = pytest . mark . xfail ( reason = xfail_reason ) ,
) ,
pytest . param (
{ " effect " : " Deny " } , 403 , marks = pytest . mark . xfail ( reason = xfail_reason )
2023-02-07 14:24:59 +00:00
) ,
2022-09-16 15:59:45 +00:00
] ,
)
2023-01-31 11:33:57 +00:00
def test_block_or_allow_get_object ( self , kwargs , status ) :
2022-09-16 15:59:45 +00:00
self . _put_policy ( * * kwargs )
2023-01-31 11:33:57 +00:00
if status == 200 :
self . client . get_object ( Bucket = " mybucket " , Key = " test_txt " )
else :
with pytest . raises ( ClientError ) :
self . client . get_object ( Bucket = " mybucket " , Key = " test_txt " )
2023-08-07 16:48:48 +00:00
assert requests . get ( self . key_name ) . status_code == status
2022-09-16 15:59:45 +00:00
2023-01-31 11:33:57 +00:00
def test_block_put_object ( self ) :
# Block Put-access
self . _put_policy ( * * { " effect " : " Deny " , " actions " : [ " s3:PutObject " ] } )
# GET still works
self . client . get_object ( Bucket = " mybucket " , Key = " test_txt " )
# But Put (via boto3 or requests) is not allowed
with pytest . raises ( ClientError ) as exc :
self . client . put_object ( Bucket = " mybucket " , Key = " test_txt " , Body = " new data " )
err = exc . value . response [ " Error " ]
2023-08-07 16:48:48 +00:00
assert err [ " Message " ] == " Forbidden "
2023-01-31 11:33:57 +00:00
2023-08-07 16:48:48 +00:00
assert requests . put ( self . key_name ) . status_code == 403
2023-01-31 11:33:57 +00:00
def test_block_all_actions ( self ) :
# Block all access
self . _put_policy ( * * { " effect " : " Deny " , " actions " : [ " s3:* " ] } )
# Nothing works
with pytest . raises ( ClientError ) as exc :
self . client . get_object ( Bucket = " mybucket " , Key = " test_txt " )
err = exc . value . response [ " Error " ]
2023-08-07 16:48:48 +00:00
assert err [ " Message " ] == " Forbidden "
2023-01-31 11:33:57 +00:00
# But Put (via boto3 or requests) is not allowed
with pytest . raises ( ClientError ) as exc :
self . client . put_object ( Bucket = " mybucket " , Key = " test_txt " , Body = " new data " )
err = exc . value . response [ " Error " ]
2023-08-07 16:48:48 +00:00
assert err [ " Message " ] == " Forbidden "
2023-01-31 11:33:57 +00:00
2023-08-07 16:48:48 +00:00
assert requests . get ( self . key_name ) . status_code == 403
assert requests . put ( self . key_name ) . status_code == 403
2023-01-31 11:33:57 +00:00
# Allow access again, because we want to delete the object during teardown
self . _put_policy ( * * { " effect " : " Allow " , " actions " : [ " s3:* " ] } )
def test_block_all_with_different_principal ( self ) :
# Block all access for principal y
self . _put_policy ( * * { " effect " : " Deny " , " actions " : [ " s3:* " ] , " principal " : " y " } )
# Everything works - Moto only blocks access for principal *
self . client . get_object ( Bucket = " mybucket " , Key = " test_txt " )
self . client . put_object ( Bucket = " mybucket " , Key = " test_txt " , Body = " new data " )
2022-09-16 15:59:45 +00:00
def _put_policy (
2023-01-31 11:33:57 +00:00
self ,
resource = " arn:aws:s3:::mybucket/* " ,
effect = " Allow " ,
actions = None ,
principal = None ,
2022-09-16 15:59:45 +00:00
) :
policy = {
" Version " : " 2012-10-17 " ,
" Statement " : [
{
" Effect " : effect ,
2023-01-31 11:33:57 +00:00
" Principal " : principal or " * " ,
2022-09-16 15:59:45 +00:00
" Action " : actions or [ " s3:GetObject " ] ,
" Resource " : resource ,
}
] ,
}
self . client . put_bucket_policy ( Bucket = " mybucket " , Policy = json . dumps ( policy ) )