Skip to content

Commit ef78a75

Browse files
author
Fabian Morgan
committed
add acl checks to ListParts for sts
1 parent 5797d63 commit ef78a75

2 files changed

Lines changed: 232 additions & 5 deletions

File tree

hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3938,9 +3938,18 @@ public OmMultipartUploadListParts listParts(final String volumeName,
39383938
final String bucketName, String keyName, String uploadID,
39393939
int partNumberMarker, int maxParts) throws IOException {
39403940

3941-
ResolvedBucket bucket = resolveBucketLink(Pair.of(volumeName, bucketName));
3941+
final ResolvedBucket bucket = resolveBucketLink(Pair.of(volumeName, bucketName));
3942+
final String realVolumeName = bucket.realVolume();
3943+
final String realBucketName = bucket.realBucket();
39423944

3943-
Map<String, String> auditMap = bucket.audit();
3945+
if (getAclsEnabled() && isStsS3Request()) {
3946+
omMetadataReader.checkAcls(
3947+
ResourceType.BUCKET, StoreType.OZONE, ACLType.READ, realVolumeName, realBucketName, null);
3948+
omMetadataReader.checkAcls(
3949+
ResourceType.KEY, StoreType.OZONE, ACLType.LIST, realVolumeName, realBucketName, keyName);
3950+
}
3951+
3952+
final Map<String, String> auditMap = bucket.audit();
39443953
auditMap.put(OzoneConsts.KEY, keyName);
39453954
auditMap.put(OzoneConsts.UPLOAD_ID, uploadID);
39463955
auditMap.put(OzoneConsts.PART_NUMBER_MARKER,
@@ -3949,9 +3958,8 @@ public OmMultipartUploadListParts listParts(final String volumeName,
39493958

39503959
metrics.incNumListMultipartUploadParts();
39513960
try {
3952-
OmMultipartUploadListParts omMultipartUploadListParts =
3953-
keyManager.listParts(bucket.realVolume(), bucket.realBucket(),
3954-
keyName, uploadID, partNumberMarker, maxParts);
3961+
final OmMultipartUploadListParts omMultipartUploadListParts = keyManager.listParts(
3962+
realVolumeName, realBucketName, keyName, uploadID, partNumberMarker, maxParts);
39553963
AUDIT.logReadSuccess(buildAuditMessageForSuccess(OMAction
39563964
.LIST_MULTIPART_UPLOAD_PARTS, auditMap));
39573965
return omMultipartUploadListParts;
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.hadoop.ozone.om;
19+
20+
import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.LIST;
21+
import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.READ;
22+
import static org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.BUCKET;
23+
import static org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.KEY;
24+
import static org.apache.hadoop.ozone.security.acl.OzoneObj.StoreType.OZONE;
25+
import static org.junit.jupiter.api.Assertions.assertThrows;
26+
import static org.mockito.ArgumentMatchers.any;
27+
import static org.mockito.ArgumentMatchers.anyInt;
28+
import static org.mockito.ArgumentMatchers.anyMap;
29+
import static org.mockito.ArgumentMatchers.anyString;
30+
import static org.mockito.ArgumentMatchers.eq;
31+
import static org.mockito.Mockito.doNothing;
32+
import static org.mockito.Mockito.doReturn;
33+
import static org.mockito.Mockito.doThrow;
34+
import static org.mockito.Mockito.inOrder;
35+
import static org.mockito.Mockito.mock;
36+
import static org.mockito.Mockito.never;
37+
import static org.mockito.Mockito.spy;
38+
import static org.mockito.Mockito.verify;
39+
import static org.mockito.Mockito.when;
40+
41+
import java.io.File;
42+
import org.apache.commons.lang3.tuple.Pair;
43+
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
44+
import org.apache.hadoop.hdds.scm.HddsWhiteboxTestUtils;
45+
import org.apache.hadoop.hdds.server.ServerUtils;
46+
import org.apache.hadoop.ozone.audit.AuditMessage;
47+
import org.apache.hadoop.ozone.om.exceptions.OMException;
48+
import org.apache.hadoop.ozone.om.helpers.OmMultipartUploadListParts;
49+
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.S3Authentication;
50+
import org.apache.hadoop.ozone.security.STSTokenIdentifier;
51+
import org.junit.jupiter.api.AfterAll;
52+
import org.junit.jupiter.api.AfterEach;
53+
import org.junit.jupiter.api.BeforeAll;
54+
import org.junit.jupiter.api.BeforeEach;
55+
import org.junit.jupiter.api.Test;
56+
import org.junit.jupiter.api.TestInstance;
57+
import org.junit.jupiter.api.io.TempDir;
58+
import org.mockito.InOrder;
59+
60+
61+
/**
62+
* Unit tests for STS + ACL checks on {@link OzoneManager#listParts}.
63+
*/
64+
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
65+
public class TestOzoneManagerListPartsAcls {
66+
67+
private OmTestManagers omTestManagers;
68+
private OzoneManager om;
69+
70+
private OzoneManager omSpy;
71+
private OmMetadataReader omMetadataReader;
72+
private KeyManager keyManager;
73+
private OMMetrics metrics;
74+
75+
private static final String REQUESTED_VOLUME = "requestedVolume";
76+
private static final String REQUESTED_BUCKET = "requestedBucket";
77+
private static final String REAL_VOLUME = "realVolume";
78+
private static final String REAL_BUCKET = "realBucket";
79+
private static final String KEY_NAME = "object/key";
80+
private static final String UPLOAD_ID = "uploadId";
81+
private static final String STS_ACCESS_ID = "ASIA7O1AJD8VV4KCEAX5";
82+
83+
@BeforeAll
84+
void setup(@TempDir File folder) throws Exception {
85+
final OzoneConfiguration conf = new OzoneConfiguration();
86+
ServerUtils.setOzoneMetaDirPath(conf, folder.toString());
87+
omTestManagers = new OmTestManagers(conf);
88+
om = omTestManagers.getOzoneManager();
89+
}
90+
91+
@AfterAll
92+
void cleanup() {
93+
if (omTestManagers != null) {
94+
omTestManagers.stop();
95+
}
96+
}
97+
98+
@BeforeEach
99+
void init() throws Exception {
100+
omSpy = spy(om);
101+
omMetadataReader = mock(OmMetadataReader.class);
102+
keyManager = mock(KeyManager.class);
103+
metrics = mock(OMMetrics.class);
104+
105+
HddsWhiteboxTestUtils.setInternalState(omSpy, "omMetadataReader", omMetadataReader);
106+
HddsWhiteboxTestUtils.setInternalState(omSpy, "keyManager", keyManager);
107+
HddsWhiteboxTestUtils.setInternalState(omSpy, "metrics", metrics);
108+
109+
doReturn(new ResolvedBucket(REQUESTED_VOLUME, REQUESTED_BUCKET, REAL_VOLUME, REAL_BUCKET, "owner", null))
110+
.when(omSpy).resolveBucketLink(Pair.of(REQUESTED_VOLUME, REQUESTED_BUCKET));
111+
112+
final AuditMessage mockAuditMessage = mock(AuditMessage.class);
113+
when(mockAuditMessage.getOp()).thenReturn("LIST_MULTIPART_UPLOAD_PARTS");
114+
doReturn(mockAuditMessage).when(omSpy).buildAuditMessageForSuccess(any(), anyMap());
115+
doReturn(mockAuditMessage).when(omSpy).buildAuditMessageForFailure(any(), anyMap(), any(Throwable.class));
116+
117+
when(keyManager.listParts(anyString(), anyString(), anyString(), anyString(), anyInt(), anyInt()))
118+
.thenReturn(mock(OmMultipartUploadListParts.class));
119+
}
120+
121+
@AfterEach
122+
void tearDown() {
123+
OzoneManager.setS3Auth(null);
124+
OzoneManager.setStsTokenIdentifier(null);
125+
}
126+
127+
@Test
128+
void testSkipsAclChecksWhenAclsAreDisabledEvenForStsRequest() throws Exception {
129+
setupStsS3Request();
130+
when(omSpy.getAclsEnabled()).thenReturn(false);
131+
132+
omSpy.listParts(REQUESTED_VOLUME, REQUESTED_BUCKET, KEY_NAME, UPLOAD_ID, 0, 10);
133+
134+
verify(omMetadataReader, never()).checkAcls(any(), any(), any(), any(), any(), any());
135+
verify(keyManager).listParts(
136+
eq(REAL_VOLUME), eq(REAL_BUCKET), eq(KEY_NAME), eq(UPLOAD_ID), eq(0), eq(10));
137+
}
138+
139+
@Test
140+
void testSkipsAclChecksWhenNotStsRequestEvenIfAclsAreEnabled() throws Exception {
141+
OzoneManager.setS3Auth(null);
142+
OzoneManager.setStsTokenIdentifier(null);
143+
when(omSpy.getAclsEnabled()).thenReturn(true);
144+
145+
omSpy.listParts(REQUESTED_VOLUME, REQUESTED_BUCKET, KEY_NAME, UPLOAD_ID, 0, 10);
146+
147+
verify(omMetadataReader, never()).checkAcls(any(), any(), any(), any(), any(), any());
148+
}
149+
150+
@Test
151+
void testAclsEnabledAndStsRequestChecksBucketReadThenKeyListUsingResolvedNames() throws Exception {
152+
setupStsS3Request();
153+
when(omSpy.getAclsEnabled()).thenReturn(true);
154+
155+
omSpy.listParts(REQUESTED_VOLUME, REQUESTED_BUCKET, KEY_NAME, UPLOAD_ID, 0, 10);
156+
157+
final InOrder inOrder = inOrder(omMetadataReader);
158+
inOrder.verify(omMetadataReader).checkAcls(BUCKET, OZONE, READ, REAL_VOLUME, REAL_BUCKET, null);
159+
inOrder.verify(omMetadataReader).checkAcls(KEY, OZONE, LIST, REAL_VOLUME, REAL_BUCKET, KEY_NAME);
160+
verify(keyManager).listParts(
161+
eq(REAL_VOLUME), eq(REAL_BUCKET), eq(KEY_NAME), eq(UPLOAD_ID), eq(0), eq(10));
162+
verify(metrics).incNumListMultipartUploadParts();
163+
verify(metrics, never()).incNumListMultipartUploadPartFails();
164+
}
165+
166+
@Test
167+
void testReadAclAccessDeniedSkipsKeyManagerAndKeyListAclChecksAndNoMetrics() throws Exception {
168+
setupStsS3Request();
169+
when(omSpy.getAclsEnabled()).thenReturn(true);
170+
171+
doThrow(new OMException("denied", OMException.ResultCodes.PERMISSION_DENIED))
172+
.when(omMetadataReader).checkAcls(BUCKET, OZONE, READ, REAL_VOLUME, REAL_BUCKET, null);
173+
174+
assertThrows(
175+
OMException.class, () -> omSpy.listParts(
176+
REQUESTED_VOLUME, REQUESTED_BUCKET, KEY_NAME, UPLOAD_ID, 0, 10));
177+
178+
verify(keyManager, never()).listParts(
179+
anyString(), anyString(), anyString(), anyString(), anyInt(), anyInt());
180+
verify(metrics, never()).incNumListMultipartUploadParts();
181+
verify(metrics, never()).incNumListMultipartUploadPartFails();
182+
verify(omMetadataReader, never()).checkAcls(KEY, OZONE, LIST, REAL_VOLUME, REAL_BUCKET, KEY_NAME);
183+
}
184+
185+
@Test
186+
void testKeyListAclAccessDeniedSkipsKeyManagerAndNoMetrics() throws Exception {
187+
setupStsS3Request();
188+
when(omSpy.getAclsEnabled()).thenReturn(true);
189+
190+
doNothing().when(omMetadataReader).checkAcls(BUCKET, OZONE, READ, REAL_VOLUME, REAL_BUCKET, null);
191+
doThrow(new OMException("denied", OMException.ResultCodes.PERMISSION_DENIED))
192+
.when(omMetadataReader).checkAcls(KEY, OZONE, LIST, REAL_VOLUME, REAL_BUCKET, KEY_NAME);
193+
194+
assertThrows(
195+
OMException.class, () -> omSpy.listParts(
196+
REQUESTED_VOLUME, REQUESTED_BUCKET, KEY_NAME, UPLOAD_ID, 0, 10));
197+
198+
verify(keyManager, never()).listParts(
199+
anyString(), anyString(), anyString(), anyString(), anyInt(), anyInt());
200+
verify(metrics, never()).incNumListMultipartUploadParts();
201+
verify(metrics, never()).incNumListMultipartUploadPartFails();
202+
}
203+
204+
@Test
205+
void testNonStsRequestSkipsAclChecks() throws Exception {
206+
OzoneManager.setS3Auth(S3Authentication.newBuilder().setAccessId(STS_ACCESS_ID).build());
207+
OzoneManager.setStsTokenIdentifier(null);
208+
when(omSpy.getAclsEnabled()).thenReturn(true);
209+
210+
omSpy.listParts(REQUESTED_VOLUME, REQUESTED_BUCKET, KEY_NAME, UPLOAD_ID, 0, 10);
211+
212+
verify(omMetadataReader, never()).checkAcls(any(), any(), any(), any(), any(), any());
213+
}
214+
215+
private void setupStsS3Request() {
216+
OzoneManager.setS3Auth(S3Authentication.newBuilder().setAccessId(STS_ACCESS_ID).build());
217+
OzoneManager.setStsTokenIdentifier(mock(STSTokenIdentifier.class));
218+
}
219+
}

0 commit comments

Comments
 (0)