jdk/src/share/sample/nio/file/AclEdit.java
changeset 2057 3acf8e5e2ca0
child 5506 202f599c92aa
equal deleted inserted replaced
2056:115e09b7a004 2057:3acf8e5e2ca0
       
     1 /*
       
     2  * Copyright 2008-2009 Sun Microsystems, Inc.  All Rights Reserved.
       
     3  *
       
     4  * Redistribution and use in source and binary forms, with or without
       
     5  * modification, are permitted provided that the following conditions
       
     6  * are met:
       
     7  *
       
     8  *   - Redistributions of source code must retain the above copyright
       
     9  *     notice, this list of conditions and the following disclaimer.
       
    10  *
       
    11  *   - Redistributions in binary form must reproduce the above copyright
       
    12  *     notice, this list of conditions and the following disclaimer in the
       
    13  *     documentation and/or other materials provided with the distribution.
       
    14  *
       
    15  *   - Neither the name of Sun Microsystems nor the names of its
       
    16  *     contributors may be used to endorse or promote products derived
       
    17  *     from this software without specific prior written permission.
       
    18  *
       
    19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
       
    20  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
       
    21  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
       
    22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
       
    23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
       
    24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
       
    25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
       
    26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
       
    27  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
       
    28  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
       
    29  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
    30  */
       
    31 
       
    32 import java.nio.file.*;
       
    33 import java.nio.file.attribute.*;
       
    34 import java.io.IOException;
       
    35 import java.util.*;
       
    36 import java.util.regex.Pattern;
       
    37 
       
    38 /**
       
    39  * Sample utility for editing a file's ACL.
       
    40  */
       
    41 
       
    42 public class AclEdit {
       
    43 
       
    44     // parse string as list of ACE permissions separated by /
       
    45     static Set<AclEntryPermission> parsePermissions(String permsString) {
       
    46         Set<AclEntryPermission> perms = new HashSet<AclEntryPermission>();
       
    47         String[] result = permsString.split("/");
       
    48         for (String s : result) {
       
    49             if (s.equals(""))
       
    50                 continue;
       
    51             try {
       
    52                 perms.add(AclEntryPermission.valueOf(s.toUpperCase()));
       
    53             } catch (IllegalArgumentException x) {
       
    54                 System.err.format("Invalid permission '%s'\n", s);
       
    55                 System.exit(-1);
       
    56             }
       
    57         }
       
    58         return perms;
       
    59     }
       
    60 
       
    61     // parse string as list of ACE flags separated by /
       
    62     static Set<AclEntryFlag> parseFlags(String flagsString) {
       
    63         Set<AclEntryFlag> flags = new HashSet<AclEntryFlag>();
       
    64         String[] result = flagsString.split("/");
       
    65         for (String s : result) {
       
    66             if (s.equals(""))
       
    67                 continue;
       
    68             try {
       
    69                 flags.add(AclEntryFlag.valueOf(s.toUpperCase()));
       
    70             } catch (IllegalArgumentException x) {
       
    71                 System.err.format("Invalid flag '%s'\n", s);
       
    72                 System.exit(-1);
       
    73             }
       
    74         }
       
    75         return flags;
       
    76     }
       
    77 
       
    78     // parse ACE type
       
    79     static AclEntryType parseType(String typeString) {
       
    80         // FIXME: support audit and alarm types in the future
       
    81         if (typeString.equalsIgnoreCase("allow"))
       
    82             return AclEntryType.ALLOW;
       
    83         if (typeString.equalsIgnoreCase("deny"))
       
    84             return AclEntryType.DENY;
       
    85         System.err.format("Invalid type '%s'\n", typeString);
       
    86         System.exit(-1);
       
    87         return null;    // keep compiler happy
       
    88     }
       
    89 
       
    90     /**
       
    91      * Parse string of the form:
       
    92      *   [user|group:]<username|groupname>:<perms>[:flags]:<allow|deny>
       
    93      */
       
    94     static AclEntry parseAceString(String s,
       
    95                                    UserPrincipalLookupService lookupService)
       
    96     {
       
    97         String[] result = s.split(":");
       
    98 
       
    99         // must have at least 3 components (username:perms:type)
       
   100         if (result.length < 3)
       
   101             usage();
       
   102 
       
   103         int index = 0;
       
   104         int remaining = result.length;
       
   105 
       
   106         // optional first component can indicate user or group type
       
   107         boolean isGroup = false;
       
   108         if (result[index].equalsIgnoreCase("user") ||
       
   109             result[index].equalsIgnoreCase("group"))
       
   110         {
       
   111             if (--remaining < 3)
       
   112                 usage();
       
   113             isGroup = result[index++].equalsIgnoreCase("group");
       
   114         }
       
   115 
       
   116         // user and permissions required
       
   117         String userString = result[index++]; remaining--;
       
   118         String permsString = result[index++]; remaining--;
       
   119 
       
   120         // flags are optional
       
   121         String flagsString = "";
       
   122         String typeString = null;
       
   123         if (remaining == 1) {
       
   124             typeString = result[index++];
       
   125         } else {
       
   126             if (remaining == 2) {
       
   127                 flagsString = result[index++];
       
   128                 typeString = result[index++];
       
   129             } else {
       
   130                 usage();
       
   131             }
       
   132         }
       
   133 
       
   134         // lookup UserPrincipal
       
   135         UserPrincipal user = null;
       
   136         try {
       
   137             user = (isGroup) ?
       
   138                 lookupService.lookupPrincipalByGroupName(userString) :
       
   139                 lookupService.lookupPrincipalByName(userString);
       
   140         } catch (UserPrincipalNotFoundException x) {
       
   141             System.err.format("Invalid %s '%s'\n",
       
   142                 ((isGroup) ? "group" : "user"),
       
   143                 userString);
       
   144             System.exit(-1);
       
   145         } catch (IOException x) {
       
   146             System.err.format("Lookup of '%s' failed: %s\n", userString, x);
       
   147             System.exit(-1);
       
   148         }
       
   149 
       
   150         // map string representation of permissions, flags, and type
       
   151         Set<AclEntryPermission> perms = parsePermissions(permsString);
       
   152         Set<AclEntryFlag> flags = parseFlags(flagsString);
       
   153         AclEntryType type = parseType(typeString);
       
   154 
       
   155         // build the ACL entry
       
   156         return AclEntry.newBuilder()
       
   157             .setType(type)
       
   158             .setPrincipal(user)
       
   159             .setPermissions(perms).setFlags(flags).build();
       
   160     }
       
   161 
       
   162     static void usage() {
       
   163         System.err.println("usage: java AclEdit [ACL-operation] file");
       
   164         System.err.println("");
       
   165         System.err.println("Example 1: Prepends access control entry to the begining of the myfile's ACL");
       
   166         System.err.println("       java AclEdit A+alice:read_data/read_attributes:allow myfile");
       
   167         System.err.println("");
       
   168         System.err.println("Example 2: Remove the entry at index 6 of myfile's ACL");
       
   169         System.err.println("       java AclEdit A6- myfile");
       
   170         System.err.println("");
       
   171         System.err.println("Example 3: Replace the entry at index 2 of myfile's ACL");
       
   172         System.err.println("       java AclEdit A2=bob:write_data/append_data:deny myfile");
       
   173         System.exit(-1);
       
   174     }
       
   175 
       
   176     static enum Action {
       
   177         PRINT,
       
   178         ADD,
       
   179         REMOVE,
       
   180         REPLACE;
       
   181     }
       
   182 
       
   183     /**
       
   184      * Main class: parses arguments and prints or edits ACL
       
   185      */
       
   186     public static void main(String[] args) throws IOException {
       
   187         Action action = null;
       
   188         int index = -1;
       
   189         String entryString = null;
       
   190 
       
   191         // parse arguments
       
   192         if (args.length < 1 || args[0].equals("-help") || args[0].equals("-?"))
       
   193             usage();
       
   194 
       
   195         if (args.length == 1) {
       
   196             action = Action.PRINT;
       
   197         } else {
       
   198             String s = args[0];
       
   199 
       
   200             // A[index]+entry
       
   201             if (Pattern.matches("^A[0-9]*\\+.*", s)) {
       
   202                 String[] result = s.split("\\+", 2);
       
   203                 if (result.length == 2) {
       
   204                     if (result[0].length() < 2) {
       
   205                         index = 0;
       
   206                     } else {
       
   207                         index = Integer.parseInt(result[0].substring(1));
       
   208                     }
       
   209                     entryString = result[1];
       
   210                     action = Action.ADD;
       
   211                 }
       
   212             }
       
   213 
       
   214             // Aindex-
       
   215             if (Pattern.matches("^A[0-9]+\\-", s)) {
       
   216                 String[] result = s.split("\\-", 2);
       
   217                 if (result.length == 2) {
       
   218                     index = Integer.parseInt(result[0].substring(1));
       
   219                     entryString = result[1];
       
   220                     action = Action.REMOVE;
       
   221                 }
       
   222             }
       
   223 
       
   224             // Aindex=entry
       
   225             if (Pattern.matches("^A[0-9]+=.*", s)) {
       
   226                 String[] result = s.split("=", 2);
       
   227                 if (result.length == 2) {
       
   228                     index = Integer.parseInt(result[0].substring(1));
       
   229                     entryString = result[1];
       
   230                     action = Action.REPLACE;
       
   231                 }
       
   232             }
       
   233         }
       
   234         if (action == null)
       
   235             usage();
       
   236 
       
   237         int fileArg = (action == Action.PRINT) ? 0 : 1;
       
   238         Path file = Paths.get(args[fileArg]);
       
   239 
       
   240         // read file's ACL
       
   241         AclFileAttributeView view =
       
   242             file.getFileAttributeView(AclFileAttributeView.class);
       
   243         if (view == null) {
       
   244             System.err.println("ACLs not supported on this platform");
       
   245             System.exit(-1);
       
   246         }
       
   247         List<AclEntry> acl = view.getAcl();
       
   248 
       
   249         switch (action) {
       
   250             // print ACL
       
   251             case PRINT : {
       
   252                 for (int i=0; i<acl.size(); i++) {
       
   253                     System.out.format("%5d: %s\n", i, acl.get(i));
       
   254                 }
       
   255                 break;
       
   256             }
       
   257 
       
   258             // add ACE to existing ACL
       
   259             case ADD: {
       
   260                 AclEntry entry = parseAceString(entryString, file
       
   261                     .getFileSystem().getUserPrincipalLookupService());
       
   262                 if (index >= acl.size()) {
       
   263                     acl.add(entry);
       
   264                 } else {
       
   265                     acl.add(index, entry);
       
   266                 }
       
   267                 view.setAcl(acl);
       
   268                 break;
       
   269             }
       
   270 
       
   271             // remove ACE
       
   272             case REMOVE: {
       
   273                 if (index >= acl.size()) {
       
   274                     System.err.format("Index '%d' is invalid", index);
       
   275                     System.exit(-1);
       
   276                 }
       
   277                 acl.remove(index);
       
   278                 view.setAcl(acl);
       
   279                 break;
       
   280             }
       
   281 
       
   282             // replace ACE
       
   283             case REPLACE: {
       
   284                 if (index >= acl.size()) {
       
   285                     System.err.format("Index '%d' is invalid", index);
       
   286                     System.exit(-1);
       
   287                 }
       
   288                 AclEntry entry = parseAceString(entryString, file
       
   289                     .getFileSystem().getUserPrincipalLookupService());
       
   290                 acl.set(index, entry);
       
   291                 view.setAcl(acl);
       
   292                 break;
       
   293             }
       
   294         }
       
   295     }
       
   296 }