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