1 /*
2  * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package java.nio.file.attribute;
27 
28 import java.util.*;
29 
30 /**
31  * An entry in an access control list (ACL).
32  *
33  * <p> The ACL entry represented by this class is based on the ACL model
34  * specified in <a href="http://www.ietf.org/rfc/rfc3530.txt"><i>RFC&nbsp;3530:
35  * Network File System (NFS) version 4 Protocol</i></a>. Each entry has four
36  * components as follows:
37  *
38  * <ol>
39  *    <li><p> The {@link #type() type} component determines if the entry
40  *    grants or denies access. </p></li>
41  *
42  *    <li><p> The {@link #principal() principal} component, sometimes called the
43  *    "who" component, is a {@link UserPrincipal} corresponding to the identity
44  *    that the entry grants or denies access
45  *    </p></li>
46  *
47  *    <li><p> The {@link #permissions permissions} component is a set of
48  *    {@link AclEntryPermission permissions}
49  *    </p></li>
50  *
51  *    <li><p> The {@link #flags flags} component is a set of {@link AclEntryFlag
52  *    flags} to indicate how entries are inherited and propagated </p></li>
53  * </ol>
54  *
55  * <p> ACL entries are created using an associated {@link Builder} object by
56  * invoking its {@link Builder#build build} method.
57  *
58  * <p> ACL entries are immutable and are safe for use by multiple concurrent
59  * threads.
60  *
61  * @since 1.7
62  */
63 
64 public final class AclEntry {
65 
66     private final AclEntryType type;
67     private final UserPrincipal who;
68     private final Set<AclEntryPermission> perms;
69     private final Set<AclEntryFlag> flags;
70 
71     // cached hash code
72     private volatile int hash;
73 
74     // private constructor
AclEntry(AclEntryType type, UserPrincipal who, Set<AclEntryPermission> perms, Set<AclEntryFlag> flags)75     private AclEntry(AclEntryType type,
76                      UserPrincipal who,
77                      Set<AclEntryPermission> perms,
78                      Set<AclEntryFlag> flags)
79     {
80         this.type = type;
81         this.who = who;
82         this.perms = perms;
83         this.flags = flags;
84     }
85 
86     /**
87      * A builder of {@link AclEntry} objects.
88      *
89      * <p> A {@code Builder} object is obtained by invoking one of the {@link
90      * AclEntry#newBuilder newBuilder} methods defined by the {@code AclEntry}
91      * class.
92      *
93      * <p> Builder objects are mutable and are not safe for use by multiple
94      * concurrent threads without appropriate synchronization.
95      *
96      * @since 1.7
97      */
98     public static final class Builder {
99         private AclEntryType type;
100         private UserPrincipal who;
101         private Set<AclEntryPermission> perms;
102         private Set<AclEntryFlag> flags;
103 
Builder(AclEntryType type, UserPrincipal who, Set<AclEntryPermission> perms, Set<AclEntryFlag> flags)104         private Builder(AclEntryType type,
105                         UserPrincipal who,
106                         Set<AclEntryPermission> perms,
107                         Set<AclEntryFlag> flags)
108         {
109             assert perms != null && flags != null;
110             this.type = type;
111             this.who = who;
112             this.perms = perms;
113             this.flags = flags;
114         }
115 
116         /**
117          * Constructs an {@link AclEntry} from the components of this builder.
118          * The type and who components are required to have been set in order
119          * to construct an {@code AclEntry}.
120          *
121          * @return  a new ACL entry
122          *
123          * @throws  IllegalStateException
124          *          if the type or who component have not been set
125          */
build()126         public AclEntry build() {
127             if (type == null)
128                 throw new IllegalStateException("Missing type component");
129             if (who == null)
130                 throw new IllegalStateException("Missing who component");
131             return new AclEntry(type, who, perms, flags);
132         }
133 
134         /**
135          * Sets the type component of this builder.
136          *
137          * @param   type  the component type
138          * @return  this builder
139          */
setType(AclEntryType type)140         public Builder setType(AclEntryType type) {
141             if (type == null)
142                 throw new NullPointerException();
143             this.type = type;
144             return this;
145         }
146 
147         /**
148          * Sets the principal component of this builder.
149          *
150          * @param   who  the principal component
151          * @return  this builder
152          */
setPrincipal(UserPrincipal who)153         public Builder setPrincipal(UserPrincipal who) {
154             if (who == null)
155                 throw new NullPointerException();
156             this.who = who;
157             return this;
158         }
159 
160         // check set only contains elements of the given type
checkSet(Set<?> set, Class<?> type)161         private static void checkSet(Set<?> set, Class<?> type) {
162             for (Object e: set) {
163                 if (e == null)
164                     throw new NullPointerException();
165                 type.cast(e);
166             }
167         }
168 
169         /**
170          * Sets the permissions component of this builder. On return, the
171          * permissions component of this builder is a copy of the given set.
172          *
173          * @param   perms  the permissions component
174          * @return  this builder
175          *
176          * @throws  ClassCastException
177          *          if the set contains elements that are not of type {@code
178          *          AclEntryPermission}
179          */
setPermissions(Set<AclEntryPermission> perms)180         public Builder setPermissions(Set<AclEntryPermission> perms) {
181             if (perms.isEmpty()) {
182                 // EnumSet.copyOf does not allow empty set
183                 perms = Collections.emptySet();
184             } else {
185                 // copy and check for erroneous elements
186                 perms = EnumSet.copyOf(perms);
187                 checkSet(perms, AclEntryPermission.class);
188             }
189 
190             this.perms = perms;
191             return this;
192         }
193 
194         /**
195          * Sets the permissions component of this builder. On return, the
196          * permissions component of this builder is a copy of the permissions in
197          * the given array.
198          *
199          * @param   perms  the permissions component
200          * @return  this builder
201          */
setPermissions(AclEntryPermission... perms)202         public Builder setPermissions(AclEntryPermission... perms) {
203             Set<AclEntryPermission> set = EnumSet.noneOf(AclEntryPermission.class);
204             // copy and check for null elements
205             for (AclEntryPermission p: perms) {
206                 if (p == null)
207                     throw new NullPointerException();
208                 set.add(p);
209             }
210             this.perms = set;
211             return this;
212         }
213 
214         /**
215          * Sets the flags component of this builder. On return, the flags
216          * component of this builder is a copy of the given set.
217          *
218          * @param   flags  the flags component
219          * @return  this builder
220          *
221          * @throws  ClassCastException
222          *          if the set contains elements that are not of type {@code
223          *          AclEntryFlag}
224          */
setFlags(Set<AclEntryFlag> flags)225         public Builder setFlags(Set<AclEntryFlag> flags) {
226             if (flags.isEmpty()) {
227                 // EnumSet.copyOf does not allow empty set
228                 flags = Collections.emptySet();
229             } else {
230                 // copy and check for erroneous elements
231                 flags = EnumSet.copyOf(flags);
232                 checkSet(flags, AclEntryFlag.class);
233             }
234 
235             this.flags = flags;
236             return this;
237         }
238 
239         /**
240          * Sets the flags component of this builder. On return, the flags
241          * component of this builder is a copy of the flags in the given
242          * array.
243          *
244          * @param   flags  the flags component
245          * @return  this builder
246          */
setFlags(AclEntryFlag... flags)247         public Builder setFlags(AclEntryFlag... flags) {
248             Set<AclEntryFlag> set = EnumSet.noneOf(AclEntryFlag.class);
249             // copy and check for null elements
250             for (AclEntryFlag f: flags) {
251                 if (f == null)
252                     throw new NullPointerException();
253                 set.add(f);
254             }
255             this.flags = set;
256             return this;
257         }
258     }
259 
260     /**
261      * Constructs a new builder. The initial value of the type and who
262      * components is {@code null}. The initial value of the permissions and
263      * flags components is the empty set.
264      *
265      * @return  a new builder
266      */
newBuilder()267     public static Builder newBuilder() {
268         Set<AclEntryPermission> perms = Collections.emptySet();
269         Set<AclEntryFlag> flags = Collections.emptySet();
270         return new Builder(null, null, perms, flags);
271     }
272 
273     /**
274      * Constructs a new builder with the components of an existing ACL entry.
275      *
276      * @param   entry  an ACL entry
277      * @return  a new builder
278      */
newBuilder(AclEntry entry)279     public static Builder newBuilder(AclEntry entry) {
280         return new Builder(entry.type, entry.who, entry.perms, entry.flags);
281     }
282 
283     /**
284      * Returns the ACL entry type.
285      *
286      * @return the ACL entry type
287      */
type()288     public AclEntryType type() {
289         return type;
290     }
291 
292     /**
293      * Returns the principal component.
294      *
295      * @return the principal component
296      */
principal()297     public UserPrincipal principal() {
298         return who;
299     }
300 
301     /**
302      * Returns a copy of the permissions component.
303      *
304      * <p> The returned set is a modifiable copy of the permissions.
305      *
306      * @return the permissions component
307      */
permissions()308     public Set<AclEntryPermission> permissions() {
309         return new HashSet<AclEntryPermission>(perms);
310     }
311 
312     /**
313      * Returns a copy of the flags component.
314      *
315      * <p> The returned set is a modifiable copy of the flags.
316      *
317      * @return the flags component
318      */
flags()319     public Set<AclEntryFlag> flags() {
320         return new HashSet<AclEntryFlag>(flags);
321     }
322 
323     /**
324      * Compares the specified object with this ACL entry for equality.
325      *
326      * <p> If the given object is not an {@code AclEntry} then this method
327      * immediately returns {@code false}.
328      *
329      * <p> For two ACL entries to be considered equals requires that they are
330      * both the same type, their who components are equal, their permissions
331      * components are equal, and their flags components are equal.
332      *
333      * <p> This method satisfies the general contract of the {@link
334      * java.lang.Object#equals(Object) Object.equals} method. </p>
335      *
336      * @param   ob   the object to which this object is to be compared
337      *
338      * @return  {@code true} if, and only if, the given object is an AclEntry that
339      *          is identical to this AclEntry
340      */
341     @Override
equals(Object ob)342     public boolean equals(Object ob) {
343         if (ob == this)
344             return true;
345         if (ob == null || !(ob instanceof AclEntry))
346             return false;
347         AclEntry other = (AclEntry)ob;
348         if (this.type != other.type)
349             return false;
350         if (!this.who.equals(other.who))
351             return false;
352         if (!this.perms.equals(other.perms))
353             return false;
354         if (!this.flags.equals(other.flags))
355             return false;
356         return true;
357     }
358 
hash(int h, Object o)359     private static int hash(int h, Object o) {
360         return h * 127 + o.hashCode();
361     }
362 
363     /**
364      * Returns the hash-code value for this ACL entry.
365      *
366      * <p> This method satisfies the general contract of the {@link
367      * Object#hashCode} method.
368      */
369     @Override
hashCode()370     public int hashCode() {
371         // return cached hash if available
372         if (hash != 0)
373             return hash;
374         int h = type.hashCode();
375         h = hash(h, who);
376         h = hash(h, perms);
377         h = hash(h, flags);
378         hash = h;
379         return hash;
380     }
381 
382     /**
383      * Returns the string representation of this ACL entry.
384      *
385      * @return  the string representation of this entry
386      */
387     @Override
toString()388     public String toString() {
389         StringBuilder sb = new StringBuilder();
390 
391         // who
392         sb.append(who.getName());
393         sb.append(':');
394 
395         // permissions
396         for (AclEntryPermission perm: perms) {
397             sb.append(perm.name());
398             sb.append('/');
399         }
400         sb.setLength(sb.length()-1); // drop final slash
401         sb.append(':');
402 
403         // flags
404         if (!flags.isEmpty()) {
405             for (AclEntryFlag flag: flags) {
406                 sb.append(flag.name());
407                 sb.append('/');
408             }
409             sb.setLength(sb.length()-1);  // drop final slash
410             sb.append(':');
411         }
412 
413         // type
414         sb.append(type.name());
415         return sb.toString();
416     }
417 }
418