Skip to content

Commit 13e7fad

Browse files
authored
feat(object): support the group bucket ACL (#3185)
* feat(object): support the group bucket ACL * update doc
1 parent 8d26f55 commit 13e7fad

File tree

6 files changed

+1119
-749
lines changed

6 files changed

+1119
-749
lines changed

docs/resources/object_bucket_acl.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,9 @@ The `owner` configuration block supports the following arguments:
114114

115115
The `grantee` configuration block supports the following arguments:
116116

117-
* `id` - (Required) The canonical user ID of the grantee.
118-
* `type` - (Required) Type of grantee. Valid values: CanonicalUser.
117+
* `id` - (Optional) The canonical user ID of the grantee.
118+
* `type` - (Required) Type of grantee. Valid values: CanonicalUser, Group.
119+
* `uri` - (Optional) The uri of the grantee if type is Group.
119120

120121
## Attributes Reference
121122

internal/services/object/bucket_acl.go

Lines changed: 67 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ import (
2323

2424
const (
2525
BucketACLSeparator = "/"
26+
27+
// AWS predefined group URIs for bucket ACL grants
28+
AuthenticatedUsersURI = "http://acs.amazonaws.com/groups/global/AuthenticatedUsers"
29+
AllUsersURI = "http://acs.amazonaws.com/groups/global/AllUsers"
2630
)
2731

2832
func ResourceBucketACL() *schema.Resource {
@@ -60,17 +64,26 @@ func ResourceBucketACL() *schema.Resource {
6064
Type: schema.TypeString,
6165
Computed: true,
6266
},
67+
"uri": {
68+
Type: schema.TypeString,
69+
Optional: true,
70+
Description: "The uri of the grantee if you are granting permissions to a predefined group.",
71+
ValidateFunc: validation.StringInSlice([]string{
72+
AuthenticatedUsersURI,
73+
AllUsersURI,
74+
}, false),
75+
},
6376
"id": {
6477
Type: schema.TypeString,
65-
Required: true,
78+
Optional: true,
6679
Description: "The project ID owner of the grantee.",
6780
ValidateDiagFunc: verify.IsUUID(),
6881
},
6982
"type": {
7083
Type: schema.TypeString,
71-
Required: true,
72-
Description: "Type of grantee. Valid values: `CanonicalUser`",
73-
ValidateFunc: validation.StringInSlice([]string{string(s3Types.TypeCanonicalUser)}, false),
84+
Optional: true,
85+
Description: "Type of grantee. Valid values: `CanonicalUser`, `Group`",
86+
ValidateFunc: validation.StringInSlice([]string{string(s3Types.TypeCanonicalUser), string(s3Types.TypeGroup)}, false),
7487
},
7588
},
7689
},
@@ -183,7 +196,12 @@ func resourceBucketACLCreate(ctx context.Context, d *schema.ResourceData, m any)
183196
}
184197

185198
if v, ok := d.GetOk("access_control_policy"); ok && len(v.([]any)) > 0 && v.([]any)[0] != nil {
186-
input.AccessControlPolicy = expandBucketACLAccessControlPolicy(v.([]any))
199+
accessControlPolicy, err := expandAndValidateBucketACLAccessControlPolicy(v.([]any))
200+
if err != nil {
201+
return diag.FromErr(err)
202+
}
203+
204+
input.AccessControlPolicy = accessControlPolicy
187205
}
188206

189207
out, err := conn.PutBucketAcl(ctx, input)
@@ -198,30 +216,35 @@ func resourceBucketACLCreate(ctx context.Context, d *schema.ResourceData, m any)
198216
return resourceBucketACLRead(ctx, d, m)
199217
}
200218

