From 7831257ed52709ce7f6b5c0ed98305e84f82d2b0 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 4 Feb 2026 16:23:20 -0800 Subject: [PATCH] s3: allow single Statement object in policy document (#8212) * s3: allow single Statement object in policy document Fixes #8201 * s3: add unit test for single Statement object in policy * s3: improve error message for malformed PolicyDocument.Statement * s3: simplify error message for malformed PolicyDocument.Statement --- weed/s3api/policy_engine/engine_test.go | 12 ++++++++++ weed/s3api/policy_engine/types.go | 31 +++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/weed/s3api/policy_engine/engine_test.go b/weed/s3api/policy_engine/engine_test.go index f8297b56b..77e2b5395 100644 --- a/weed/s3api/policy_engine/engine_test.go +++ b/weed/s3api/policy_engine/engine_test.go @@ -286,6 +286,18 @@ func TestPolicyValidation(t *testing.T) { }`, expectError: false, }, + { + name: "Valid policy with single statement object", + policyJSON: `{ + "Version": "2012-10-17", + "Statement": { + "Effect": "Allow", + "Action": "s3:GetObject", + "Resource": "arn:aws:s3:::test-bucket/*" + } + }`, + expectError: false, + }, { name: "Invalid version", policyJSON: `{ diff --git a/weed/s3api/policy_engine/types.go b/weed/s3api/policy_engine/types.go index b358d4c2c..0af425de4 100644 --- a/weed/s3api/policy_engine/types.go +++ b/weed/s3api/policy_engine/types.go @@ -88,6 +88,37 @@ type PolicyDocument struct { Statement []PolicyStatement `json:"Statement"` } +// UnmarshalJSON implements json.Unmarshaler for PolicyDocument +func (p *PolicyDocument) UnmarshalJSON(data []byte) error { + type Alias PolicyDocument + aux := &struct { + Statement json.RawMessage `json:"Statement"` + *Alias + }{ + Alias: (*Alias)(p), + } + + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + // Try unmarshaling as []PolicyStatement first + var statements []PolicyStatement + if err := json.Unmarshal(aux.Statement, &statements); err == nil { + p.Statement = statements + return nil + } + + // Try unmarshaling as single PolicyStatement + var statement PolicyStatement + if err := json.Unmarshal(aux.Statement, &statement); err == nil { + p.Statement = []PolicyStatement{statement} + return nil + } + + return fmt.Errorf("Statement must be an array or a single object") +} + // PolicyStatement represents a single policy statement type PolicyStatement struct { Sid string `json:"Sid,omitempty"`