diff --git a/src/middlewared/middlewared/plugins/smb_/util_groupmap.py b/src/middlewared/middlewared/plugins/smb_/util_groupmap.py index eb1d94b254849..be68a70e6023d 100644 --- a/src/middlewared/middlewared/plugins/smb_/util_groupmap.py +++ b/src/middlewared/middlewared/plugins/smb_/util_groupmap.py @@ -190,5 +190,5 @@ def list_foreign_group_memberships( with get_tdb_handle(groupmap_file.value, GROUP_MAPPING_TDB_OPTIONS) as hdl: tdb_key = f'{MEMBEROF_PREFIX}{entry_sid}' - tdb_val = hdl.fetch(tdb_key) + tdb_val = hdl.get(tdb_key) return _parse_memberof(tdb_key, tdb_val) diff --git a/src/middlewared/middlewared/pytest/unit/utils/test_groupmap.py b/src/middlewared/middlewared/pytest/unit/utils/test_groupmap.py new file mode 100644 index 0000000000000..4946efe4b959c --- /dev/null +++ b/src/middlewared/middlewared/pytest/unit/utils/test_groupmap.py @@ -0,0 +1,151 @@ +import os +import pytest + +from middlewared.plugins.smb_.util_groupmap import ( + insert_groupmap_entries, + delete_groupmap_entry, + list_foreign_group_memberships, + query_groupmap_entries, + SMBGroupMap, + SMBGroupMembership, + GroupmapEntryType, + GroupmapFile, +) +from middlewared.service_exception import MatchNotFound +from middlewared.utils.sid import ( + lsa_sidtype, + random_sid +) + + +@pytest.fixture(scope='module') +def groupmap_dir(): + os.makedirs('/var/db/system/samba4', exist_ok=True) + + +@pytest.fixture(scope='module') +def local_sid(): + yield random_sid() + + +def test__insert_groupmap(groupmap_dir, local_sid): + entries = [ + SMBGroupMap( + sid=f'{local_sid}-2000010', + gid=3000, + sid_type=lsa_sidtype.ALIAS, + name='bob', + comment='' + ), + SMBGroupMap( + sid=f'{local_sid}-2000011', + gid=3001, + sid_type=lsa_sidtype.ALIAS, + name='larry', + comment='' + ) + ] + + insert_groupmap_entries(GroupmapFile.DEFAULT, entries) + + bob = query_groupmap_entries(GroupmapFile.DEFAULT, [ + ['entry_type', '=', GroupmapEntryType.GROUP_MAPPING.name], + ['name', '=', 'bob'] + ], {'get': True}) + assert bob['sid'] == f'{local_sid}-2000010' + assert bob['gid'] == 3000 + assert bob['sid_type'] == lsa_sidtype.ALIAS + assert bob['name'] == 'bob' + assert bob['comment'] == '' + + larry = query_groupmap_entries(GroupmapFile.DEFAULT, [ + ['entry_type', '=', GroupmapEntryType.GROUP_MAPPING.name], + ['name', '=', 'larry'] + ], {'get': True}) + assert larry['sid'] == f'{local_sid}-2000011' + assert larry['gid'] == 3001 + assert larry['sid_type'] == lsa_sidtype.ALIAS + assert larry['name'] == 'larry' + assert larry['comment'] == '' + + delete_groupmap_entry( + GroupmapFile.DEFAULT, + GroupmapEntryType.GROUP_MAPPING, + f'{local_sid}-2000010' + ) + + delete_groupmap_entry( + GroupmapFile.DEFAULT, + GroupmapEntryType.GROUP_MAPPING, + f'{local_sid}-2000011' + ) + + with pytest.raises(MatchNotFound): + query_groupmap_entries(GroupmapFile.DEFAULT, [ + ['entry_type', '=', GroupmapEntryType.GROUP_MAPPING.name], + ['name', '=', 'larry'] + ], {'get': True}) + + groupmaps = query_groupmap_entries(GroupmapFile.DEFAULT, [ + ['entry_type', '=', GroupmapEntryType.GROUP_MAPPING.name], + ], {}) + + assert len(groupmaps) == 0 + + +def test__insert_group_membership(groupmap_dir, local_sid): + entries = [ + SMBGroupMembership( + sid='S-1-5-32-544', + members=(f'{local_sid}-2000010', f'{local_sid}-2000011') + ), + SMBGroupMembership( + sid='S-1-5-32-545', + members=(f'{local_sid}-2000012', f'{local_sid}-2000013') + ), + ] + insert_groupmap_entries(GroupmapFile.DEFAULT, entries) + + res = query_groupmap_entries(GroupmapFile.DEFAULT, [ + ['entry_type', '=', GroupmapEntryType.MEMBERSHIP.name], + ['sid', '=', 'S-1-5-32-544'] + ], {'get': True}) + + assert res['sid'] == 'S-1-5-32-544' + assert set(res['members']) == set((f'{local_sid}-2000010', f'{local_sid}-2000011')) + + res = list_foreign_group_memberships(GroupmapFile.DEFAULT, 'S-1-5-32-544') + assert res.sid == 'S-1-5-32-544' + assert set(res.members) == set((f'{local_sid}-2000010', f'{local_sid}-2000011')) + + res = query_groupmap_entries(GroupmapFile.DEFAULT, [ + ['entry_type', '=', GroupmapEntryType.MEMBERSHIP.name], + ['sid', '=', 'S-1-5-32-545'] + ], {'get': True}) + + assert res['sid'] == 'S-1-5-32-545' + assert set(res['members']) == set((f'{local_sid}-2000012', f'{local_sid}-2000013')) + + delete_groupmap_entry( + GroupmapFile.DEFAULT, + GroupmapEntryType.MEMBERSHIP, + 'S-1-5-32-544' + ) + + delete_groupmap_entry( + GroupmapFile.DEFAULT, + GroupmapEntryType.MEMBERSHIP, + 'S-1-5-32-545' + ) + + with pytest.raises(MatchNotFound): + query_groupmap_entries(GroupmapFile.DEFAULT, [ + ['entry_type', '=', GroupmapEntryType.MEMBERSHIP.name], + ['sid', '=', 'S-1-5-32-544'] + ], {'get': True}) + + entries = query_groupmap_entries(GroupmapFile.DEFAULT, [ + ['entry_type', '=', GroupmapEntryType.MEMBERSHIP.name], + ], {}) + + assert len(entries) == 0 diff --git a/src/middlewared/middlewared/pytest/unit/utils/test_sid.py b/src/middlewared/middlewared/pytest/unit/utils/test_sid.py new file mode 100644 index 0000000000000..79ae755a3bb9f --- /dev/null +++ b/src/middlewared/middlewared/pytest/unit/utils/test_sid.py @@ -0,0 +1,62 @@ +import pytest + +from middlewared.plugins.idmap_.idmap_constants import ( + BASE_SYNTHETIC_DATASTORE_ID, + IDType +) +from middlewared.utils.sid import ( + db_id_to_rid, + get_domain_rid, + random_sid, + sid_is_valid, + BASE_RID_GROUP, + BASE_RID_USER, +) + + +@pytest.fixture(scope='module') +def local_sid(): + yield random_sid() + + +@pytest.mark.parametrize('id_type,db_id,expected_rid,valid', [ + (IDType.USER, 1000, 1000 + BASE_RID_USER, True), + (IDType.GROUP, 1000, 1000 + BASE_RID_GROUP, True), + (IDType.USER, 1000 + BASE_SYNTHETIC_DATASTORE_ID, None, False), +]) +def test__db_id_to_rid(id_type, db_id, expected_rid, valid): + if valid: + assert db_id_to_rid(id_type, db_id) == expected_rid + else: + with pytest.raises(ValueError): + db_id_to_rid(id_type, db_id) + + +@pytest.mark.parametrize('sid,valid', [ + ('S-1-5-21-3510196835-1033636670-2319939847-200108', True), + ('S-1-5-32-544', True), + ('S-1-2-0', False), # technically valid SID but we don't permit it + ('S-1-5-21-3510196835-1033636670-2319939847-200108-200108', False), + ('S-1-5-21-3510196835-200108', False), + ('S-1-5-21-3510196835-1033636670-231993009847-200108', False), + ('S-1-5-21-351019683b-1033636670-231993009847-200108', False), +]) +def test__sid_is_valid(sid, valid): + assert sid_is_valid(sid) is valid + + +@pytest.mark.parametrize('sid,rid,valid', [ + ('S-1-5-21-3510196835-1033636670-2319939847-200108', 200108, True), + ('S-1-5-21-3510196835-1033636670-2319939847', None, False), + ('S-1-5-32-544', None, False), +]) +def test__get_domain_rid(sid, rid, valid): + if valid: + assert get_domain_rid(sid) == rid + else: + with pytest.raises(ValueError): + get_domain_rid(sid) + + +def test__random_sid_is_valid(local_sid): + assert sid_is_valid(local_sid) diff --git a/src/middlewared/middlewared/utils/sid.py b/src/middlewared/middlewared/utils/sid.py index 9ddd3026125ec..5a112429ddb01 100644 --- a/src/middlewared/middlewared/utils/sid.py +++ b/src/middlewared/middlewared/utils/sid.py @@ -99,7 +99,7 @@ def sid_is_valid(sid: str) -> bool: return False for subauth in subauths: - if not subauths.isdigit(): + if not subauth.isdigit(): return False subauth_val = int(subauth) @@ -111,6 +111,9 @@ def sid_is_valid(sid: str) -> bool: def get_domain_rid(sid: str) -> str: """ get rid component of the specified SID """ + if not sid_is_valid(sid): + raise ValueError(f'{sid}: not a valid SID') + if not sid.startswith(DOM_SID_PREFIX): raise ValueError(f'{sid}: not a domain SID') @@ -118,7 +121,7 @@ def get_domain_rid(sid: str) -> str: if len(subauths) == DOM_SID_SUBAUTHS: raise ValueError(f'{sid}: does not contain a RID component') - return subauths[-1] + return int(subauths[-1]) def db_id_to_rid(id_type: IDType, db_id: int) -> int: