Skip to content

Commit 91f41b7

Browse files
HDDS-1884. Support Bucket ACL operations for OM HA. (#1202)
1 parent aa5f445 commit 91f41b7

File tree

15 files changed

+978
-5
lines changed

15 files changed

+978
-5
lines changed

hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,9 @@ public enum ResultCodes {
205205

206206
S3_BUCKET_INVALID_LENGTH,
207207

208-
RATIS_ERROR // Error in Ratis server
208+
RATIS_ERROR, // Error in Ratis server
209+
210+
INVALID_PATH_IN_ACL_REQUEST // Error code when path name is invalid during
211+
// acl requests.
209212
}
210213
}

hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmBucketInfo.java

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
package org.apache.hadoop.ozone.om.helpers;
1919

2020

21+
import java.util.BitSet;
2122
import java.util.HashMap;
2223
import java.util.LinkedHashMap;
2324
import java.util.LinkedList;
@@ -30,11 +31,14 @@
3031
import org.apache.hadoop.ozone.OzoneAcl;
3132
import org.apache.hadoop.ozone.OzoneConsts;
3233
import org.apache.hadoop.ozone.audit.Auditable;
33-
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.BucketInfo;
34+
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos
35+
.BucketInfo;
3436
import org.apache.hadoop.ozone.protocolPB.OMPBHelper;
3537

3638
import com.google.common.base.Preconditions;
3739

40+
import static org.apache.hadoop.ozone.OzoneAcl.ZERO_BITSET;
41+
3842
/**
3943
* A class that encapsulates Bucket Info.
4044
*/
@@ -124,6 +128,95 @@ public List<OzoneAcl> getAcls() {
124128
return acls;
125129
}
126130

131+
/**
132+
* Add an ozoneAcl to list of existing Acl set.
133+
* @param ozoneAcl
134+
* @return true - if successfully added, false if not added or acl is
135+
* already existing in the acl list.
136+
*/
137+
public boolean addAcl(OzoneAcl ozoneAcl) {
138+
// Case 1: When we are adding more rights to existing user/group.
139+
boolean addToExistingAcl = false;
140+
for(OzoneAcl existingAcl: getAcls()) {
141+
if(existingAcl.getName().equals(ozoneAcl.getName()) &&
142+
existingAcl.getType().equals(ozoneAcl.getType())) {
143+
144+
BitSet bits = (BitSet) ozoneAcl.getAclBitSet().clone();
145+
146+
// We need to do "or" before comparision because think of a case like
147+
// existing acl is 777 and newly added acl is 444, we have already
148+
// that acl set. In this case if we do direct check they will not
149+
// be equal, but if we do or and then check, we shall know it
150+
// has acl's already set or not.
151+
bits.or(existingAcl.getAclBitSet());
152+
153+
if (bits.equals(existingAcl.getAclBitSet())) {
154+
return false;
155+
} else {
156+
existingAcl.getAclBitSet().or(ozoneAcl.getAclBitSet());
157+
addToExistingAcl = true;
158+
break;
159+
}
160+
}
161+
}
162+
163+
// Case 2: When a completely new acl is added.
164+
if(!addToExistingAcl) {
165+
getAcls().add(ozoneAcl);
166+
}
167+
return true;
168+
}
169+
170+
/**
171+
* Remove acl from existing acl list.
172+
* @param ozoneAcl
173+
* @return true - if successfully removed, false if not able to remove due
174+
* to that acl is not in the existing acl list.
175+
*/
176+
public boolean removeAcl(OzoneAcl ozoneAcl) {
177+
boolean removed = false;
178+
179+
// When we are removing subset of rights from existing acl.
180+
for(OzoneAcl existingAcl: getAcls()) {
181+
if (existingAcl.getName().equals(ozoneAcl.getName()) &&
182+
existingAcl.getType().equals(ozoneAcl.getType())) {
183+
BitSet bits = (BitSet) ozoneAcl.getAclBitSet().clone();
184+
bits.and(existingAcl.getAclBitSet());
185+
186+
// This happens when the acl bitset is not existing for current name
187+
// and type.
188+
// Like a case we have 444 permission, 333 is asked to removed.
189+
if (bits.equals(ZERO_BITSET)) {
190+
return false;
191+
}
192+
193+
// We have some matching. Remove them.
194+
existingAcl.getAclBitSet().xor(bits);
195+
196+
// If existing acl has same bitset as passed acl bitset, remove that
197+
// acl from the list
198+
if (existingAcl.getAclBitSet().equals(ZERO_BITSET)) {
199+
getAcls().remove(existingAcl);
200+
}
201+
removed = true;
202+
break;
203+
}
204+
}
205+
206+
return removed;
207+
}
208+
209+
/**
210+
* Reset the existing acl list.
211+
* @param ozoneAcls
212+
* @return true - if successfully able to reset.
213+
*/
214+
public boolean setAcls(List<OzoneAcl> ozoneAcls) {
215+
this.acls.clear();
216+
this.acls = ozoneAcls;
217+
return true;
218+
}
219+
127220
/**
128221
* Returns true if bucket version is enabled, else false.
129222
* @return isVersionEnabled
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.apache.hadoop.ozone.util;
2+
3+
/**
4+
* Defines a functional interface having two inputs and returns boolean as
5+
* output.
6+
*/
7+
@FunctionalInterface
8+
public interface BooleanBiFunction<LEFT, RIGHT> {
9+
boolean apply(LEFT left, RIGHT right);
10+
}
11+

