Feature/32 extend bucket policy #33
3 changed files with 164 additions and 0 deletions
|
@ -9,6 +9,7 @@ This document outlines major changes between releases.
|
|||
- Billing metrics (TrueCloudLab#5)
|
||||
- Multiple configs support (TrueCloudLab#21)
|
||||
- Bucket name resolving policy (TrueCloudLab#25)
|
||||
- Support string `Action` and `Resource` fields in `bucketPolicy.Statement` (TrueCloudLab#32)
|
||||
|
||||
### Changed
|
||||
- Update neo-go to v0.101.0 (#14)
|
||||
|
|
|
@ -158,6 +158,90 @@ func (s ServiceRecord) ToEACLRecord() *eacl.Record {
|
|||
return serviceRecord
|
||||
}
|
||||
|
||||
var (
|
||||
errInvalidStatement = stderrors.New("invalid statement")
|
||||
errInvalidPrincipal = stderrors.New("invalid principal")
|
||||
)
|
||||
|
||||
func (s *statement) UnmarshalJSON(data []byte) error {
|
||||
var statementMap map[string]interface{}
|
||||
if err := json.Unmarshal(data, &statementMap); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sidField, ok := statementMap["Sid"]
|
||||
if ok {
|
||||
if s.Sid, ok = sidField.(string); !ok {
|
||||
return errInvalidStatement
|
||||
}
|
||||
}
|
||||
|
||||
effectField, ok := statementMap["Effect"]
|
||||
if ok {
|
||||
if s.Effect, ok = effectField.(string); !ok {
|
||||
return errInvalidStatement
|
||||
}
|
||||
}
|
||||
|
||||
principalField, ok := statementMap["Principal"]
|
||||
if ok {
|
||||
principalMap, ok := principalField.(map[string]interface{})
|
||||
if !ok {
|
||||
return errInvalidPrincipal
|
||||
}
|
||||
|
||||
awsField, ok := principalMap["AWS"]
|
||||
if ok {
|
||||
if s.Principal.AWS, ok = awsField.(string); !ok {
|
||||
return fmt.Errorf("%w: 'AWS' field must be string", errInvalidPrincipal)
|
||||
}
|
||||
}
|
||||
|
||||
canonicalUserField, ok := principalMap["CanonicalUser"]
|
||||
if ok {
|
||||
if s.Principal.CanonicalUser, ok = canonicalUserField.(string); !ok {
|
||||
return errInvalidPrincipal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
actionField, ok := statementMap["Action"]
|
||||
if ok {
|
||||
switch actionField := actionField.(type) {
|
||||
case []interface{}:
|
||||
s.Action = make([]string, len(actionField))
|
||||
for i, action := range actionField {
|
||||
if s.Action[i], ok = action.(string); !ok {
|
||||
return errInvalidStatement
|
||||
}
|
||||
}
|
||||
case string:
|
||||
s.Action = []string{actionField}
|
||||
default:
|
||||
return errInvalidStatement
|
||||
}
|
||||
}
|
||||
|
||||
resourceField, ok := statementMap["Resource"]
|
||||
if ok {
|
||||
switch resourceField := resourceField.(type) {
|
||||
case []interface{}:
|
||||
s.Resource = make([]string, len(resourceField))
|
||||
for i, action := range resourceField {
|
||||
if s.Resource[i], ok = action.(string); !ok {
|
||||
return errInvalidStatement
|
||||
}
|
||||
}
|
||||
case string:
|
||||
s.Resource = []string{resourceField}
|
||||
default:
|
||||
return errInvalidStatement
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *handler) GetBucketACLHandler(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := api.GetReqInfo(r.Context())
|
||||
|
||||
|
|
|
@ -1352,6 +1352,85 @@ func TestBucketPolicy(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestBucketPolicyUnmarshal(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
policy string
|
||||
}{
|
||||
{
|
||||
name: "action/resource array",
|
||||
policy: `
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [{
|
||||
"Principal": {
|
||||
"AWS": "arn:aws:iam::111122223333:role/JohnDoe"
|
||||
},
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:GetObject",
|
||||
"s3:GetObjectVersion"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::DOC-EXAMPLE-BUCKET/*",
|
||||
"arn:aws:s3:::DOC-EXAMPLE-BUCKET2/*"
|
||||
]
|
||||
}]
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "action/resource string",
|
||||
policy: `
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [{
|
||||
"Principal": {
|
||||
"AWS": "arn:aws:iam::111122223333:role/JohnDoe"
|
||||
},
|
||||
"Effect": "Deny",
|
||||
"Action": "s3:GetObject",
|
||||
"Resource": "arn:aws:s3:::DOC-EXAMPLE-BUCKET/*"
|
||||
}]
|
||||
}
|
||||
`,
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
bktPolicy := &bucketPolicy{}
|
||||
err := json.Unmarshal([]byte(tc.policy), bktPolicy)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPutBucketPolicy(t *testing.T) {
|
||||
bktPolicy := `
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [{
|
||||
"Principal": {
|
||||
"AWS": "*"
|
||||
},
|
||||
"Effect": "Deny",
|
||||
"Action": "s3:GetObject",
|
||||
"Resource": "arn:aws:s3:::bucket-for-policy/*"
|
||||
}]
|
||||
}
|
||||
`
|
||||
hc := prepareHandlerContext(t)
|
||||
bktName := "bucket-for-policy"
|
||||
|
||||
box, _ := createAccessBox(t)
|
||||
createBucket(t, hc, bktName, box)
|
||||
|
||||
w, r := prepareTestPayloadRequest(hc, bktName, "", bytes.NewReader([]byte(bktPolicy)))
|
||||
ctx := context.WithValue(r.Context(), api.BoxData, box)
|
||||
r = r.WithContext(ctx)
|
||||
hc.Handler().PutBucketPolicyHandler(w, r)
|
||||
assertStatus(hc.t, w, http.StatusOK)
|
||||
}
|
||||
|
||||
func getBucketPolicy(hc *handlerContext, bktName string) *bucketPolicy {
|
||||
w, r := prepareTestRequest(hc, bktName, "", nil)
|
||||
hc.Handler().GetBucketPolicyHandler(w, r)
|
||||
|
|
Loading…
Reference in a new issue