Implement ACL merging/updating #4
50
pyacl/acl.py
50
pyacl/acl.py
@ -243,6 +243,46 @@ def apply_acl_to_path(acl, path):
|
||||
acl.applyto(path)
|
||||
|
||||
|
||||
def merge_acls(acl1, acl2):
|
||||
"""
|
||||
Example usage: merge_acls(posix1e.ACL, posix1e.ACL)
|
||||
Return: posix1e.ACL
|
||||
"""
|
||||
acl3 = ACL(acl=acl1)
|
||||
for entry in acl2:
|
||||
tag_type = entry.tag_type
|
||||
|
||||
# keep existing entries which may only exist once
|
||||
if tag_type not in [ACL_USER_OBJ, ACL_GROUP_OBJ, ACL_OTHER]:
|
||||
|
||||
# replace existing user/group entries with new ones if the uid/gid matches
|
||||
if tag_type in [ACL_USER, ACL_GROUP, ACL_MASK]:
|
||||
|
||||
for existing_entry in acl3:
|
||||
existing_tag_type = existing_entry.tag_type
|
||||
|
||||
if tag_type in [ACL_USER, ACL_GROUP, ACL_MASK]:
|
||||
if tag_type == existing_tag_type:
|
||||
if tag_type == ACL_MASK or entry.qualifier == existing_entry.qualifier:
|
||||
acl3.delete_entry(existing_entry)
|
||||
|
||||
acl3.append(entry)
|
||||
|
||||
acl3.calc_mask()
|
||||
|
||||
return acl3
|
||||
|
||||
|
||||
def update_acl_on_path(new_acl, path):
|
||||
"""
|
||||
Example usage: update_acl_on_path(posix1e.ACL, '/etc/foo.txt')
|
||||
Return: None
|
||||
"""
|
||||
acl = merge_acls(read_acl_from_path(path), new_acl)
|
||||
|
||||
return apply_acl_to_path(acl, path)
|
||||
|
||||
|
||||
def read_acl_from_path(path):
|
||||
"""
|
||||
Example usage: read_acl_from_path('/etc/foo.txt')
|
||||
@ -266,3 +306,13 @@ def parse_acl_from_path(path):
|
||||
Return: Complete ACL map
|
||||
"""
|
||||
return parse_acl(read_acl_from_path(path))
|
||||
|
||||
|
||||
def debug_dump_acl_entries(acl):
|
||||
for entry in acl:
|
||||
print(f'tag: {entry.tag_type}', end='')
|
||||
try:
|
||||
print(f' qual: {entry.qualifier}')
|
||||
except TypeError:
|
||||
print()
|
||||
print(f'read: {entry.permset.read}')
|
||||
|
24
tests/matrix-apply-update.yaml
Normal file
24
tests/matrix-apply-update.yaml
Normal file
@ -0,0 +1,24 @@
|
||||
---
|
||||
user:user:rw:
|
||||
args:
|
||||
target_name: user
|
||||
target_type: user
|
||||
read: false
|
||||
write: true
|
||||
expect:
|
||||
user:
|
||||
user:
|
||||
read: false
|
||||
write: true
|
||||
execute: false
|
||||
group: &null_allfalse
|
||||
null:
|
||||
read: true
|
||||
write: false
|
||||
execute: false
|
||||
mask: &null_ro
|
||||
null:
|
||||
read: true
|
||||
write: true
|
||||
execute: false
|
||||
other: *null_allfalse
|
@ -23,6 +23,19 @@ def load_yaml(file):
|
||||
return list(data.items())
|
||||
|
||||
|
||||
def load_yamls(file1, file2):
|
||||
data1 = load_yaml(file1)
|
||||
data2 = load_yaml(file2)
|
||||
|
||||
out = []
|
||||
|
||||
for lentry in data1:
|
||||
for i in range(len(lentry)):
|
||||
ix = i - 1
|
||||
out.append( ({'first': data1[ix], 'second': data2[ix]}) ) # noqa UP034, pytest mangles this in the parameters
|
||||
|
||||
return out
|
||||
|
||||
@mark.parametrize('aclin, aclout', load_yaml('matrix.yaml'))
|
||||
def test_parse_acl_through_string(sample_file_with_acl, aclin, aclout):
|
||||
have = acl.parse_acl_from_path_via_string(sample_file_with_acl)
|
||||
@ -35,10 +48,36 @@ def test_parse_acl_native(sample_file_with_acl, aclin, aclout):
|
||||
assert aclout == have
|
||||
|
||||
|
||||
@mark.parametrize('mode', ['fresh', 'update'])
|
||||
@mark.parametrize('scenario, data', load_yaml('matrix-apply.yaml'))
|
||||
def test_build_and_apply_acl(sample_file, scenario, data):
|
||||
def test_build_and_apply_acl(sample_file, mode, scenario, data):
|
||||
built_acl = acl.build_acl(**data['args'])
|
||||
assert len(list(built_acl)) == 5 # noqa PLR2004, this is the expected size of the built ACL
|
||||
assert acl.apply_acl_to_path(built_acl, sample_file) is None
|
||||
if mode == 'update':
|
||||
assert acl.update_acl_on_path(built_acl, sample_file) is None
|
||||
read_acl = acl.parse_acl_from_path(sample_file)
|
||||
assert read_acl == data['expect']
|
||||
|
||||
|
||||
@mark.parametrize('data', load_yamls('matrix-apply.yaml', 'matrix-apply-update.yaml') )
|
||||
def test_build_and_update_acl(sample_file, data):
|
||||
# we're updating the default instead of overwriting with apply
|
||||
# one better way would be to have both "before" and "after" in matrix-apply-update.yaml instead of re-using the one for apply
|
||||
# another would be to somehow adjust the apply map
|
||||
data['first'][1]['expect']['group'][None]['read'] = True
|
||||
data['first'][1]['expect']['other'][None]['read'] = True
|
||||
|
||||
built_acl1 = acl.build_acl(**data['first'][1]['args'])
|
||||
assert len(list(built_acl1)) == 5 # noqa PLR2004, this is the expected size of the built ACL
|
||||
|
||||
built_acl2 = acl.build_acl(**data['second'][1]['args'])
|
||||
assert len(list(built_acl2)) == 5 # noqa PLR2004
|
||||
|
||||
assert acl.update_acl_on_path(built_acl1, sample_file) is None
|
||||
read_acl1 = acl.parse_acl_from_path(sample_file)
|
||||
assert read_acl1 == data['first'][1]['expect']
|
||||
|
||||
assert acl.update_acl_on_path(built_acl2, sample_file) is None
|
||||
read_acl2 = acl.parse_acl_from_path(sample_file)
|
||||
assert read_acl2 == data['second'][1]['expect']
|
||||
|
Loading…
Reference in New Issue
Block a user