hadoop-ozone/common/src/main/proto/OzoneManagerProtocol.proto

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,8 @@ enum Status {
283283
S3_BUCKET_INVALID_LENGTH = 51; // s3 bucket invalid length.
284284

285285
RATIS_ERROR = 52;
286+
287+
INVALID_PATH_IN_ACL_REQUEST = 53; // Invalid path name in acl request.
286288
}
287289

288290

hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOzoneManagerHA.java

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,16 @@
1919
import java.io.IOException;
2020
import java.net.ConnectException;
2121
import java.net.InetSocketAddress;
22+
import java.util.BitSet;
23+
import java.util.Collections;
2224
import java.util.HashMap;
2325
import java.util.List;
2426
import java.util.Map;
2527
import java.util.UUID;
2628

29+
import org.apache.hadoop.ozone.OzoneAcl;
30+
import org.apache.hadoop.ozone.security.acl.OzoneObj;
31+
import org.apache.hadoop.ozone.security.acl.OzoneObjInfo;
2732
import org.junit.After;
2833
import org.junit.Assert;
2934
import org.junit.Before;
@@ -65,6 +70,7 @@
6570

6671
import static org.apache.hadoop.ozone.MiniOzoneHAClusterImpl
6772
.NODE_FAILURE_TIMEOUT;
73+
import static org.apache.hadoop.ozone.OzoneAcl.AclScope.DEFAULT;
6874
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ACL_ENABLED;
6975
import static org.apache.hadoop.ozone.OzoneConfigKeys
7076
.OZONE_CLIENT_FAILOVER_MAX_ATTEMPTS_KEY;
@@ -76,6 +82,9 @@
7682
.OZONE_OPEN_KEY_EXPIRE_THRESHOLD_SECONDS;
7783
import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.FILE_ALREADY_EXISTS;
7884
import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.NOT_A_FILE;
85+
import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLIdentityType.USER;
86+
import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.READ;
87+
import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.WRITE;
7988
import static org.junit.Assert.fail;
8089

