#!/usr/local/bin/python

# How to modify POSIX ACLs with Python on FreeBSD
#
# 1. pkg install python
# (or: make -C /usr/ports/lang/python install clean)
#
# 2. pkg install py312-pylibacl
# (or: make -C /usr/ports/security/py-pylibacl install clean)
#
# Enable POSIX.1e ACLs on a filesystem, e.g.:
# 3. umount /mnt
# 4. tunefs -a enable /dev/vtbd1p1
# 5. mount /dev/vtbd1p1 /mnt
# (n.b. For the root filesystem, you have to reboot into single user mode.)
#

import posix1e

with open("/mnt/readme.txt", "w"):
    # make sure file exists
    pass

with open("/mnt/readme.txt") as fh:
    # load ACLs from existing file
    acl = posix1e.ACL(fd=fh)
    print("Existing ACL:")
    print(acl)

    # remove any ACL entry that we will add anyways
    for entry in acl:
        if entry.tag_type == posix1e.ACL_USER:
            acl.delete_entry(entry)
        if entry.tag_type == posix1e.ACL_MASK:
            acl.delete_entry(entry)

    # grant user id 2 read and write access
    entry = acl.append()
    entry.tag_type = posix1e.ACL_USER
    entry.qualifier = 2

    perms = entry.permset
    perms.add(posix1e.ACL_READ)
    perms.add(posix1e.ACL_WRITE)

    # a mask is required
    # here, mask limits any extended permissions to read access
    entry = acl.append()
    entry.tag_type = posix1e.ACL_MASK

    perms = entry.permset
    perms.add(posix1e.ACL_READ)

    # apply new acl to the file
    print("Modified ACL:")
    print(acl)
    acl.applyto(fh)


# alternatively, start with a fresh ACL with no permissions at all
acl = posix1e.ACL()

# grant read+write access to the owner
entry = acl.append()
entry.tag_type = posix1e.ACL_USER_OBJ
perms = entry.permset
perms.add(posix1e.ACL_READ)
perms.add(posix1e.ACL_WRITE)

# grant read+write access to the group
entry = acl.append()
entry.tag_type = posix1e.ACL_GROUP_OBJ
perms = entry.permset
perms.add(posix1e.ACL_READ)
perms.add(posix1e.ACL_WRITE)

# ensure no other user has access
entry = acl.append()
entry.tag_type = posix1e.ACL_OTHER
perms = entry.permset
perms.clear()

# but as above, grant read+write access to user id 2
entry = acl.append()
entry.tag_type = posix1e.ACL_USER
entry.qualifier = 2

perms = entry.permset
perms.add(posix1e.ACL_READ)
perms.add(posix1e.ACL_WRITE)

# and again, mask limits any extended permissions
entry = acl.append()
entry.tag_type = posix1e.ACL_MASK

perms = entry.permset
perms.add(posix1e.ACL_READ)

# apply acl to the file
print("New ACL:")
print(acl)
with open("/mnt/readme.txt") as fh:
    acl.applyto(fh)

# Now, check with: getfacl /mnt/readme.txt
