Merge pull request 'Implement ACL merging/updating' (#4) from feature/update_acl into main
Reviewed-on: #4
This commit is contained in:
commit
0f1a6431e5
50
pyacl/acl.py
50
pyacl/acl.py
@ -243,6 +243,46 @@ def apply_acl_to_path(acl, path):
|
|||||||
acl.applyto(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):
|
def read_acl_from_path(path):
|
||||||
"""
|
"""
|
||||||
Example usage: read_acl_from_path('/etc/foo.txt')
|
Example usage: read_acl_from_path('/etc/foo.txt')
|
||||||
@ -266,3 +306,13 @@ def parse_acl_from_path(path):
|
|||||||
Return: Complete ACL map
|
Return: Complete ACL map
|
||||||
"""
|
"""
|
||||||
return parse_acl(read_acl_from_path(path))
|
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())
|
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'))
|
@mark.parametrize('aclin, aclout', load_yaml('matrix.yaml'))
|
||||||
def test_parse_acl_through_string(sample_file_with_acl, aclin, aclout):
|
def test_parse_acl_through_string(sample_file_with_acl, aclin, aclout):
|
||||||
have = acl.parse_acl_from_path_via_string(sample_file_with_acl)
|
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
|
assert aclout == have
|
||||||
|
|
||||||
|
|
||||||
|
@mark.parametrize('mode', ['fresh', 'update'])
|
||||||
@mark.parametrize('scenario, data', load_yaml('matrix-apply.yaml'))
|
@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'])
|
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 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
|
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)
|
read_acl = acl.parse_acl_from_path(sample_file)
|
||||||
assert read_acl == data['expect']
|
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