blob: 6a74342b8d289f5a9e8a3efb21ceeabe5ed0f8cd [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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.google.protobuf.ByteString;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OzoneAclInfo;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OzoneAclInfo.OzoneAclScope;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OzoneAclInfo.OzoneAclType;
import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLIdentityType;
import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* OzoneACL classes define bucket ACLs used in OZONE.
*
* ACLs in Ozone follow this pattern.
* <ul>
* <li>user:name:rw
* <li>group:name:rw
* <li>world::rw
* </ul>
*/
@JsonIgnoreProperties(value = {"aclBitSet"})
public class OzoneAcl {
private static final String ACL_SCOPE_REGEX = ".*\\[(ACCESS|DEFAULT)\\]";
private ACLIdentityType type;
private String name;
private BitSet aclBitSet;
private AclScope aclScope;
private static final List<ACLType> EMPTY_LIST = new ArrayList<>(0);
public static final BitSet ZERO_BITSET = new BitSet(0);
/**
* Default constructor.
*/
public OzoneAcl() {
}
/**
* Constructor for OzoneAcl.
*
* @param type - Type
* @param name - Name of user
* @param acl - Rights
* @param scope - AclScope
*/
public OzoneAcl(ACLIdentityType type, String name, ACLType acl,
AclScope scope) {
this.name = name;
this.aclBitSet = new BitSet(ACLType.getNoOfAcls());
aclBitSet.set(acl.ordinal(), true);
this.type = type;
if (type == ACLIdentityType.WORLD || type == ACLIdentityType.ANONYMOUS) {
if (!name.equals(ACLIdentityType.WORLD.name()) &&
!name.equals(ACLIdentityType.ANONYMOUS.name()) &&
name.length() != 0) {
throw new IllegalArgumentException("Unexpected name:{" + name +
"} for type WORLD, ANONYMOUS. It should be WORLD & " +
"ANONYMOUS respectively.");
}
// For type WORLD and ANONYMOUS we allow only one acl to be set.
this.name = type.name();
}
if (((type == ACLIdentityType.USER) || (type == ACLIdentityType.GROUP))
&& (name.length() == 0)) {
throw new IllegalArgumentException("User or group name is required");
}
aclScope = scope;
}
/**
* Constructor for OzoneAcl.
*
* @param type - Type
* @param name - Name of user
* @param acls - Rights
* @param scope - AclScope
*/
public OzoneAcl(ACLIdentityType type, String name, BitSet acls,
AclScope scope) {
Objects.requireNonNull(type);
Objects.requireNonNull(acls);
if(acls.cardinality() > ACLType.getNoOfAcls()) {
throw new IllegalArgumentException("Acl bitset passed has unexpected " +
"size. bitset size:" + acls.cardinality() + ", bitset:"
+ acls.toString());
}
this.aclBitSet = (BitSet) acls.clone();
this.name = name;
this.type = type;
if (type == ACLIdentityType.WORLD || type == ACLIdentityType.ANONYMOUS) {
if (!name.equals(ACLIdentityType.WORLD.name()) &&
!name.equals(ACLIdentityType.ANONYMOUS.name()) &&
name.length() != 0) {
throw new IllegalArgumentException("Unexpected name:{" + name +
"} for type WORLD, ANONYMOUS. It should be WORLD & " +
"ANONYMOUS respectively.");
}
// For type WORLD and ANONYMOUS we allow only one acl to be set.
this.name = type.name();
}
if (((type == ACLIdentityType.USER) || (type == ACLIdentityType.GROUP))
&& (name.length() == 0)) {
throw new IllegalArgumentException("User or group name is required");
}
aclScope = scope;
}
/**
* Parses an ACL string and returns the ACL object. If acl scope is not
* passed in input string then scope is set to ACCESS.
*
* @param acl - Acl String , Ex. user:anu:rw
*
* @return - Ozone ACLs
*/
public static OzoneAcl parseAcl(String acl)
throws IllegalArgumentException {
if ((acl == null) || acl.isEmpty()) {
throw new IllegalArgumentException("ACLs cannot be null or empty");
}
String[] parts = acl.trim().split(":");
if (parts.length < 3) {
throw new IllegalArgumentException("ACLs are not in expected format");
}
ACLIdentityType aclType = ACLIdentityType.valueOf(parts[0].toUpperCase());
BitSet acls = new BitSet(ACLType.getNoOfAcls());
String bits = parts[2];
// Default acl scope is ACCESS.
AclScope aclScope = AclScope.ACCESS;
// Check if acl string contains scope info.
if(parts[2].matches(ACL_SCOPE_REGEX)) {
int indexOfOpenBracket = parts[2].indexOf("[");
bits = parts[2].substring(0, indexOfOpenBracket);
aclScope = AclScope.valueOf(parts[2].substring(indexOfOpenBracket + 1,
parts[2].indexOf("]")));
}
// Set all acl bits.
for (char ch : bits.toCharArray()) {
acls.set(ACLType.getACLRight(String.valueOf(ch)).ordinal());
}
// TODO : Support sanitation of these user names by calling into
// userAuth Interface.
return new OzoneAcl(aclType, parts[1], acls, aclScope);
}
/**
* Parses an ACL string and returns the ACL object.
*
* @param acls - Acl String , Ex. user:anu:rw
*
* @return - Ozone ACLs
*/
public static List<OzoneAcl> parseAcls(String acls)
throws IllegalArgumentException {
if ((acls == null) || acls.isEmpty()) {
throw new IllegalArgumentException("ACLs cannot be null or empty");
}
String[] parts = acls.trim().split(",");
if (parts.length < 1) {
throw new IllegalArgumentException("ACLs are not in expected format");
}
List<OzoneAcl> ozAcls = new ArrayList<>();
for(String acl:parts) {
ozAcls.add(parseAcl(acl));
}
return ozAcls;
}
public static OzoneAclInfo toProtobuf(OzoneAcl acl) {
OzoneAclInfo.Builder builder = OzoneAclInfo.newBuilder()
.setName(acl.getName())
.setType(OzoneAclType.valueOf(acl.getType().name()))
.setAclScope(OzoneAclScope.valueOf(acl.getAclScope().name()))
.setRights(ByteString.copyFrom(acl.getAclBitSet().toByteArray()));
return builder.build();
}
public static OzoneAcl fromProtobuf(OzoneAclInfo protoAcl) {
BitSet aclRights = BitSet.valueOf(protoAcl.getRights().toByteArray());
return new OzoneAcl(ACLIdentityType.valueOf(protoAcl.getType().name()),
protoAcl.getName(), aclRights,
AclScope.valueOf(protoAcl.getAclScope().name()));
}
/**
* Helper function to convert a proto message of type {@link OzoneAclInfo}
* to {@link OzoneAcl} with acl scope of type ACCESS.
*
* @param protoAcl
* @return OzoneAcl
* */
public static OzoneAcl fromProtobufWithAccessType(OzoneAclInfo protoAcl) {
BitSet aclRights = BitSet.valueOf(protoAcl.getRights().toByteArray());
return new OzoneAcl(ACLIdentityType.valueOf(protoAcl.getType().name()),
protoAcl.getName(), aclRights, AclScope.ACCESS);
}
/**
* Helper function to convert an {@link OzoneAcl} to proto message of type
* {@link OzoneAclInfo} with acl scope of type ACCESS.
*
* @param acl
* @return OzoneAclInfo
* */
public static OzoneAclInfo toProtobufWithAccessType(OzoneAcl acl) {
OzoneAclInfo.Builder builder = OzoneAclInfo.newBuilder()
.setName(acl.getName())
.setType(OzoneAclType.valueOf(acl.getType().name()))
.setAclScope(OzoneAclScope.ACCESS)
.setRights(ByteString.copyFrom(acl.getAclBitSet().toByteArray()));
return builder.build();
}
public AclScope getAclScope() {
return aclScope;
}
@Override
public String toString() {
return type + ":" + name + ":" + ACLType.getACLString(aclBitSet)
+ "[" + aclScope + "]";
}
/**
* Returns a hash code value for the object. This method is
* supported for the benefit of hash tables.
*
* @return a hash code value for this object.
*
* @see Object#equals(Object)
* @see System#identityHashCode
*/
@Override
public int hashCode() {
return Objects.hash(this.getName(), this.getAclBitSet(),
this.getType().toString(), this.getAclScope());
}
/**
* Returns name.
*
* @return name
*/
public String getName() {
return name;
}
/**
* Returns Rights.
*
* @return - Rights
*/
public BitSet getAclBitSet() {
return aclBitSet;
}
public List<ACLType> getAclList() {
if(aclBitSet != null) {
return aclBitSet.stream().mapToObj(a ->
ACLType.values()[a]).collect(Collectors.toList());
}
return EMPTY_LIST;
}
/**
* Returns Type.
*
* @return type
*/
public ACLIdentityType getType() {
return type;
}
/**
* Indicates whether some other object is "equal to" this one.
*
* @param obj the reference object with which to compare.
*
* @return {@code true} if this object is the same as the obj
* argument; {@code false} otherwise.
*/
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
OzoneAcl otherAcl = (OzoneAcl) obj;
return otherAcl.getName().equals(this.getName()) &&
otherAcl.getType().equals(this.getType()) &&
otherAcl.getAclBitSet().equals(this.getAclBitSet()) &&
otherAcl.getAclScope().equals(this.getAclScope());
}
public OzoneAcl setAclScope(AclScope scope) {
this.aclScope = scope;
return this;
}
/**
* Scope of ozone acl.
* */
public enum AclScope {
ACCESS,
DEFAULT;
}
}