blob: fd42fea9ac062bcf1ded86b25f46d816b687a658 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.ozone.om.helpers;
import org.apache.hadoop.ozone.OzoneAcl;
import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OzoneAclInfo;
import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer;
import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType;
import org.apache.hadoop.ozone.security.acl.RequestContext;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
import java.util.stream.Collectors;
import static org.apache.hadoop.ozone.OzoneAcl.AclScope.ACCESS;
import static org.apache.hadoop.ozone.OzoneAcl.AclScope.DEFAULT;
import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLIdentityType.GROUP;
import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLIdentityType.USER;
import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.ALL;
import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.NONE;
/**
* Helper class for ozone acls operations.
*/
public final class OzoneAclUtil {
private OzoneAclUtil(){
}
/**
* Helper function to get access acl list for current user.
*
* @param userName
* @param userGroups
* @return list of OzoneAcls
* */
public static List<OzoneAcl> getAclList(String userName,
List<String> userGroups, ACLType userRights, ACLType groupRights) {
List<OzoneAcl> listOfAcls = new ArrayList<>();
// User ACL.
listOfAcls.add(new OzoneAcl(USER, userName, userRights, ACCESS));
if(userGroups != null) {
// Group ACLs of the User.
userGroups.forEach((group) -> listOfAcls.add(
new OzoneAcl(GROUP, group, groupRights, ACCESS)));
}
return listOfAcls;
}
/**
* Check if acl right requested for given RequestContext exist
* in provided acl list.
* Acl validation rules:
* 1. If user/group has ALL bit set than all user should have all rights.
* 2. If user/group has NONE bit set than user/group will not have any right.
* 3. For all other individual rights individual bits should be set.
*
* @param acls
* @param context
* @return return true if acl list contains right requsted in context.
* */
public static boolean checkAclRight(List<OzoneAcl> acls,
RequestContext context) throws OMException {
String[] userGroups = context.getClientUgi().getGroupNames();
String userName = context.getClientUgi().getUserName();
ACLType aclToCheck = context.getAclRights();
for (OzoneAcl a : acls) {
if(checkAccessInAcl(a, userGroups, userName, aclToCheck)) {
return true;
}
}
return false;
}
private static boolean checkAccessInAcl(OzoneAcl a, String[] groups,
String username, ACLType aclToCheck) {
BitSet rights = a.getAclBitSet();
switch (a.getType()) {
case USER:
if (a.getName().equals(username)) {
return checkIfAclBitIsSet(aclToCheck, rights);
}
break;
case GROUP:
for (String grp : groups) {
if (a.getName().equals(grp)) {
return checkIfAclBitIsSet(aclToCheck, rights);
}
}
break;
default:
return checkIfAclBitIsSet(aclToCheck, rights);
}
return false;
}
/**
* Check if acl right requested for given RequestContext exist
* in provided acl list.
* Acl validation rules:
* 1. If user/group has ALL bit set than all user should have all rights.
* 2. If user/group has NONE bit set than user/group will not have any right.
* 3. For all other individual rights individual bits should be set.
*
* @param acls
* @param context
* @return return true if acl list contains right requsted in context.
* */
public static boolean checkAclRights(List<OzoneAcl> acls,
RequestContext context) throws OMException {
String[] userGroups = context.getClientUgi().getGroupNames();
String userName = context.getClientUgi().getUserName();
ACLType aclToCheck = context.getAclRights();
for (OzoneAcl acl : acls) {
if (checkAccessInAcl(acl, userGroups, userName, aclToCheck)) {
return true;
}
}
return false;
}
/**
* Helper function to check if bit for given acl is set.
* @param acl
* @param bitset
* @return True of acl bit is set else false.
* */
public static boolean checkIfAclBitIsSet(IAccessAuthorizer.ACLType acl,
BitSet bitset) {
if (bitset == null) {
return false;
}
return ((bitset.get(acl.ordinal())
|| bitset.get(ALL.ordinal()))
&& !bitset.get(NONE.ordinal()));
}
/**
* Helper function to inherit default ACL as access ACL for child object.
* 1. deep copy of OzoneAcl to avoid unexpected parent default ACL change
* 2. merge inherited access ACL with existing access ACL via
* OzoneUtils.addAcl().
* @param acls
* @param parentAcls
* @return true if acls inherited DEFAULT acls from parentAcls successfully,
* false otherwise.
*/
public static boolean inheritDefaultAcls(List<OzoneAcl> acls,
List<OzoneAcl> parentAcls) {
List<OzoneAcl> inheritedAcls = null;
if (parentAcls != null && !parentAcls.isEmpty()) {
inheritedAcls = parentAcls.stream()
.filter(a -> a.getAclScope() == DEFAULT)
.map(acl -> new OzoneAcl(acl.getType(), acl.getName(),
acl.getAclBitSet(), OzoneAcl.AclScope.ACCESS))
.collect(Collectors.toList());
}
if (inheritedAcls != null && !inheritedAcls.isEmpty()) {
inheritedAcls.stream().forEach(acl -> addAcl(acls, acl));
return true;
}
return false;
}
/**
* Convert a list of OzoneAclInfo(protoc) to list of OzoneAcl(java).
* @param protoAcls
* @return list of OzoneAcl.
*/
public static List<OzoneAcl> fromProtobuf(List<OzoneAclInfo> protoAcls) {
return protoAcls.stream().map(acl->OzoneAcl.fromProtobuf(acl))
.collect(Collectors.toList());
}
/**
* Convert a list of OzoneAcl(java) to list of OzoneAclInfo(protoc).
* @param protoAcls
* @return list of OzoneAclInfo.
*/
public static List<OzoneAclInfo> toProtobuf(List<OzoneAcl> protoAcls) {
return protoAcls.stream().map(acl->OzoneAcl.toProtobuf(acl))
.collect(Collectors.toList());
}
/**
* Add an OzoneAcl to existing list of OzoneAcls.
* @param existingAcls
* @param acl
* @return true if current OzoneAcls are changed, false otherwise.
*/
public static boolean addAcl(List<OzoneAcl> existingAcls, OzoneAcl acl) {
if (existingAcls == null || acl == null) {
return false;
}
for (OzoneAcl a: existingAcls) {
if (a.getName().equals(acl.getName()) &&
a.getType().equals(acl.getType()) &&
a.getAclScope().equals(acl.getAclScope())) {
BitSet current = a.getAclBitSet();
BitSet original = (BitSet) current.clone();
current.or(acl.getAclBitSet());
if (current.equals(original)) {
return false;
}
return true;
}
}
existingAcls.add(acl);
return true;
}
/**
* remove OzoneAcl from existing list of OzoneAcls.
* @param existingAcls
* @param acl
* @return true if current OzoneAcls are changed, false otherwise.
*/
public static boolean removeAcl(List<OzoneAcl> existingAcls, OzoneAcl acl) {
if (existingAcls == null || existingAcls.isEmpty() || acl == null) {
return false;
}
for (OzoneAcl a: existingAcls) {
if (a.getName().equals(acl.getName()) &&
a.getType().equals(acl.getType()) &&
a.getAclScope().equals(acl.getAclScope())) {
BitSet current = a.getAclBitSet();
BitSet original = (BitSet) current.clone();
current.andNot(acl.getAclBitSet());
if (current.equals(original)) {
return false;
}
if (current.isEmpty()) {
existingAcls.remove(a);
}
return true;
}
}
return false;
}
/**
* Set existingAcls to newAcls.
* @param existingAcls
* @param newAcls
* @return true if newAcls are set successfully, false otherwise.
*/
public static boolean setAcl(List<OzoneAcl> existingAcls,
List<OzoneAcl> newAcls) {
if (existingAcls == null) {
return false;
} else {
existingAcls.clear();
if (newAcls != null) {
existingAcls.addAll(newAcls);
}
}
return true;
}
}