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.Evolving 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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}