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(type.toString().toLowerCase()); 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 this.name = name; 150 return this; 151 } 152 153 /** 154 * Sets the set of permissions in the ACL entry. 155 * 156 * @param permission FsAction set of permissions in the ACL entry 157 * @return Builder this builder, for call chaining 158 */ 159 public Builder setPermission(FsAction permission) { 160 this.permission = permission; 161 return this; 162 } 163 164 /** 165 * Sets the scope of the ACL entry. If this method is not called, then the 166 * builder assumes {@link AclEntryScope#ACCESS}. 167 * 168 * @param scope AclEntryScope scope of the ACL entry 169 * @return Builder this builder, for call chaining 170 */ 171 public Builder setScope(AclEntryScope scope) { 172 this.scope = scope; 173 return this; 174 } 175 176 /** 177 * Builds a new AclEntry populated with the set properties. 178 * 179 * @return AclEntry new AclEntry 180 */ 181 public AclEntry build() { 182 return new AclEntry(type, name, permission, scope); 183 } 184 } 185 186 /** 187 * Private constructor. 188 * 189 * @param type AclEntryType ACL entry type 190 * @param name String optional ACL entry name 191 * @param permission FsAction set of permissions in the ACL entry 192 * @param scope AclEntryScope scope of the ACL entry 193 */ 194 private AclEntry(AclEntryType type, String name, FsAction permission, AclEntryScope scope) { 195 this.type = type; 196 this.name = name; 197 this.permission = permission; 198 this.scope = scope; 199 } 200 201 /** 202 * Parses a string representation of an ACL spec into a list of AclEntry 203 * objects. Example: "user::rwx,user:foo:rw-,group::r--,other::---" 204 * 205 * @param aclSpec 206 * String representation of an ACL spec. 207 * @param includePermission 208 * for setAcl operations this will be true. i.e. AclSpec should 209 * include permissions.<br> 210 * But for removeAcl operation it will be false. i.e. AclSpec should 211 * not contain permissions.<br> 212 * Example: "user:foo,group:bar" 213 * @return Returns list of {@link AclEntry} parsed 214 */ 215 public static List<AclEntry> parseAclSpec(String aclSpec, 216 boolean includePermission) { 217 List<AclEntry> aclEntries = new ArrayList<AclEntry>(); 218 Collection<String> aclStrings = StringUtils.getStringCollection(aclSpec, 219 ","); 220 for (String aclStr : aclStrings) { 221 AclEntry aclEntry = parseAclEntry(aclStr, includePermission); 222 aclEntries.add(aclEntry); 223 } 224 return aclEntries; 225 } 226 227 /** 228 * Parses a string representation of an ACL into a AclEntry object.<br> 229 * 230 * @param aclStr 231 * String representation of an ACL.<br> 232 * Example: "user:foo:rw-" 233 * @param includePermission 234 * for setAcl operations this will be true. i.e. Acl should include 235 * permissions.<br> 236 * But for removeAcl operation it will be false. i.e. Acl should not 237 * contain permissions.<br> 238 * Example: "user:foo,group:bar,mask::" 239 * @return Returns an {@link AclEntry} object 240 */ 241 public static AclEntry parseAclEntry(String aclStr, 242 boolean includePermission) { 243 AclEntry.Builder builder = new AclEntry.Builder(); 244 // Here "::" represent one empty string. 245 // StringUtils.getStringCollection() will ignore this. 246 String[] split = aclStr.split(":"); 247 248 if (split.length == 0) { 249 throw new HadoopIllegalArgumentException("Invalid <aclSpec> : " + aclStr); 250 } 251 int index = 0; 252 if ("default".equals(split[0])) { 253 // default entry 254 index++; 255 builder.setScope(AclEntryScope.DEFAULT); 256 } 257 258 if (split.length <= index) { 259 throw new HadoopIllegalArgumentException("Invalid <aclSpec> : " + aclStr); 260 } 261 262 AclEntryType aclType = null; 263 try { 264 aclType = Enum.valueOf(AclEntryType.class, split[index].toUpperCase()); 265 builder.setType(aclType); 266 index++; 267 } catch (IllegalArgumentException iae) { 268 throw new HadoopIllegalArgumentException( 269 "Invalid type of acl in <aclSpec> :" + aclStr); 270 } 271 272 if (split.length > index) { 273 String name = split[index]; 274 if (!name.isEmpty()) { 275 builder.setName(name); 276 } 277 index++; 278 } 279 280 if (includePermission) { 281 if (split.length <= index) { 282 throw new HadoopIllegalArgumentException("Invalid <aclSpec> : " 283 + aclStr); 284 } 285 String permission = split[index]; 286 FsAction fsAction = FsAction.getFsAction(permission); 287 if (null == fsAction) { 288 throw new HadoopIllegalArgumentException( 289 "Invalid permission in <aclSpec> : " + aclStr); 290 } 291 builder.setPermission(fsAction); 292 index++; 293 } 294 295 if (split.length > index) { 296 throw new HadoopIllegalArgumentException("Invalid <aclSpec> : " + aclStr); 297 } 298 AclEntry aclEntry = builder.build(); 299 return aclEntry; 300 } 301 302 /** 303 * Convert a List of AclEntries into a string - the reverse of parseAclSpec. 304 * @param aclSpec List of AclEntries to convert 305 * @return String representation of aclSpec 306 */ 307 public static String aclSpecToString(List<AclEntry> aclSpec) { 308 StringBuilder buf = new StringBuilder(); 309 for ( AclEntry e : aclSpec ) { 310 buf.append(e.toString()); 311 buf.append(","); 312 } 313 return buf.substring(0, buf.length()-1); // remove last , 314 } 315}