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 package org.apache.hadoop.fs.permission; 19 20 import java.util.ArrayList; 21 import java.util.Collection; 22 import java.util.List; 23 24 import com.google.common.base.Objects; 25 26 import org.apache.hadoop.HadoopIllegalArgumentException; 27 import org.apache.hadoop.classification.InterfaceAudience; 28 import org.apache.hadoop.classification.InterfaceStability; 29 import org.apache.hadoop.util.StringUtils; 30 31 /** 32 * Defines a single entry in an ACL. An ACL entry has a type (user, group, 33 * mask, or other), an optional name (referring to a specific user or group), a 34 * set of permissions (any combination of read, write and execute), and a scope 35 * (access or default). AclEntry instances are immutable. Use a {@link Builder} 36 * to create a new instance. 37 */ 38 @InterfaceAudience.Public 39 @InterfaceStability.Evolving 40 public class AclEntry { 41 private final AclEntryType type; 42 private final String name; 43 private final FsAction permission; 44 private final AclEntryScope scope; 45 46 /** 47 * Returns the ACL entry type. 48 * 49 * @return AclEntryType ACL entry type 50 */ getType()51 public AclEntryType getType() { 52 return type; 53 } 54 55 /** 56 * Returns the optional ACL entry name. 57 * 58 * @return String ACL entry name, or null if undefined 59 */ getName()60 public String getName() { 61 return name; 62 } 63 64 /** 65 * Returns the set of permissions in the ACL entry. 66 * 67 * @return FsAction set of permissions in the ACL entry 68 */ getPermission()69 public FsAction getPermission() { 70 return permission; 71 } 72 73 /** 74 * Returns the scope of the ACL entry. 75 * 76 * @return AclEntryScope scope of the ACL entry 77 */ getScope()78 public AclEntryScope getScope() { 79 return scope; 80 } 81 82 @Override equals(Object o)83 public boolean equals(Object o) { 84 if (o == null) { 85 return false; 86 } 87 if (getClass() != o.getClass()) { 88 return false; 89 } 90 AclEntry other = (AclEntry)o; 91 return Objects.equal(type, other.type) && 92 Objects.equal(name, other.name) && 93 Objects.equal(permission, other.permission) && 94 Objects.equal(scope, other.scope); 95 } 96 97 @Override hashCode()98 public int hashCode() { 99 return Objects.hashCode(type, name, permission, scope); 100 } 101 102 @Override toString()103 public String toString() { 104 StringBuilder sb = new StringBuilder(); 105 if (scope == AclEntryScope.DEFAULT) { 106 sb.append("default:"); 107 } 108 if (type != null) { 109 sb.append(StringUtils.toLowerCase(type.toString())); 110 } 111 sb.append(':'); 112 if (name != null) { 113 sb.append(name); 114 } 115 sb.append(':'); 116 if (permission != null) { 117 sb.append(permission.SYMBOL); 118 } 119 return sb.toString(); 120 } 121 122 /** 123 * Builder for creating new AclEntry instances. 124 */ 125 public static class Builder { 126 private AclEntryType type; 127 private String name; 128 private FsAction permission; 129 private AclEntryScope scope = AclEntryScope.ACCESS; 130 131 /** 132 * Sets the ACL entry type. 133 * 134 * @param type AclEntryType ACL entry type 135 * @return Builder this builder, for call chaining 136 */ setType(AclEntryType type)137 public Builder setType(AclEntryType type) { 138 this.type = type; 139 return this; 140 } 141 142 /** 143 * Sets the optional ACL entry name. 144 * 145 * @param name String optional ACL entry name 146 * @return Builder this builder, for call chaining 147 */ setName(String name)148 public Builder setName(String name) { 149 if (name != null && !name.isEmpty()) { 150 this.name = name; 151 } 152 return this; 153 } 154 155 /** 156 * Sets the set of permissions in the ACL entry. 157 * 158 * @param permission FsAction set of permissions in the ACL entry 159 * @return Builder this builder, for call chaining 160 */ setPermission(FsAction permission)161 public Builder setPermission(FsAction permission) { 162 this.permission = permission; 163 return this; 164 } 165 166 /** 167 * Sets the scope of the ACL entry. If this method is not called, then the 168 * builder assumes {@link AclEntryScope#ACCESS}. 169 * 170 * @param scope AclEntryScope scope of the ACL entry 171 * @return Builder this builder, for call chaining 172 */ setScope(AclEntryScope scope)173 public Builder setScope(AclEntryScope scope) { 174 this.scope = scope; 175 return this; 176 } 177 178 /** 179 * Builds a new AclEntry populated with the set properties. 180 * 181 * @return AclEntry new AclEntry 182 */ build()183 public AclEntry build() { 184 return new AclEntry(type, name, permission, scope); 185 } 186 } 187 188 /** 189 * Private constructor. 190 * 191 * @param type AclEntryType ACL entry type 192 * @param name String optional ACL entry name 193 * @param permission FsAction set of permissions in the ACL entry 194 * @param scope AclEntryScope scope of the ACL entry 195 */ AclEntry(AclEntryType type, String name, FsAction permission, AclEntryScope scope)196 private AclEntry(AclEntryType type, String name, FsAction permission, AclEntryScope scope) { 197 this.type = type; 198 this.name = name; 199 this.permission = permission; 200 this.scope = scope; 201 } 202 203 /** 204 * Parses a string representation of an ACL spec into a list of AclEntry 205 * objects. Example: "user::rwx,user:foo:rw-,group::r--,other::---" 206 * 207 * @param aclSpec 208 * String representation of an ACL spec. 209 * @param includePermission 210 * for setAcl operations this will be true. i.e. AclSpec should 211 * include permissions.<br> 212 * But for removeAcl operation it will be false. i.e. AclSpec should 213 * not contain permissions.<br> 214 * Example: "user:foo,group:bar" 215 * @return Returns list of {@link AclEntry} parsed 216 */ parseAclSpec(String aclSpec, boolean includePermission)217 public static List<AclEntry> parseAclSpec(String aclSpec, 218 boolean includePermission) { 219 List<AclEntry> aclEntries = new ArrayList<AclEntry>(); 220 Collection<String> aclStrings = StringUtils.getStringCollection(aclSpec, 221 ","); 222 for (String aclStr : aclStrings) { 223 AclEntry aclEntry = parseAclEntry(aclStr, includePermission); 224 aclEntries.add(aclEntry); 225 } 226 return aclEntries; 227 } 228 229 /** 230 * Parses a string representation of an ACL into a AclEntry object.<br> 231 * 232 * @param aclStr 233 * String representation of an ACL.<br> 234 * Example: "user:foo:rw-" 235 * @param includePermission 236 * for setAcl operations this will be true. i.e. Acl should include 237 * permissions.<br> 238 * But for removeAcl operation it will be false. i.e. Acl should not 239 * contain permissions.<br> 240 * Example: "user:foo,group:bar,mask::" 241 * @return Returns an {@link AclEntry} object 242 */ parseAclEntry(String aclStr, boolean includePermission)243 public static AclEntry parseAclEntry(String aclStr, 244 boolean includePermission) { 245 AclEntry.Builder builder = new AclEntry.Builder(); 246 // Here "::" represent one empty string. 247 // StringUtils.getStringCollection() will ignore this. 248 String[] split = aclStr.split(":"); 249 250 if (split.length == 0) { 251 throw new HadoopIllegalArgumentException("Invalid <aclSpec> : " + aclStr); 252 } 253 int index = 0; 254 if ("default".equals(split[0])) { 255 // default entry 256 index++; 257 builder.setScope(AclEntryScope.DEFAULT); 258 } 259 260 if (split.length <= index) { 261 throw new HadoopIllegalArgumentException("Invalid <aclSpec> : " + aclStr); 262 } 263 264 AclEntryType aclType = null; 265 try { 266 aclType = Enum.valueOf( 267 AclEntryType.class, StringUtils.toUpperCase(split[index])); 268 builder.setType(aclType); 269 index++; 270 } catch (IllegalArgumentException iae) { 271 throw new HadoopIllegalArgumentException( 272 "Invalid type of acl in <aclSpec> :" + aclStr); 273 } 274 275 if (split.length > index) { 276 String name = split[index]; 277 if (!name.isEmpty()) { 278 builder.setName(name); 279 } 280 index++; 281 } 282 283 if (includePermission) { 284 if (split.length <= index) { 285 throw new HadoopIllegalArgumentException("Invalid <aclSpec> : " 286 + aclStr); 287 } 288 String permission = split[index]; 289 FsAction fsAction = FsAction.getFsAction(permission); 290 if (null == fsAction) { 291 throw new HadoopIllegalArgumentException( 292 "Invalid permission in <aclSpec> : " + aclStr); 293 } 294 builder.setPermission(fsAction); 295 index++; 296 } 297 298 if (split.length > index) { 299 throw new HadoopIllegalArgumentException("Invalid <aclSpec> : " + aclStr); 300 } 301 AclEntry aclEntry = builder.build(); 302 return aclEntry; 303 } 304 305 /** 306 * Convert a List of AclEntries into a string - the reverse of parseAclSpec. 307 * @param aclSpec List of AclEntries to convert 308 * @return String representation of aclSpec 309 */ aclSpecToString(List<AclEntry> aclSpec)310 public static String aclSpecToString(List<AclEntry> aclSpec) { 311 StringBuilder buf = new StringBuilder(); 312 for ( AclEntry e : aclSpec ) { 313 buf.append(e.toString()); 314 buf.append(","); 315 } 316 return buf.substring(0, buf.length()-1); // remove last , 317 } 318 } 319