Implement native ACL parsing

Instead of parsing a string representation, parse the native ACL entries
to construct the ACL mapping. This should yield better reliability.
Keep the existing logic in a renamed function, as it might still be useful
to parse existing string representations.

Signed-off-by: Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
This commit is contained in:
Georg Pfuetzenreuter 2024-09-18 20:43:08 +02:00
parent 4a2fb83885
commit 722fec83b3
Signed by: Georg
GPG Key ID: 1ED2F138E7E6FF57
2 changed files with 72 additions and 7 deletions

View File

@ -8,7 +8,7 @@ An English copy of the Licence is shipped in a file called LICENSE along with th
You may obtain copies of the Licence in any of the official languages at https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12. You may obtain copies of the Licence in any of the official languages at https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12.
""" """
from pwd import getpwnam from pwd import getpwnam, getpwuid
from posix1e import ( from posix1e import (
ACL, ACL,
@ -58,7 +58,7 @@ def reduce_entries(acl):
return entries return entries
def parse_permission(strpermission): def parsestrpermission(strpermission):
if len(strpermission) != MAX_PERMBITS: if len(strpermission) != MAX_PERMBITS:
return ValueError('Invalid permission') return ValueError('Invalid permission')
@ -92,7 +92,7 @@ def parse_permission(strpermission):
return outmap return outmap
def parse_entry(strentry): def parsestrentry(strentry):
if not strentry: if not strentry:
raise ValueError('Got empty string') raise ValueError('Got empty string')
@ -111,18 +111,73 @@ def parse_entry(strentry):
return { return {
entrytype: { entrytype: {
entryvalue: parse_permission(permissions), entryvalue: parsestrpermission(permissions),
}, },
} }
def parsefromacl(acl): # noqa PLR0912, FIXME: uncomplexify this
permap = {
permission: False for permission in DEFAULT_PERMISSIONS.keys()
}
outmap = {
group: {
None: permap.copy(),
} for group in DEFAULT_ENTRYTYPES
}
for entry in acl:
name = None
permset = entry.permset
tag_type = entry.tag_type
try:
qualifier = entry.qualifier
except TypeError:
qualifier = None
if tag_type == 0:
return ValueError('Got ACL with undefined tag')
if isinstance(qualifier, int):
try:
name = getpwuid(qualifier).pw_name
except KeyError:
name = qualifier
elif qualifier is not None:
return ValueError('Got ACL with unhandled qualifier')
if tag_type in [ACL_USER, ACL_GROUP, ACL_USER_OBJ, ACL_GROUP_OBJ, ACL_MASK, ACL_OTHER]:
for tag_high, tag_low in LIBACL_TAGS.items():
if tag_low == tag_type:
lowmap = permap.copy()
for permission in lowmap.keys():
lowmap[permission] = getattr(permset, permission)
outtag = tag_high
if tag_type == ACL_USER_OBJ:
outname = None
outtag = 'user'
elif tag_type == ACL_GROUP_OBJ:
outname = None
outtag = 'group'
else:
outname = name
if outtag not in outmap:
outmap[outtag] = {}
if len(outmap[outtag]) == 1 and list(outmap[outtag].keys())[0] is None:
del outmap[outtag][None]
outmap[outtag][outname] = lowmap
break
return outmap
def parse_entries(acl): def parse_entries(acl):
outmap = { outmap = {
group: DEFAULT_PERMISSIONS for group in DEFAULT_ENTRYTYPES group: DEFAULT_PERMISSIONS for group in DEFAULT_ENTRYTYPES
} }
for entry in acl: for entry in acl:
outmap.update(parse_entry(entry)) outmap.update(parsestrentry(entry))
return outmap return outmap
@ -168,5 +223,9 @@ def entriesfromfile(path):
return reduce_entries(aclfromfile(path)) return reduce_entries(aclfromfile(path))
def parsefromfile(path): def parsefromfile_throughstring(path):
return parse_entries(reduce_entries(aclfromfile(path))) return parse_entries(reduce_entries(aclfromfile(path)))
def parsefromfile(path):
return parsefromacl(aclfromfile(path))

View File

@ -24,7 +24,13 @@ def load_yaml(file):
@mark.parametrize('aclin, aclout', load_yaml('matrix.yaml')) @mark.parametrize('aclin, aclout', load_yaml('matrix.yaml'))
def test_parse_acl(sample_file_with_acl, aclin, aclout): def test_parse_acl_through_string(sample_file_with_acl, aclin, aclout):
have = acl.parsefromfile_throughstring(sample_file_with_acl)
assert aclout == have
@mark.parametrize('aclin, aclout', load_yaml('matrix.yaml'))
def test_parse_acl_native(sample_file_with_acl, aclin, aclout):
have = acl.parsefromfile(sample_file_with_acl) have = acl.parsefromfile(sample_file_with_acl)
assert aclout == have assert aclout == have