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 */
018 package org.apache.hadoop.fs.permission;
019
020 import java.util.ArrayList;
021 import java.util.Collection;
022 import java.util.List;
023
024 import com.google.common.base.Objects;
025
026 import org.apache.hadoop.HadoopIllegalArgumentException;
027 import org.apache.hadoop.classification.InterfaceAudience;
028 import org.apache.hadoop.classification.InterfaceStability;
029 import 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
040 public 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 }