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