001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.fs.permission; 019 020import java.util.ArrayList; 021import java.util.Collection; 022import java.util.List; 023 024import com.google.common.base.Objects; 025 026import org.apache.hadoop.HadoopIllegalArgumentException; 027import org.apache.hadoop.classification.InterfaceAudience; 028import org.apache.hadoop.classification.InterfaceStability; 029import org.apache.hadoop.util.StringUtils; 030 031/** 032 * Defines a single entry in an ACL. An ACL entry has a type (user, group, 033 * mask, or other), an optional name (referring to a specific user or group), a 034 * set of permissions (any combination of read, write and execute), and a scope 035 * (access or default). AclEntry instances are immutable. Use a {@link Builder} 036 * to create a new instance. 037 */ 038@InterfaceAudience.Public 039@InterfaceStability.Stable 040public class AclEntry { 041 private final AclEntryType type; 042 private final String name; 043 private final FsAction permission; 044 private final AclEntryScope scope; 045 046 /** 047 * Returns the ACL entry type. 048 * 049 * @return AclEntryType ACL entry type 050 */ 051 public AclEntryType getType() { 052 return type; 053 } 054 055 /** 056 * Returns the optional ACL entry name. 057 * 058 * @return String ACL entry name, or null if undefined 059 */ 060 public String getName() { 061 return name; 062 } 063 064 /** 065 * Returns the set of permissions in the ACL entry. 066 * 067 * @return FsAction set of permissions in the ACL entry 068 */ 069 public FsAction getPermission() { 070 return permission; 071 } 072 073 /** 074 * Returns the scope of the ACL entry. 075 * 076 * @return AclEntryScope scope of the ACL entry 077 */ 078 public AclEntryScope getScope() { 079 return scope; 080 } 081 082 @Override 083 public boolean equals(Object o) { 084 if (o == null) { 085 return false; 086 } 087 if (getClass() != o.getClass()) { 088 return false; 089 } 090 AclEntry other = (AclEntry)o; 091 return Objects.equal(type, other.type) && 092 Objects.equal(name, other.name) && 093 Objects.equal(permission, other.permission) && 094 Objects.equal(scope, other.scope); 095 } 096 097 @Override 098 public int hashCode() { 099 return Objects.hashCode(type, name, permission, scope); 100 } 101 102 @Override 103 @InterfaceStability.Unstable 104 public String toString() { 105 // This currently just delegates to the stable string representation, but it 106 // is permissible for the output of this method to change across versions. 107 return toStringStable(); 108 } 109 110 /** 111 * Returns a string representation guaranteed to be stable across versions to 112 * satisfy backward compatibility requirements, such as for shell command 113 * output or serialization. The format of this string representation matches 114 * what is expected by the {@link #parseAclSpec(String, boolean)} and 115 * {@link #parseAclEntry(String, boolean)} methods. 116 * 117 * @return stable, backward compatible string representation 118 */ 119 public String toStringStable() { 120 StringBuilder sb = new StringBuilder(); 121 if (scope == AclEntryScope.DEFAULT) { 122 sb.append("default:"); 123 } 124 if (type != null) { 125 sb.append(StringUtils.toLowerCase(type.toStringStable())); 126 } 127 sb.append(':'); 128 if (name != null) { 129 sb.append(name); 130 } 131 sb.append(':'); 132 if (permission != null) { 133 sb.append(permission.SYMBOL); 134 } 135 return sb.toString(); 136 } 137 138 /** 139 * Builder for creating new AclEntry instances. 140 */ 141 public static class Builder { 142 private AclEntryType type; 143 private String name; 144 private FsAction permission; 145 private AclEntryScope scope = AclEntryScope.ACCESS; 146 147 /** 148 * Sets the ACL entry type. 149 * 150 * @param type AclEntryType ACL entry type 151 * @return Builder this builder, for call chaining 152 */ 153 public Builder setType(AclEntryType type) { 154 this.type = type; 155 return this; 156 } 157 158 /** 159 * Sets the optional ACL entry name. 160 * 161 * @param name String optional ACL entry name 162 * @return Builder this builder, for call chaining 163 */ 164 public Builder setName(String name) { 165 if (name != null && !name.isEmpty()) { 166 this.name = name; 167 } 168 return this; 169 } 170 171 /** 172 * Sets the set of permissions in the ACL entry. 173 * 174 * @param permission FsAction set of permissions in the ACL entry 175 * @return Builder this builder, for call chaining 176 */ 177 public Builder setPermission(FsAction permission) { 178 this.permission = permission; 179 return this; 180 } 181 182 /** 183 * Sets the scope of the ACL entry. If this method is not called, then the 184 * builder assumes {@link AclEntryScope#ACCESS}. 185 * 186 * @param scope AclEntryScope scope of the ACL entry 187 * @return Builder this builder, for call chaining 188 */ 189 public Builder setScope(AclEntryScope scope) { 190 this.scope = scope; 191 return this; 192 } 193 194 /** 195 * Builds a new AclEntry populated with the set properties. 196 * 197 * @return AclEntry new AclEntry 198 */ 199 public AclEntry build() { 200 return new AclEntry(type, name, permission, scope); 201 } 202 } 203 204 /** 205 * Private constructor. 206 * 207 * @param type AclEntryType ACL entry type 208 * @param name String optional ACL entry name 209 * @param permission FsAction set of permissions in the ACL entry 210 * @param scope AclEntryScope scope of the ACL entry 211 */ 212 private AclEntry(AclEntryType type, String name, FsAction permission, AclEntryScope scope) { 213 this.type = type; 214 this.name = name; 215 this.permission = permission; 216 this.scope = scope; 217 } 218 219 /** 220 * Parses a string representation of an ACL spec into a list of AclEntry 221 * objects. Example: "user::rwx,user:foo:rw-,group::r--,other::---" 222 * The expected format of ACL entries in the string parameter is the same 223 * format produced by the {@link #toStringStable()} method. 224 * 225 * @param aclSpec 226 * String representation of an ACL spec. 227 * @param includePermission 228 * for setAcl operations this will be true. i.e. AclSpec should 229 * include permissions.<br> 230 * But for removeAcl operation it will be false. i.e. AclSpec should 231 * not contain permissions.<br> 232 * Example: "user:foo,group:bar" 233 * @return Returns list of {@link AclEntry} parsed 234 */ 235 public static List<AclEntry> parseAclSpec(String aclSpec, 236 boolean includePermission) { 237 List<AclEntry> aclEntries = new ArrayList<AclEntry>(); 238 Collection<String> aclStrings = StringUtils.getStringCollection(aclSpec, 239 ","); 240 for (String aclStr : aclStrings) { 241 AclEntry aclEntry = parseAclEntry(aclStr, includePermission); 242 aclEntries.add(aclEntry); 243 } 244 return aclEntries; 245 } 246 247 /** 248 * Parses a string representation of an ACL into a AclEntry object.<br> 249 * The expected format of ACL entries in the string parameter is the same 250 * format produced by the {@link #toStringStable()} method. 251 * 252 * @param aclStr 253 * String representation of an ACL.<br> 254 * Example: "user:foo:rw-" 255 * @param includePermission 256 * for setAcl operations this will be true. i.e. Acl should include 257 * permissions.<br> 258 * But for removeAcl operation it will be false. i.e. Acl should not 259 * contain permissions.<br> 260 * Example: "user:foo,group:bar,mask::" 261 * @return Returns an {@link AclEntry} object 262 */ 263 public static AclEntry parseAclEntry(String aclStr, 264 boolean includePermission) { 265 AclEntry.Builder builder = new AclEntry.Builder(); 266 // Here "::" represent one empty string. 267 // StringUtils.getStringCollection() will ignore this. 268 String[] split = aclStr.split(":"); 269 270 if (split.length == 0) { 271 throw new HadoopIllegalArgumentException("Invalid <aclSpec> : " + aclStr); 272 } 273 int index = 0; 274 if ("default".equals(split[0])) { 275 // default entry 276 index++; 277 builder.setScope(AclEntryScope.DEFAULT); 278 } 279 280 if (split.length <= index) { 281 throw new HadoopIllegalArgumentException("Invalid <aclSpec> : " + aclStr); 282 } 283 284 AclEntryType aclType = null; 285 try { 286 aclType = Enum.valueOf( 287 AclEntryType.class, StringUtils.toUpperCase(split[index])); 288 builder.setType(aclType); 289 index++; 290 } catch (IllegalArgumentException iae) { 291 throw new HadoopIllegalArgumentException( 292 "Invalid type of acl in <aclSpec> :" + aclStr); 293 } 294 295 if (split.length > index) { 296 String name = split[index]; 297 if (!name.isEmpty()) { 298 builder.setName(name); 299 } 300 index++; 301 } 302 303 if (includePermission) { 304 if (split.length <= index) { 305 throw new HadoopIllegalArgumentException("Invalid <aclSpec> : " 306 + aclStr); 307 } 308 String permission = split[index]; 309 FsAction fsAction = FsAction.getFsAction(permission); 310 if (null == fsAction) { 311 throw new HadoopIllegalArgumentException( 312 "Invalid permission in <aclSpec> : " + aclStr); 313 } 314 builder.setPermission(fsAction); 315 index++; 316 } 317 318 if (split.length > index) { 319 throw new HadoopIllegalArgumentException("Invalid <aclSpec> : " + aclStr); 320 } 321 AclEntry aclEntry = builder.build(); 322 return aclEntry; 323 } 324 325 /** 326 * Convert a List of AclEntries into a string - the reverse of parseAclSpec. 327 * @param aclSpec List of AclEntries to convert 328 * @return String representation of aclSpec 329 */ 330 public static String aclSpecToString(List<AclEntry> aclSpec) { 331 StringBuilder buf = new StringBuilder(); 332 for ( AclEntry e : aclSpec ) { 333 buf.append(e.toString()); 334 buf.append(","); 335 } 336 return buf.substring(0, buf.length()-1); // remove last , 337 } 338}