def test_postprocess_grants(): from ldap2pg.manager import SyncManager from ldap2pg.privilege import DefAcl, Grant, Acl manager = SyncManager( privileges=dict(ro=DefAcl(name='ro')), privilege_aliases=dict(ro=['ro']), ) # No owners acl = manager.postprocess_acl(Acl(), schemas=dict()) assert 0 == len(acl) acl = Acl([Grant(privilege='ro', dbname=['db'], schema=None)]) acl = manager.postprocess_acl( acl, schemas=dict(db=dict( public=['postgres', 'owner'], ns=['owner'], )), ) # One grant per schema, per owner assert 3 == len(acl)
def test_inspect_ldap_unexpected_dn(mocker): ql = mocker.patch('ldap2pg.manager.SyncManager.query_ldap') from ldap2pg.manager import SyncManager, UserError, LDAPEntry from ldap2pg.role import RoleRule manager = SyncManager() ql.return_value = [ LDAPEntry('dn0', { 'member': ['cn=member0', 'baddn=o0', 'cn=member1'], }) ] list( manager.inspect_ldap([ dict( description="Test query desc", ldap=dict(on_unexpected_dn='warn'), roles=[RoleRule(names=['{member.cn}'])], ) ])) list( manager.inspect_ldap([ dict(ldap=dict(on_unexpected_dn='ignore'), roles=[RoleRule(names=['{member.cn}'])]) ])) with pytest.raises(UserError): list( manager.inspect_ldap( [dict( ldap=dict(), roles=[RoleRule(names=['{member.cn}'])], )]))
def test_inspect_ldap_grants(mocker): from ldap2pg.manager import SyncManager from ldap2pg.privilege import Grant, NspAcl from ldap2pg.utils import make_group_map privileges = dict(ro=NspAcl(name='ro')) manager = SyncManager( psql=mocker.Mock(), ldapconn=mocker.Mock(), privileges=privileges, privilege_aliases=make_group_map(privileges), inspector=mocker.Mock(name='inspector'), ) manager.inspector.roles_blacklist = ['blacklisted'] rule = mocker.Mock(name='grant', attributes_map={}) rule.generate.return_value = [ Grant('ro', 'postgres', None, 'alice'), Grant('ro', 'postgres', None, 'blacklisted'), ] syncmap = [dict(roles=[], grant=[rule])] _, grants = manager.inspect_ldap(syncmap=syncmap) assert 1 == len(grants)
def test_query_ldap(mocker): from ldap2pg.manager import SyncManager, UserError manager = SyncManager(ldapconn=mocker.Mock()) manager.ldapconn.search_s.return_value = [ ('dn=a', {}), ('dn=b', { 'member': ['m'] }), (None, { 'ref': True }), (None, ['ldap://list_ref']), ] entries = manager.query_ldap( base='ou=people,dc=global', filter='(objectClass=*)', scope=2, joins={}, attributes=['cn'], allow_missing_attributes=['member'], ) assert 2 == len(entries) assert [] == entries[0][1]['member'] manager.ldapconn.search_s.return_value = [('dn=a', {'a': b'\xbb'})] with pytest.raises(UserError): manager.query_ldap( base='ou=people,dc=global', filter='(objectClass=*)', scope=2, joins={}, attributes=['cn'], )
def test_sync_sql_error(mocker): diff = mocker.patch('ldap2pg.manager.SyncManager.diff') from ldap2pg.manager import SyncManager psql = mocker.MagicMock() cursor = psql.return_value.__enter__.return_value cursor.side_effect = Exception() manager = SyncManager(psql=psql) # Simple diff with one query diff.return_value = qry = [mocker.Mock(name='qry', args=())] qry[0].expand.return_value = [qry[0]] sync_kw = dict( databases=['postgres', 'template1'], pgroles=set(), pgacls=set(), ldaproles=set(), ldapacls=set(), ) manager.dry = False with pytest.raises(Exception): manager.sync(**sync_kw) assert cursor.called is True
def test_apply_grant_rule_ok(mocker): gla = mocker.patch('ldap2pg.manager.get_ldap_attribute', autospec=True) from ldap2pg.manager import SyncManager manager = SyncManager() gla.side_effect = [['alice'], ['bob']] items = manager.apply_grant_rules( grant=[dict( acl='connect', database='postgres', schema='__any__', role_attribute='cn', )], entries=[None, None], ) items = list(items) assert 2 == len(items) assert 'alice' == items[0].role assert 'postgres' == items[0].dbname # Ensure __any__ schema is mapped to None assert items[0].schema is None assert 'bob' == items[1].role
def test_apply_grant_rule_filter(mocker): from ldap2pg.manager import SyncManager items = SyncManager().apply_grant_rules( grant=[dict( privilege='connect', database='postgres', schema='__any__', role_match='*_r', roles=['alice_r', 'bob_rw'], )], entries=[None], ) items = list(items) assert 1 == len(items) assert 'alice_r' == items[0].role
def test_fetch_roles(mocker): from ldap2pg.manager import SyncManager manager = SyncManager() psql = mocker.Mock(name='psql') psql.return_value = mocker.MagicMock() psql.return_value.__iter__.return_value = r = [mocker.Mock()] rows = manager.fetch_pg_roles(psql) rows = list(rows) assert r == rows manager = SyncManager(roles_query=None) psql = mocker.Mock(name='psql') psql.return_value = cur = mocker.MagicMock() rows = manager.fetch_pg_roles(psql) rows = list(rows) assert cur.called is False assert [] == rows
def test_sync(mocker): from ldap2pg.manager import RoleOptions mod = 'ldap2pg.manager' mocker.patch( mod + '.RoleOptions.SUPPORTED_COLUMNS', RoleOptions.SUPPORTED_COLUMNS[:], ) cls = mod + '.SyncManager' il = mocker.patch(cls + '.inspect_ldap', autospec=True) mocker.patch(cls + '.postprocess_acl', autospec=True) from ldap2pg.manager import SyncManager, UserError psql = mocker.Mock(name='psql') inspector = mocker.Mock(name='inspector') manager = SyncManager(psql=psql, inspector=inspector) inspector.fetch_me.return_value = ('postgres', False) inspector.roles_blacklist = ['pg_*'] inspector.fetch_roles.return_value = (['postgres'], set(), set()) pgroles = mocker.Mock(name='pgroles') # Simple diff with one query pgroles.diff.return_value = qry = [ mocker.Mock(name='qry', args=(), message='hop')] inspector.filter_roles.return_value = set(), pgroles il.return_value = (mocker.Mock(name='ldaproles'), set()) qry[0].expand.return_value = [qry[0]] inspector.fetch_schemas.return_value = dict(postgres=dict(ns=['owner'])) inspector.fetch_grants.return_value = pgacl = mocker.Mock(name='pgacl') pgacl.diff.return_value = [] # No privileges to sync, one query psql.dry = False psql.run_queries.return_value = 1 count = manager.sync(syncmap=[]) assert pgroles.diff.called is True assert pgacl.diff.called is False assert 1 == count # With privileges manager.privileges = dict(ro=mocker.Mock(name='ro')) count = manager.sync(syncmap=[]) assert pgroles.diff.called is True assert pgacl.diff.called is True assert 2 == count # Dry run with roles and ACL manager.psql.dry = True manager.sync(syncmap=[]) # Nothing to do psql.run_queries.return_value = 0 count = manager.sync(syncmap=[]) assert 0 == count # resolve_membership failure il.return_value[0].resolve_membership.side_effect = ValueError() with pytest.raises(UserError): manager.sync(syncmap=[])
def test_query_ldap_joins(mocker): from ldap2pg.manager import SyncManager, LDAPError search_result = [ ('cn=A,ou=people,dc=global', { 'cn': ['A'], 'member': ['cn=P,ou=people,dc=global']}), ('cn=B,ou=people,dc=global', { 'cn': ['B'], 'member': ['cn=P,ou=people,dc=global']}), ] sub_search_result = [ ('cn=P,ou=people,dc=global', {'sAMAccountName': ['P']}), ] manager = SyncManager(ldapconn=mocker.Mock()) manager.ldapconn.search_s.side_effect = [ search_result, sub_search_result] entries = manager.query_ldap( base='ou=people,dc=global', filter='(objectClass=group)', scope=2, joins={'member': dict( base='ou=people,dc=global', scope=2, filter='(objectClass=people)', attributes=['sAMAccountName'], )}, attributes=['cn', 'member'], ) expected_entries = [ ('cn=A,ou=people,dc=global', { 'cn': [('A', {})], 'dn': [('cn=A,ou=people,dc=global', {})], 'member': [('cn=P,ou=people,dc=global', { 'dn': ['cn=P,ou=people,dc=global'], 'samaccountname': ['P'], })], }), ('cn=B,ou=people,dc=global', { 'cn': [('B', {})], 'dn': [('cn=B,ou=people,dc=global', {})], 'member': [('cn=P,ou=people,dc=global', { 'dn': ['cn=P,ou=people,dc=global'], 'samaccountname': ['P'], })], }), ] assert expected_entries == entries manager.ldapconn.search_s.side_effect = [search_result] entries = manager.query_ldap( base='ou=people,dc=global', filter='(objectClass=group)', scope=2, joins={'member': dict( base='ou=people,dc=global', scope=2, filter='(objectClass=people)', attributes=[], )}, attributes=['cn', 'member'], ) expected_entries = [ ('cn=A,ou=people,dc=global', { 'cn': [('A', {})], 'dn': [('cn=A,ou=people,dc=global', {})], 'member': [('cn=P,ou=people,dc=global', { 'dn': ['cn=P,ou=people,dc=global'], })], }), ('cn=B,ou=people,dc=global', { 'cn': [('B', {})], 'dn': [('cn=B,ou=people,dc=global', {})], 'member': [('cn=P,ou=people,dc=global', { 'dn': ['cn=P,ou=people,dc=global'], })], }), ] assert expected_entries == entries search_result = [ ('cn=A,ou=people,dc=global', { 'cn': ['A'], 'member': ['cn=P,ou=people,dc=global']}), ] sub_search_result = LDAPError() manager = SyncManager(ldapconn=mocker.Mock()) manager.ldapconn.search_s.side_effect = [ search_result, sub_search_result] entries = manager.query_ldap( base='ou=people,dc=global', filter='(objectClass=group)', scope=2, joins={'member': dict( base='ou=people,dc=global', scope=2, filter='(objectClass=people)', attributes=['sAMAccountName'], )}, attributes=['cn', 'member'], ) expected_entries = [ ('cn=A,ou=people,dc=global', { 'cn': [('A', {})], 'dn': [('cn=A,ou=people,dc=global', {})], 'member': [], }), ] assert expected_entries == entries
def test_entry_build_vars(): from ldap2pg.ldap import LDAPEntry from ldap2pg.manager import SyncManager entry = LDAPEntry( dn='cn=my0,uo=gr0,dc=acme,dc=tld', attributes=dict( cn=['my0'], member=[ 'cn=m0,uo=gr0', 'cn=m1,uo=gr0', ], simple=[ 'cn=s0,uo=gr1', 'cn=s1,uo=gr1', 'cn=s2,uo=gr1', ], ), children=dict(member=[ LDAPEntry( 'cn=m0,uo=gr0', dict(mail=['*****@*****.**', '*****@*****.**']), ), LDAPEntry( 'cn=m1,uo=gr0', dict(mail=['*****@*****.**']), ), ]), ) map_ = dict( __self__=['dn', 'cn', 'dn.cn'], member=['cn', 'dn', 'mail', 'dn.cn'], simple=['cn'], ) manager = SyncManager() vars_ = manager.build_format_vars(entry, map_, on_unexpected_dn='fail') wanted = dict( __self__=[ dict( dn=[dict( dn="cn=my0,uo=gr0,dc=acme,dc=tld", cn="my0", )], cn=["my0"], ) ], member=[ dict( dn=[dict(dn="cn=m0,uo=gr0", cn="m0")], cn=["m0"], mail=["*****@*****.**", "*****@*****.**"], ), dict( dn=[dict(dn="cn=m1,uo=gr0", cn="m1")], cn=["m1"], mail=["*****@*****.**"], ), ], simple=[ dict( dn=["cn=s0,uo=gr1"], cn=["s0"], ), dict( dn=["cn=s1,uo=gr1"], cn=["s1"], ), dict( dn=["cn=s2,uo=gr1"], cn=["s2"], ), ], ) assert wanted == vars_
def test_sync(mocker): diff = mocker.patch('ldap2pg.manager.SyncManager.diff') from ldap2pg.manager import SyncManager psql = mocker.MagicMock() cursor = psql.return_value.__enter__.return_value manager = SyncManager(psql=psql) # Simple diff with one query diff.return_value = qry = [mocker.Mock(name='qry', args=(), message='hop')] qry[0].expand.return_value = [qry[0]] sync_kw = dict( databases=['postgres', 'template1'], pgroles=set(), pgacls=set(), ldaproles=set(), ldapacls=set(), ) # Dry run manager.dry = True # No mapping, we're just testing query loop manager.sync(**sync_kw) assert cursor.called is False # Real mode manager.dry = False manager.sync(**sync_kw) assert cursor.called is True # Nothing to do diff.return_value = [] manager.dry = False manager.sync(**sync_kw) assert cursor.called is True