8190
/**
@@ -759,6 +768,139 @@ public void testReadRequest() throws Exception {
759768
}
760769
}
761770

771+
@Test
772+
public void testAddBucketAcl() throws Exception {
773+
OzoneBucket ozoneBucket = setupBucket();
774+
String remoteUserName = "remoteUser";
775+
OzoneAcl defaultUserAcl = new OzoneAcl(USER, remoteUserName,
776+
READ, DEFAULT);
777+
778+
OzoneObj ozoneObj = OzoneObjInfo.Builder.newBuilder()
779+
.setResType(OzoneObj.ResourceType.BUCKET)
780+
.setStoreType(OzoneObj.StoreType.OZONE)
781+
.setVolumeName(ozoneBucket.getVolumeName())
782+
.setBucketName(ozoneBucket.getName()).build();
783+
784+
boolean addAcl = objectStore.addAcl(ozoneObj, defaultUserAcl);
785+
Assert.assertTrue(addAcl);
786+
787+
List<OzoneAcl> acls = objectStore.getAcl(ozoneObj);
788+
789+
Assert.assertTrue(containsAcl(defaultUserAcl, acls));
790+
791+
// Add an already existing acl.
792+
addAcl = objectStore.addAcl(ozoneObj, defaultUserAcl);
793+
Assert.assertFalse(addAcl);
794+
795+
// Add an acl by changing acl type with same type, name and scope.
796+
defaultUserAcl = new OzoneAcl(USER, remoteUserName,
797+
WRITE, DEFAULT);
798+
addAcl = objectStore.addAcl(ozoneObj, defaultUserAcl);
799+
Assert.assertTrue(addAcl);
800+
}
801+
802+
@Test
803+
public void testRemoveBucketAcl() throws Exception {
804+
OzoneBucket ozoneBucket = setupBucket();
805+
String remoteUserName = "remoteUser";
806+
OzoneAcl defaultUserAcl = new OzoneAcl(USER, remoteUserName,
807+
READ, DEFAULT);
808+
809+
OzoneObj ozoneObj = OzoneObjInfo.Builder.newBuilder()
810+
.setResType(OzoneObj.ResourceType.BUCKET)
811+
.setStoreType(OzoneObj.StoreType.OZONE)
812+
.setVolumeName(ozoneBucket.getVolumeName())
813+
.setBucketName(ozoneBucket.getName()).build();
814+
815+
// As by default create bucket we add some default acls in RpcClient.
816+
List<OzoneAcl> acls = objectStore.getAcl(ozoneObj);
817+
818+
Assert.assertTrue(acls.size() > 0);
819+
820+
// Remove an existing acl.
821+
boolean removeAcl = objectStore.removeAcl(ozoneObj, acls.get(0));
822+
Assert.assertTrue(removeAcl);
823+
824+
// Trying to remove an already removed acl.
825+
removeAcl = objectStore.removeAcl(ozoneObj, acls.get(0));
826+
Assert.assertFalse(removeAcl);
827+
828+
boolean addAcl = objectStore.addAcl(ozoneObj, defaultUserAcl);
829+
Assert.assertTrue(addAcl);
830+
831+
// Just changed acl type here to write, rest all is same as defaultUserAcl.
832+
OzoneAcl modifiedUserAcl = new OzoneAcl(USER, remoteUserName,
833+
WRITE, DEFAULT);
834+
addAcl = objectStore.addAcl(ozoneObj, modifiedUserAcl);
835+
Assert.assertTrue(addAcl);
836+
837+
removeAcl = objectStore.removeAcl(ozoneObj, modifiedUserAcl);
838+
Assert.assertTrue(removeAcl);
839+
840+
removeAcl = objectStore.removeAcl(ozoneObj, defaultUserAcl);
841+
Assert.assertTrue(removeAcl);
842+
843+
}
844+
845+
@Test
846+
public void testSetBucketAcl() throws Exception {
847+
OzoneBucket ozoneBucket = setupBucket();
848+
String remoteUserName = "remoteUser";
849+
OzoneAcl defaultUserAcl = new OzoneAcl(USER, remoteUserName,
850+
READ, DEFAULT);
851+
852+
OzoneObj ozoneObj = OzoneObjInfo.Builder.newBuilder()
853+
.setResType(OzoneObj.ResourceType.BUCKET)
854+
.setStoreType(OzoneObj.StoreType.OZONE)
855+
.setVolumeName(ozoneBucket.getVolumeName())
856+
.setBucketName(ozoneBucket.getName()).build();
857+
858+
// As by default create bucket we add some default acls in RpcClient.
859+
List<OzoneAcl> acls = objectStore.getAcl(ozoneObj);
860+
861+
Assert.assertTrue(acls.size() > 0);
862+
863+
OzoneAcl modifiedUserAcl = new OzoneAcl(USER, remoteUserName,
864+
WRITE, DEFAULT);
865+
866+
List<OzoneAcl> newAcls = Collections.singletonList(modifiedUserAcl);
867+
boolean setAcl = objectStore.setAcl(ozoneObj, newAcls);
868+
Assert.assertTrue(setAcl);
869+
870+
// Get acls and check whether they are reset or not.
871+
List<OzoneAcl> getAcls = objectStore.getAcl(ozoneObj);
872+
873+
Assert.assertTrue(newAcls.size() == getAcls.size());
874+
int i = 0;
875+
for (OzoneAcl ozoneAcl : newAcls) {
876+
Assert.assertTrue(compareAcls(getAcls.get(i++), ozoneAcl));
877+
}
878+
}
879+
880+
private boolean containsAcl(OzoneAcl ozoneAcl, List<OzoneAcl> ozoneAcls) {
881+
for (OzoneAcl acl : ozoneAcls) {
882+
boolean result = compareAcls(ozoneAcl, acl);
883+
if (result) {
884+
// We found a match, return.
885+
return result;
886+
}
887+
}
888+
return false;
889+
}
890+
891+
private boolean compareAcls(OzoneAcl givenAcl, OzoneAcl existingAcl) {
892+
if (givenAcl.getType().equals(existingAcl.getType())
893+
&& givenAcl.getName().equals(existingAcl.getName())
894+
&& givenAcl.getAclScope().equals(existingAcl.getAclScope())) {
895+
BitSet bitSet = (BitSet) givenAcl.getAclBitSet().clone();
896+
bitSet.and(existingAcl.getAclBitSet());
897+
if (bitSet.equals(existingAcl.getAclBitSet())) {
898+
return true;
899+
}
900+
}
901+
return false;
902+
}
903+
762904
@Test
763905
public void testOMRatisSnapshot() throws Exception {
764906
String userName = "user" + RandomStringUtils.randomNumeric(5);

hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/utils/OzoneManagerRatisUtils.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
import org.apache.hadoop.ozone.om.request.bucket.OMBucketDeleteRequest;
2424
import org.apache.hadoop.ozone.om.request.bucket.OMBucketSetPropertyRequest;
2525
import org.apache.hadoop.ozone.om.request.OMClientRequest;
26+
import org.apache.hadoop.ozone.om.request.bucket.acl.OMBucketAddAclRequest;
27+
import org.apache.hadoop.ozone.om.request.bucket.acl.OMBucketRemoveAclRequest;
28+
import org.apache.hadoop.ozone.om.request.bucket.acl.OMBucketSetAclRequest;
2629
import org.apache.hadoop.ozone.om.request.file.OMDirectoryCreateRequest;
2730
import org.apache.hadoop.ozone.om.request.file.OMFileCreateRequest;
2831
import org.apache.hadoop.ozone.om.request.key.OMAllocateBlockRequest;
@@ -136,19 +139,25 @@ private static OMClientRequest getOMAclRequest(OMRequest omRequest) {
136139
ObjectType type = omRequest.getAddAclRequest().getObj().getResType();
137140
if (ObjectType.VOLUME == type) {
138141
return new OMVolumeAddAclRequest(omRequest);
142+
} else if (ObjectType.BUCKET == type) {
143+
return new OMBucketAddAclRequest(omRequest);
139144
}
140145
} else if (Type.RemoveAcl == cmdType) {
141-
ObjectType type = omRequest.getAddAclRequest().getObj().getResType();
146+
ObjectType type = omRequest.getRemoveAclRequest().getObj().getResType();
142147
if (ObjectType.VOLUME == type) {
143148
return new OMVolumeRemoveAclRequest(omRequest);
149+
} else if (ObjectType.BUCKET == type) {
150+
return new OMBucketRemoveAclRequest(omRequest);
144151
}
145152
} else if (Type.SetAcl == cmdType) {
146-
ObjectType type = omRequest.getAddAclRequest().getObj().getResType();
153+
ObjectType type = omRequest.getSetAclRequest().getObj().getResType();
147154
if (ObjectType.VOLUME == type) {
148155
return new OMVolumeSetAclRequest(omRequest);
156+
} else if (ObjectType.BUCKET == type) {
157+
return new OMBucketSetAclRequest(omRequest);
149158
}
150159
}
151-
//TODO: handle bucket, key and prefix AddAcl
160+
//TODO: handle key and prefix AddAcl
152161
return null;
153162
}
154163

0 commit comments

Comments
 (0)