Skip to content

Commit 0af71d6

Browse files
committed
HADOOP-17822. fs.s3a.acl.default not working after S3A Audit feature
Fixes the regression caused by HADOOP-17511 by moving where the cannedACL properties are inited -so guaranteeing that they are valid before the RequestFactory is created. Adds * A unit test in TestRequestFactory to verify the ACLs are set on all file write operations * A new ITestS3ACannedACLs test which verifies that ACLs really do get all the way through. Change-Id: Ic96bc93edfc182d88f6d4ebc43c594a29f94d2cf
1 parent 63dfd84 commit 0af71d6

File tree

8 files changed

+160
-3
lines changed

8 files changed

+160
-3
lines changed

hadoop-common-project/hadoop-common/src/main/resources/core-default.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1550,7 +1550,9 @@
15501550
<name>fs.s3a.acl.default</name>
15511551
<description>Set a canned ACL for newly created and copied objects. Value may be Private,
15521552
PublicRead, PublicReadWrite, AuthenticatedRead, LogDeliveryWrite, BucketOwnerRead,
1553-
or BucketOwnerFullControl.</description>
1553+
or BucketOwnerFullControl.
1554+
If set, caller IAM role must have "s3:PutObjectAcl" permission on the bucket.
1555+
</description>
15541556
</property>
15551557

15561558
<property>

hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/S3AFileSystem.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,6 @@ public void initialize(URI name, Configuration originalConf)
479479

480480
initTransferManager();
481481

482-
initCannedAcls(conf);
483482

484483
// This initiates a probe against S3 for the bucket existing.
485484
doBucketProbing();
@@ -886,6 +885,10 @@ protected RequestFactory createRequestFactory() {
886885
UPLOAD_PART_COUNT_LIMIT);
887886
}
888887

888+
// ACLs; this is passed to the
889+
// request factory.
890+
initCannedAcls(getConf());
891+
889892
return RequestFactoryImpl.builder()
890893
.withBucket(requireNonNull(bucket))
891894
.withCannedACL(getCannedACL())

hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/auth/RolePolicies.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ private RolePolicies() {
248248
Collections.unmodifiableList(Arrays.asList(new String[]{
249249
S3_ALL_GET,
250250
S3_PUT_OBJECT,
251+
S3_PUT_OBJECT_ACL,
251252
S3_DELETE_OBJECT,
252253
S3_ABORT_MULTIPART_UPLOAD,
253254
}));
@@ -262,6 +263,7 @@ private RolePolicies() {
262263
public static final List<String> S3_PATH_WRITE_OPERATIONS =
263264
Collections.unmodifiableList(Arrays.asList(new String[]{
264265
S3_PUT_OBJECT,
266+
S3_PUT_OBJECT_ACL,
265267
S3_DELETE_OBJECT,
266268
S3_ABORT_MULTIPART_UPLOAD
267269
}));
@@ -274,6 +276,7 @@ private RolePolicies() {
274276
Collections.unmodifiableList(Arrays.asList(new String[]{
275277
S3_ALL_GET,
276278
S3_PUT_OBJECT,
279+
S3_PUT_OBJECT_ACL,
277280
S3_DELETE_OBJECT,
278281
S3_ABORT_MULTIPART_UPLOAD,
279282
}));
@@ -375,6 +378,7 @@ private RolePolicies() {
375378
STATEMENT_ALL_S3_GET_BUCKET_LOCATION
376379
);
377380

381+
public static final String CANNED_ACL_LOG = "LogDeliveryWrite";
378382
public static Statement allowS3GuardClientOperations(String tableArn) {
379383
return statement(true,
380384
tableArn,

hadoop-tools/hadoop-aws/src/site/markdown/tools/hadoop-aws/index.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -944,7 +944,9 @@ options are covered in [Testing](./testing.md).
944944
<name>fs.s3a.acl.default</name>
945945
<description>Set a canned ACL for newly created and copied objects. Value may be Private,
946946
PublicRead, PublicReadWrite, AuthenticatedRead, LogDeliveryWrite, BucketOwnerRead,
947-
or BucketOwnerFullControl.</description>
947+
or BucketOwnerFullControl.
948+
If set, caller IAM role must have "s3:PutObjectAcl" permission on the bucket.
949+
</description>
948950
</property>
949951

950952
<property>
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.apache.hadoop.fs.s3a;
20+
21+
import java.util.List;
22+
23+
import com.amazonaws.services.s3.AmazonS3;
24+
import com.amazonaws.services.s3.model.AccessControlList;
25+
import com.amazonaws.services.s3.model.Grant;
26+
import com.amazonaws.services.s3.model.GroupGrantee;
27+
import com.amazonaws.services.s3.model.Permission;
28+
import org.assertj.core.api.Assertions;
29+
import org.junit.Test;
30+
import org.slf4j.Logger;
31+
import org.slf4j.LoggerFactory;
32+
33+
import org.apache.hadoop.conf.Configuration;
34+
import org.apache.hadoop.fs.Path;
35+
import org.apache.hadoop.fs.contract.ContractTestUtils;
36+
import org.apache.hadoop.fs.s3a.audit.S3AAuditConstants;
37+
import org.apache.hadoop.fs.s3a.impl.StoreContext;
38+
39+
import static org.apache.hadoop.fs.s3a.Constants.CANNED_ACL;
40+
import static org.apache.hadoop.fs.s3a.S3ATestUtils.removeBaseAndBucketOverrides;
41+
42+
/**
43+
* Tests of ACL handling in the FS.
44+
* If you enable logging, the grantee list adds
45+
* Grant [grantee=GroupGrantee [http://acs.amazonaws.com/groups/s3/LogDelivery], permission=WRITE]
46+
*/
47+
public class ITestS3ACannedACLs extends AbstractS3ATestBase {
48+
49+
private static final Logger LOG =
50+
LoggerFactory.getLogger(ITestS3ACannedACLs.class);
51+
52+
@Override
53+
protected Configuration createConfiguration() {
54+
Configuration conf = super.createConfiguration();
55+
removeBaseAndBucketOverrides(conf,
56+
CANNED_ACL);
57+
58+
conf.set(CANNED_ACL, LOG_DELIVERY_WRITE);
59+
// needed because of direct calls made
60+
conf.setBoolean(S3AAuditConstants.REJECT_OUT_OF_SPAN_OPERATIONS, false);
61+
return conf;
62+
}
63+
64+
@Test
65+
public void testCreatedObjectsHaveACLs() throws Throwable {
66+
S3AFileSystem fs = getFileSystem();
67+
Path dir = methodPath();
68+
fs.mkdirs(dir);
69+
assertObjectHasLoggingGrant(dir, false);
70+
Path path = new Path(dir, "1");
71+
ContractTestUtils.touch(fs, path);
72+
assertObjectHasLoggingGrant(path, true);
73+
Path path2 = new Path(dir, "2");
74+
fs.rename(path, path2);
75+
assertObjectHasLoggingGrant(path2, true);
76+
}
77+
78+
/**
79+
* Assert that a given object granded the AWS logging service
80+
* write access.
81+
* Logs all the grants.
82+
* @param path path
83+
* @param isFile is this a file or a directory?
84+
*/
85+
private void assertObjectHasLoggingGrant(Path path, boolean isFile) {
86+
S3AFileSystem fs = getFileSystem();
87+
88+
StoreContext storeContext = fs.createStoreContext();
89+
AmazonS3 s3 = fs.getAmazonS3ClientForTesting("acls");
90+
String key = storeContext.pathToKey(path);
91+
if (!isFile) {
92+
key = key + "/";
93+
}
94+
AccessControlList acl = s3.getObjectAcl(storeContext.getBucket(),
95+
key);
96+
List<Grant> grants = acl.getGrantsAsList();
97+
for (Grant grant : grants) {
98+
LOG.info("{}", grant.toString());
99+
}
100+
Grant loggingGrant = new Grant(GroupGrantee.LogDelivery, Permission.Write);
101+
Assertions.assertThat(grants)
102+
.describedAs("ACL grants of object %s", path)
103+
.contains(loggingGrant);
104+
}
105+
}

hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/S3ATestConstants.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,11 @@ public interface S3ATestConstants {
178178
"fs.s3a.s3guard.test.dynamo.table.prefix";
179179
String TEST_S3GUARD_DYNAMO_TABLE_PREFIX_DEFAULT = "s3guard.test.";
180180

181+
/**
182+
* ACL for S3 Logging; used in some tests: {@value}.
183+
*/
184+
String LOG_DELIVERY_WRITE = "LogDeliveryWrite";
185+
181186
/**
182187
* Timeout in Milliseconds for standard tests: {@value}.
183188
*/

hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/auth/delegation/ITestSessionDelegationInFileystem.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ protected Configuration createConfiguration() {
156156
}
157157
// set the YARN RM up for YARN tests.
158158
conf.set(YarnConfiguration.RM_PRINCIPAL, YARN_RM);
159+
// turn on ACLs so as to verify role DT permissions include
160+
// write access.
161+
conf.set(CANNED_ACL, LOG_DELIVERY_WRITE);
159162
return conf;
160163
}
161164

hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/impl/TestRequestFactory.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@
2424
import java.util.concurrent.atomic.AtomicLong;
2525

2626
import com.amazonaws.AmazonWebServiceRequest;
27+
import com.amazonaws.services.s3.model.CannedAccessControlList;
2728
import com.amazonaws.services.s3.model.ObjectListing;
2829
import com.amazonaws.services.s3.model.ObjectMetadata;
30+
import org.assertj.core.api.Assertions;
2931
import org.junit.Test;
3032
import org.slf4j.Logger;
3133
import org.slf4j.LoggerFactory;
@@ -73,6 +75,37 @@ public void testRequestFactoryWithEncryption() throws Throwable {
7375
createFactoryObjects(factory);
7476
}
7577

78+
/**
79+
* Verify ACLs are passed from the factory to the requests.
80+
*/
81+
@Test
82+
public void testRequestFactoryWithCannedACL() throws Throwable {
83+
CannedAccessControlList acl = CannedAccessControlList.BucketOwnerFullControl;
84+
RequestFactory factory = RequestFactoryImpl.builder()
85+
.withBucket("bucket")
86+
.withCannedACL(acl)
87+
.build();
88+
String path = "path";
89+
String path2 = "path2";
90+
ObjectMetadata md = factory.newObjectMetadata(128);
91+
Assertions.assertThat(
92+
factory.newPutObjectRequest(path, md,
93+
new ByteArrayInputStream(new byte[0]))
94+
.getCannedAcl())
95+
.describedAs("ACL of PUT")
96+
.isEqualTo(acl);
97+
Assertions.assertThat(factory.newCopyObjectRequest(path, path2, md)
98+
.getCannedAccessControlList())
99+
.describedAs("ACL of COPY")
100+
.isEqualTo(acl);
101+
Assertions.assertThat(factory.newMultipartUploadRequest(path)
102+
.getCannedACL())
103+
.describedAs("ACL of MPU")
104+
.isEqualTo(acl);
105+
}
106+
107+
108+
76109
/**
77110
* Now add a processor and verify that it was invoked for
78111
* exactly as many requests as were analyzed.

0 commit comments

Comments
 (0)