Search code examples
javawindowsfilepermissionsacl

How to set "modify" ACL on a Windows file using Java


How do you set the Windows ACL using Java 7's File API so that it mimics adding the user/group and the following options:

For example, in the OS Properties dialog, you can select the following for basic write access:

  • Modify
  • ☑ Read and Execute (auto-selected)
  • ☑ List folder Contents (auto-selected)
  • ☑ Read (auto-selected)
  • ☑ Write (auto-selected)

However, when I use similar options using Java 7's File API, it instead selects:

  • ☑ Special
    • ☑ Advanced (NOT Edit, Click User or Group, View)
      • Show Advanced Permissions
      • Advanced Permissions
        • ☑ (Bunch of checkboxes)

Not only is this harder to administer (traversing dialogs is easier to miss something), it doesn't behave the same was as simply clicking those boxes. Some of the UAC prompts behave differently. Worse, it's harder to toggle the permissions (e.g. from the Windows desktop) due to the dialog traversing, leaving more chance of making a mistake.

How do I use Java to set these checkboxes?

UserPrincipal authenticatedUsers = path.getFileSystem().getUserPrincipalLookupService()
        .lookupPrincipalByName("Authenticated Users");
AclFileAttributeView view = Files.getFileAttributeView(path, AclFileAttributeView.class);

// Create ACL to give "Authenticated Users" "modify" access
AclEntry entry = AclEntry.newBuilder()
        .setType(AclEntryType.ALLOW)
        .setPrincipal(authenticatedUsers)
        .setPermissions(DELETE, DELETE_CHILD, LIST_DIRECTORY, READ_DATA, READ_ATTRIBUTES, ADD_FILE, WRITE_DATA, WRITE_ATTRIBUTES)
        .build();

List<AclEntry> acl = view.getAcl();
acl.add(0, entry); // insert before any DENY entries
view.setAcl(acl);

Solution

  • I was able to mimic the Windows Properties file permissions by making a folder that I wanted to mimic, setting the values through the Properties dialog, then echoing them back out...

    // Echo ACL
    Path path = Paths.get("C:\\myfolder");
    
    UserPrincipal authenticatedUsers = path.getFileSystem().getUserPrincipalLookupService()
            .lookupPrincipalByName("Authenticated Users");
    
    AclFileAttributeView view = Files.getFileAttributeView(path, AclFileAttributeView.class);
    for(AclEntry entry : view.getAcl()) {
        if(entry.principal().equals(authenticatedUsers)) {
            System.out.println("=== flags ===");
            for (AclEntryFlag flags : entry.flags()) {
                System.out.println(flags.name());
            }
    
            System.out.println("=== permissions ===");
            for (AclEntryPermission permission : entry.permissions()) {
                System.out.println(permission.name());
            }
        }
    }
    

    The output:

    === flags ===
    DIRECTORY_INHERIT
    FILE_INHERIT
    
    === permissions ===
    WRITE_NAMED_ATTRS
    WRITE_ATTRIBUTES
    DELETE
    WRITE_DATA
    READ_ACL
    APPEND_DATA
    READ_ATTRIBUTES
    READ_DATA
    EXECUTE
    SYNCHRONIZE
    READ_NAMED_ATTRS
    

    Then, I was able to plug these values back into my original example:

    UserPrincipal authenticatedUsers = path.getFileSystem().getUserPrincipalLookupService()
            .lookupPrincipalByName("Authenticated Users");
    AclFileAttributeView view = Files.getFileAttributeView(path, AclFileAttributeView.class);
    
    // Create ACL to give "Authenticated Users" "modify" access
    AclEntry entry = AclEntry.newBuilder()
            .setType(AclEntryType.ALLOW)
            .setPrincipal(authenticatedUsers)
            .setFlags(DIRECTORY_INHERIT,
                      FILE_INHERIT)
            .setPermissions(WRITE_NAMED_ATTRS,
                            WRITE_ATTRIBUTES,
                            DELETE,
                            WRITE_DATA,
                            READ_ACL,
                            APPEND_DATA,
                            READ_ATTRIBUTES,
                            READ_DATA,
                            EXECUTE,
                            SYNCHRONIZE,
                            READ_NAMED_ATTRS)
            .build();
    

    ... and now "Modify" is checked exactly as desired.