feat: implementes GetBucketPolicyStatus s3 action

Closes #1454

Adds the implementation of [S3 GetBucketPolicyStatus action](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketPolicyStatus.html). The implementation goes to front-end. Front-End loads the bucket policy and checks if it grants public access to all users.

A bucket policy document `is public` only when `Principal` contains `*`(all users): only when it grants access to `ALL` users.
This commit is contained in:
niksis02
2025-08-25 21:48:06 +04:00
parent 9992e341da
commit d90944afd1
9 changed files with 331 additions and 16 deletions

View File

@@ -92,12 +92,12 @@ func (bp *BucketPolicy) isAllowed(principal string, action Action, resource stri
return isAllowed
}
// isPublic checks if the bucket policy statements contain
// an entity granting public access
func (bp *BucketPolicy) isPublic(resource string, action Action) bool {
// IsPublicFor checks if the bucket policy statements contain
// an entity granting public access to the given resource and action
func (bp *BucketPolicy) isPublicFor(resource string, action Action) bool {
var isAllowed bool
for _, statement := range bp.Statement {
if statement.isPublic(resource, action) {
if statement.isPublicFor(resource, action) {
switch statement.Effect {
case BucketPolicyAccessTypeAllow:
isAllowed = true
@@ -110,6 +110,18 @@ func (bp *BucketPolicy) isPublic(resource string, action Action) bool {
return isAllowed
}
// IsPublic checks if one of bucket policy statments grant
// public access to ALL users
func (bp *BucketPolicy) IsPublic() bool {
for _, statement := range bp.Statement {
if statement.isPublic() {
return true
}
}
return false
}
type BucketPolicyItem struct {
Effect BucketPolicyAccessType `json:"Effect"`
Principals Principals `json:"Principal"`
@@ -155,9 +167,16 @@ func (bpi *BucketPolicyItem) findMatch(principal string, action Action, resource
return false
}
// isPublic checks if the bucket policy statemant grants public access
func (bpi *BucketPolicyItem) isPublic(resource string, action Action) bool {
return bpi.Principals.IsPublic() && bpi.Actions.FindMatch(action) && bpi.Resources.FindMatch(resource)
// isPublicFor checks if the bucket policy statemant grants public access
// for given resource and action
func (bpi *BucketPolicyItem) isPublicFor(resource string, action Action) bool {
return bpi.Principals.isPublic() && bpi.Actions.FindMatch(action) && bpi.Resources.FindMatch(resource)
}
// isPublic checks if the statement grants public access
// to ALL users
func (bpi *BucketPolicyItem) isPublic() bool {
return bpi.Principals.isPublic()
}
func getMalformedPolicyError(err error) error {
@@ -168,17 +187,27 @@ func getMalformedPolicyError(err error) error {
}
}
// ParsePolicyDocument parses raw bytes to 'BucketPolicy'
func ParsePolicyDocument(data []byte) (*BucketPolicy, error) {
var policy BucketPolicy
if err := json.Unmarshal(data, &policy); err != nil {
var pe policyErr
if errors.As(err, &pe) {
return nil, getMalformedPolicyError(err)
}
return nil, getMalformedPolicyError(policyErrInvalidPolicy)
}
return &policy, nil
}
func ValidatePolicyDocument(policyBin []byte, bucket string, iam IAMService) error {
if len(policyBin) == 0 || policyBin[0] != '{' {
return getMalformedPolicyError(policyErrInvalidFirstChar)
}
var policy BucketPolicy
if err := json.Unmarshal(policyBin, &policy); err != nil {
var pe policyErr
if errors.As(err, &pe) {
return getMalformedPolicyError(err)
}
return getMalformedPolicyError(policyErrInvalidPolicy)
policy, err := ParsePolicyDocument(policyBin)
if err != nil {
return err
}
if len(policy.Statement) == 0 {
@@ -222,7 +251,7 @@ func VerifyPublicBucketPolicy(policy []byte, bucket, object string, action Actio
resource += "/" + object
}
if !bucketPolicy.isPublic(resource, action) {
if !bucketPolicy.isPublicFor(resource, action) {
return ErrAccessDenied
}

View File

@@ -86,6 +86,7 @@ const (
GetAccelerateConfigurationAction Action = "s3:GetAccelerateConfiguration"
PutBucketWebsiteAction Action = "s3:PutBucketWebsite"
GetBucketWebsiteAction Action = "s3:GetBucketWebsite"
GetBucketPolicyStatusAction Action = "s3:GetBucketPolicyStatus"
AllActions Action = "s3:*"
)
@@ -155,6 +156,7 @@ var supportedActionList = map[Action]struct{}{
GetAccelerateConfigurationAction: {},
PutBucketWebsiteAction: {},
GetBucketWebsiteAction: {},
GetBucketPolicyStatusAction: {},
AllActions: {},
}

View File

@@ -124,7 +124,7 @@ func (p Principals) Contains(userAccess string) bool {
// Bucket policy grants public access, if it contains
// a wildcard match to all the users
func (p Principals) IsPublic() bool {
func (p Principals) isPublic() bool {
_, ok := p["*"]
return ok
}