201-
func expandBucketACLAccessControlPolicy(l []any) *s3Types.AccessControlPolicy {
219+
func expandAndValidateBucketACLAccessControlPolicy(l []any) (*s3Types.AccessControlPolicy, error) {
202220
if len(l) == 0 || l[0] == nil {
203-
return nil
221+
return nil, nil
204222
}
205223

206224
tfMap, ok := l[0].(map[string]any)
207225
if !ok {
208-
return nil
226+
return nil, nil
209227
}
210228

211229
result := &s3Types.AccessControlPolicy{}
212230

213231
if v, ok := tfMap["grant"].(*schema.Set); ok && v.Len() > 0 {
214-
result.Grants = expandBucketACLAccessControlPolicyGrants(v.List())
232+
grants, err := expandAndValidateBucketACLAccessControlPolicyGrants(v.List())
233+
if err != nil {
234+
return nil, err
235+
}
236+
237+
result.Grants = grants
215238
}
216239

217240
if v, ok := tfMap["owner"].([]any); ok && len(v) > 0 && v[0] != nil {
218241
result.Owner = expandBucketACLAccessControlPolicyOwner(v)
219242
}
220243

221-
return result
244+
return result, nil
222245
}
223246

224-
func expandBucketACLAccessControlPolicyGrants(l []any) []s3Types.Grant {
247+
func expandAndValidateBucketACLAccessControlPolicyGrants(l []any) ([]s3Types.Grant, error) {
225248
grants := make([]s3Types.Grant, 0, len(l))
226249

227250
for _, tfMapRaw := range l {
@@ -233,7 +256,12 @@ func expandBucketACLAccessControlPolicyGrants(l []any) []s3Types.Grant {
233256
grant := s3Types.Grant{}
234257

235258
if v, ok := tfMap["grantee"].([]any); ok && len(v) > 0 && v[0] != nil {
236-
grant.Grantee = expandBucketACLAccessControlPolicyGrantsGrantee(v)
259+
grantee, err := expandAndValidateBucketACLAccessControlPolicyGrantsGrantee(v)
260+
if err != nil {
261+
return nil, err
262+
}
263+
264+
grant.Grantee = grantee
237265
}
238266

239267
if v, ok := tfMap["permission"].(string); ok && v != "" {
@@ -243,17 +271,17 @@ func expandBucketACLAccessControlPolicyGrants(l []any) []s3Types.Grant {
243271
grants = append(grants, grant)
244272
}
245273

246-
return grants
274+
return grants, nil
247275
}
248276

249-
func expandBucketACLAccessControlPolicyGrantsGrantee(l []any) *s3Types.Grantee {
277+
func expandAndValidateBucketACLAccessControlPolicyGrantsGrantee(l []any) (*s3Types.Grantee, error) {
250278
if len(l) == 0 || l[0] == nil {
251-
return nil
279+
return nil, nil
252280
}
253281

254282
tfMap, ok := l[0].(map[string]any)
255283
if !ok {
256-
return nil
284+
return nil, nil
257285
}
258286

259287
result := &s3Types.Grantee{}
@@ -262,11 +290,23 @@ func expandBucketACLAccessControlPolicyGrantsGrantee(l []any) *s3Types.Grantee {
262290
result.ID = buildBucketOwnerID(aws.String(v))
263291
}
264292

293+
if v, ok := tfMap["uri"].(string); ok && v != "" {
294+
result.URI = aws.String(v)
295+
}
296+
265297
if v, ok := tfMap["type"].(string); ok && v != "" {
266298
result.Type = s3Types.Type(v)
267299
}
268300

269-
return result
301+
if result.Type == s3Types.TypeCanonicalUser && result.ID == nil {
302+
return nil, errors.New("id is required when grantee type is CanonicalUser")
303+
}
304+
305+
if result.Type == s3Types.TypeGroup && result.URI == nil {
306+
return nil, errors.New("uri is required when grantee type is Group")
307+
}
308+
309+
return result, nil
270310
}
271311

272312
func expandBucketACLAccessControlPolicyOwner(l []any) *s3Types.Owner {
@@ -345,6 +385,10 @@ func flattenBucketACLAccessControlPolicyGrantsGrantee(grantee *s3Types.Grantee)
345385
m["display_name"] = NormalizeOwnerID(grantee.DisplayName)
346386
}
347387

388+
if grantee.URI != nil {
389+
m["uri"] = *grantee.URI
390+
}
391+
348392
if grantee.ID != nil {
349393
m["id"] = NormalizeOwnerID(grantee.ID)
350394
}
@@ -451,7 +495,12 @@ func resourceBucketACLUpdate(ctx context.Context, d *schema.ResourceData, m any)
451495
}
452496

453497
if d.HasChange("access_control_policy") {
454-
input.AccessControlPolicy = expandBucketACLAccessControlPolicy(d.Get("access_control_policy").([]any))
498+
accessControlPolicy, err := expandAndValidateBucketACLAccessControlPolicy(d.Get("access_control_policy").([]any))
499+
if err != nil {
500+
return diag.FromErr(err)
501+
}
502+
503+
input.AccessControlPolicy = accessControlPolicy
455504
}
456505

457506
_, err = conn.PutBucketAcl(ctx, input)

internal/services/object/bucket_acl_test.go

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,7 @@ func TestAccObjectBucketACL_Grantee(t *testing.T) {
8080

8181
testBucketName := sdkacctest.RandomWithPrefix("tf-tests-scw-object-acl-grantee")
8282

83-
ownerID := "105bdce1-64c0-48ab-899d-868455867ecf"
84-
ownerIDChild := "50ab77d5-56bd-4981-a118-4e0fa5309b59"
83+
ownerID := "105bdce1-64c0-48ab-899d-868455867ecf" // scaleway-dev-tools-org
8584
resource.Test(t, resource.TestCase{
8685
PreCheck: func() { acctest.PreCheck(t) },
8786
ProviderFactories: tt.ProviderFactories,
@@ -128,9 +127,13 @@ func TestAccObjectBucketACL_Grantee(t *testing.T) {
128127
Config: fmt.Sprintf(`
129128
resource "scaleway_object_bucket" "main" {
130129
name = "%[1]s"
131-
region = "%[4]s"
130+
region = "%[3]s"
132131
}
133-
132+
133+
data "scaleway_iam_user" "devtool" {
134+
135+
}
136+
134137
resource "scaleway_object_bucket_acl" "main" {
135138
bucket = scaleway_object_bucket.main.id
136139
access_control_policy {
@@ -141,29 +144,44 @@ func TestAccObjectBucketACL_Grantee(t *testing.T) {
141144
}
142145
permission = "FULL_CONTROL"
143146
}
144-
147+
145148
grant {
146149
grantee {
147150
id = "%[2]s"
148151
type = "CanonicalUser"
149152
}
150153
permission = "WRITE"
151154
}
152-
155+
153156
grant {
154157
grantee {
155-
id = "%[3]s"
158+
id = data.scaleway_iam_user.devtool.id
156159
type = "CanonicalUser"
157160
}
158161
permission = "FULL_CONTROL"
159162
}
160-
163+
164+
grant {
165+
grantee {
166+
uri = "%[4]s"
167+
type = "Group"
168+
}
169+
permission = "READ_ACP"
170+
}
171+
172+
grant {
173+
grantee {
174+
uri = "%[5]s"
175+
type = "Group"
176+
}
177+
permission = "READ"
178+
}
161179
owner {
162180
id = "%[2]s"
163181
}
164182
}
165183
}
166-
`, testBucketName, ownerID, ownerIDChild, objectTestsMainRegion),
184+
`, testBucketName, ownerID, objectTestsMainRegion, object.AuthenticatedUsersURI, object.AllUsersURI),
167185
Check: resource.ComposeTestCheckFunc(
168186
objectchecks.CheckBucketExists(tt, "scaleway_object_bucket.main", true),
169187
resource.TestCheckResourceAttr("scaleway_object_bucket_acl.main", "bucket", testBucketName),
@@ -263,7 +281,7 @@ func TestAccObjectBucketACL_WithBucketName(t *testing.T) {
263281
name = "%s"
264282
region = "%s"
265283
}
266-
284+
267285
resource "scaleway_object_bucket_acl" "main" {
268286
bucket = scaleway_object_bucket.main.name
269287
acl = "public-read"

internal/services/object/object.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ func objectIsPublic(acl *s3.GetObjectAclOutput) bool {
445445
for _, grant := range acl.Grants {
446446
if grant.Grantee != nil &&
447447
grant.Grantee.Type == s3Types.TypeGroup &&
448-
*grant.Grantee.URI == "http://acs.amazonaws.com/groups/global/AllUsers" {
448+
*grant.Grantee.URI == AllUsersURI {
449449
return true
450450
}
451451
}

0 commit comments

Comments
 (0)