| /* |
| * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * - Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * - Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * - Neither the name of Oracle nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
| * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /* |
| * This source code is provided to illustrate the usage of a given feature |
| * or technique and has been deliberately simplified. Additional steps |
| * required for a production-quality application, such as security checks, |
| * input validation and proper error handling, might not be present in |
| * this sample code. |
| */ |
| |
| |
| import java.nio.file.*; |
| import java.nio.file.attribute.*; |
| import java.io.IOException; |
| import java.util.*; |
| import java.util.regex.Pattern; |
| |
| /** |
| * Sample utility for editing a file's ACL. |
| */ |
| |
| public class AclEdit { |
| |
| // parse string as list of ACE permissions separated by / |
| static Set<AclEntryPermission> parsePermissions(String permsString) { |
| Set<AclEntryPermission> perms = new HashSet<AclEntryPermission>(); |
| String[] result = permsString.split("/"); |
| for (String s : result) { |
| if (s.equals("")) |
| continue; |
| try { |
| perms.add(AclEntryPermission.valueOf(s.toUpperCase())); |
| } catch (IllegalArgumentException x) { |
| System.err.format("Invalid permission '%s'\n", s); |
| System.exit(-1); |
| } |
| } |
| return perms; |
| } |
| |
| // parse string as list of ACE flags separated by / |
| static Set<AclEntryFlag> parseFlags(String flagsString) { |
| Set<AclEntryFlag> flags = new HashSet<AclEntryFlag>(); |
| String[] result = flagsString.split("/"); |
| for (String s : result) { |
| if (s.equals("")) |
| continue; |
| try { |
| flags.add(AclEntryFlag.valueOf(s.toUpperCase())); |
| } catch (IllegalArgumentException x) { |
| System.err.format("Invalid flag '%s'\n", s); |
| System.exit(-1); |
| } |
| } |
| return flags; |
| } |
| |
| // parse ACE type |
| static AclEntryType parseType(String typeString) { |
| // FIXME: support audit and alarm types in the future |
| if (typeString.equalsIgnoreCase("allow")) |
| return AclEntryType.ALLOW; |
| if (typeString.equalsIgnoreCase("deny")) |
| return AclEntryType.DENY; |
| System.err.format("Invalid type '%s'\n", typeString); |
| System.exit(-1); |
| return null; // keep compiler happy |
| } |
| |
| /** |
| * Parse string of the form: |
| * [user|group:]<username|groupname>:<perms>[:flags]:<allow|deny> |
| */ |
| static AclEntry parseAceString(String s, |
| UserPrincipalLookupService lookupService) |
| { |
| String[] result = s.split(":"); |
| |
| // must have at least 3 components (username:perms:type) |
| if (result.length < 3) |
| usage(); |
| |
| int index = 0; |
| int remaining = result.length; |
| |
| // optional first component can indicate user or group type |
| boolean isGroup = false; |
| if (result[index].equalsIgnoreCase("user") || |
| result[index].equalsIgnoreCase("group")) |
| { |
| if (--remaining < 3) |
| usage(); |
| isGroup = result[index++].equalsIgnoreCase("group"); |
| } |
| |
| // user and permissions required |
| String userString = result[index++]; remaining--; |
| String permsString = result[index++]; remaining--; |
| |
| // flags are optional |
| String flagsString = ""; |
| String typeString = null; |
| if (remaining == 1) { |
| typeString = result[index++]; |
| } else { |
| if (remaining == 2) { |
| flagsString = result[index++]; |
| typeString = result[index++]; |
| } else { |
| usage(); |
| } |
| } |
| |
| // lookup UserPrincipal |
| UserPrincipal user = null; |
| try { |
| user = (isGroup) ? |
| lookupService.lookupPrincipalByGroupName(userString) : |
| lookupService.lookupPrincipalByName(userString); |
| } catch (UserPrincipalNotFoundException x) { |
| System.err.format("Invalid %s '%s'\n", |
| ((isGroup) ? "group" : "user"), |
| userString); |
| System.exit(-1); |
| } catch (IOException x) { |
| System.err.format("Lookup of '%s' failed: %s\n", userString, x); |
| System.exit(-1); |
| } |
| |
| // map string representation of permissions, flags, and type |
| Set<AclEntryPermission> perms = parsePermissions(permsString); |
| Set<AclEntryFlag> flags = parseFlags(flagsString); |
| AclEntryType type = parseType(typeString); |
| |
| // build the ACL entry |
| return AclEntry.newBuilder() |
| .setType(type) |
| .setPrincipal(user) |
| .setPermissions(perms).setFlags(flags).build(); |
| } |
| |
| static void usage() { |
| System.err.println("usage: java AclEdit [ACL-operation] file"); |
| System.err.println(""); |
| System.err.println("Example 1: Prepends access control entry to the begining of the myfile's ACL"); |
| System.err.println(" java AclEdit A+alice:read_data/read_attributes:allow myfile"); |
| System.err.println(""); |
| System.err.println("Example 2: Remove the entry at index 6 of myfile's ACL"); |
| System.err.println(" java AclEdit A6- myfile"); |
| System.err.println(""); |
| System.err.println("Example 3: Replace the entry at index 2 of myfile's ACL"); |
| System.err.println(" java AclEdit A2=bob:write_data/append_data:deny myfile"); |
| System.exit(-1); |
| } |
| |
| static enum Action { |
| PRINT, |
| ADD, |
| REMOVE, |
| REPLACE; |
| } |
| |
| /** |
| * Main class: parses arguments and prints or edits ACL |
| */ |
| public static void main(String[] args) throws IOException { |
| Action action = null; |
| int index = -1; |
| String entryString = null; |
| |
| // parse arguments |
| if (args.length < 1 || args[0].equals("-help") || args[0].equals("-?")) |
| usage(); |
| |
| if (args.length == 1) { |
| action = Action.PRINT; |
| } else { |
| String s = args[0]; |
| |
| // A[index]+entry |
| if (Pattern.matches("^A[0-9]*\\+.*", s)) { |
| String[] result = s.split("\\+", 2); |
| if (result.length == 2) { |
| if (result[0].length() < 2) { |
| index = 0; |
| } else { |
| index = Integer.parseInt(result[0].substring(1)); |
| } |
| entryString = result[1]; |
| action = Action.ADD; |
| } |
| } |
| |
| // Aindex- |
| if (Pattern.matches("^A[0-9]+\\-", s)) { |
| String[] result = s.split("\\-", 2); |
| if (result.length == 2) { |
| index = Integer.parseInt(result[0].substring(1)); |
| entryString = result[1]; |
| action = Action.REMOVE; |
| } |
| } |
| |
| // Aindex=entry |
| if (Pattern.matches("^A[0-9]+=.*", s)) { |
| String[] result = s.split("=", 2); |
| if (result.length == 2) { |
| index = Integer.parseInt(result[0].substring(1)); |
| entryString = result[1]; |
| action = Action.REPLACE; |
| } |
| } |
| } |
| if (action == null) |
| usage(); |
| |
| int fileArg = (action == Action.PRINT) ? 0 : 1; |
| Path file = Paths.get(args[fileArg]); |
| |
| // read file's ACL |
| AclFileAttributeView view = |
| Files.getFileAttributeView(file, AclFileAttributeView.class); |
| if (view == null) { |
| System.err.println("ACLs not supported on this platform"); |
| System.exit(-1); |
| } |
| List<AclEntry> acl = view.getAcl(); |
| |
| switch (action) { |
| // print ACL |
| case PRINT : { |
| for (int i=0; i<acl.size(); i++) { |
| System.out.format("%5d: %s\n", i, acl.get(i)); |
| } |
| break; |
| } |
| |
| // add ACE to existing ACL |
| case ADD: { |
| AclEntry entry = parseAceString(entryString, file |
| .getFileSystem().getUserPrincipalLookupService()); |
| if (index >= acl.size()) { |
| acl.add(entry); |
| } else { |
| acl.add(index, entry); |
| } |
| view.setAcl(acl); |
| break; |
| } |
| |
| // remove ACE |
| case REMOVE: { |
| if (index >= acl.size()) { |
| System.err.format("Index '%d' is invalid", index); |
| System.exit(-1); |
| } |
| acl.remove(index); |
| view.setAcl(acl); |
| break; |
| } |
| |
| // replace ACE |
| case REPLACE: { |
| if (index >= acl.size()) { |
| System.err.format("Index '%d' is invalid", index); |
| System.exit(-1); |
| } |
| AclEntry entry = parseAceString(entryString, file |
| .getFileSystem().getUserPrincipalLookupService()); |
| acl.set(index, entry); |
| view.setAcl(acl); |
| break; |
| } |
| } |
| } |
| } |