def remove(config, username, filename, force): """Remove user's SSH public key from their LDAP entry.""" client = Client() client.prepare_connection() user_api = UserApi(client) key_api = API(client) key_api.remove(username, user_api, filename, force)
def show(config, username): # pragma: no cover """Show a user's SSH public key from their LDAP entry.""" client = Client() client.prepare_connection() key_api = API(client) for key, value in key_api.get_keys_from_ldap(username).items(): print(value)
def create(config, name, group, type): """Create an LDAP user.""" if type not in ('user', 'service'): raise click.BadOptionUsage("--type must be 'user' or 'service'") client = Client() client.prepare_connection() user_api = API(client) group_api = GroupApi(client) user_api.create(name[0], name[1], group, type, group_api)
def list(config): # pragma: no cover """List SSH public key(s) from LDAP.""" client = Client() client.prepare_connection() key_api = API(client) for key, values in key_api.get_keys_from_ldap().items(): print("{}: ".format(key)) for value in [v.decode() for v in values]: print("\t - {}".format(value))
def create(config, group, type): """Create an LDAP group.""" if type not in ('user', 'service'): raise click.BadOptionUsage( # pragma: no cover "--grouptype must be 'user' or 'service'") client = Client() client.prepare_connection() group_api = API(client) group_api.create(group, type)
def delete(config, group, force): """Delete an LDAP group.""" if not force: if not click.confirm( 'Confirm that you want to delete group {}'.format(group)): sys.exit("Deletion of {} aborted".format(group)) client = Client() client.prepare_connection() group_api = API(client) group_api.delete(group)
def remove_user(config, group, username): """Remove specified user from specified group.""" client = Client() client.prepare_connection() group_api = API(client) try: group_api.remove_user(group, username) except ldap_tools.exceptions.NoGroupsFound: # pragma: no cover print("Group ({}) not found".format(group)) except ldap_tools.exceptions.TooManyResults: # pragma: no cover print("Query for group ({}) returned multiple results.".format( group)) except ldap3.NO_SUCH_ATTRIBUTE: # pragma: no cover print("{} does not exist in {}".format(username, group))
def add_user(config, group, username): """Add specified user to specified group.""" client = Client() client.prepare_connection() group_api = API(client) try: group_api.add_user(group, username) except ldap_tools.exceptions.NoGroupsFound: # pragma: no cover print("Group ({}) not found".format(group)) except ldap_tools.exceptions.TooManyResults: # pragma: no cover print("Query for group ({}) returned multiple results.".format( group)) except ldap3.TYPE_OR_VALUE_EXISTS: # pragma: no cover print("{} already exists in {}".format(username, group))
def add(config, username, filename): """Add user's SSH public key to their LDAP entry.""" try: client = Client() client.prepare_connection() user_api = UserApi(client) key_api = API(client) key_api.add(username, user_api, filename) except (ldap3.core.exceptions.LDAPNoSuchAttributeResult, ldap_tools.exceptions.InvalidResult, ldap3.core.exceptions.LDAPAttributeOrValueExistsResult ) as err: # pragma: no cover print('{}: {}'.format(type(err), err.args[0])) except Exception as err: # pragma: no cover raise err from None
def install(config): # pragma: no cover """Install user's SSH public key to the local system.""" client = Client() client.prepare_connection() key_api = API(client) key_api.install()
def index(config): """Display user info in LDIF format.""" client = Client() client.prepare_connection() user_api = API(client) CLI.show_user(user_api.index())
def describe_client(): client = Client() client.conn = MagicMock() client.basedn = 'dc=test,dc=org' distinguished_name = 'cn=test.user,ou=People,dc=test,dc=org' object_class = ['inetOrgPerson'] attributes = {'givenName': 'Test', 'sn': 'User'} def it_adds_object_to_ldap(): client.conn.reset_mock() client.add(distinguished_name, object_class, attributes) client.conn.add.assert_called_once_with(distinguished_name, object_class, attributes) def describe_client_search(): client.conn.search = MagicMock() def it_searches_with_default_filter_and_attributes(): pytest.xfail(reason='ideopathic unreliable results') search_filter = ["(objectclass=*)"] filterstr = "(&{})".format(''.join(search_filter)) attributes = ['*'] client.conn.reset_mock() client.conn.search.reset_mock() client.search(None) client.conn.search.assert_any_call(search_base=client.basedn, search_filter=filterstr, search_scope=ldap3.SUBTREE, attributes=attributes) def it_searches_with_default_attributes_and_custom_filter(): pytest.xfail(reason='ideopathic unreliable results') search_filter = ["(objectclass=top)"] filterstr = "(&{})".format(''.join(search_filter)) attributes = ['*'] client.conn.reset_mock() client.conn.search.reset_mock() client.search(search_filter) client.conn.search.assert_any_call(search_base=client.basedn, search_filter=filterstr, search_scope=ldap3.SUBTREE, attributes=attributes) def it_searches_with_default_filter_and_custom_attributes(): pytest.xfail(reason='ideopathic unreliable results') search_filter = ["(objectclass=*)"] filterstr = "(&{})".format(''.join(search_filter)) attributes = ['uid', 'gid'] client.conn.reset_mock() client.conn.search.reset_mock() client.search(None, attributes) client.conn.search.assert_any_call(search_base=client.basedn, search_filter=filterstr, search_scope=ldap3.SUBTREE, attributes=attributes) def describe_get_max_id(): def it_gets_id_of_service_user(): client.search = MagicMock(return_value=[]) assert client.get_max_id('user', 'service') == 20000 def it_gets_id_of_normal_user(): client.search = MagicMock(return_value=[]) assert client.get_max_id('user', 'user') == 10000 def it_raises_error_on_bad_user_type(): client.search = MagicMock(return_value=[]) with pytest.raises(ldap_tools.exceptions.InvalidResult, message=("Unknown object type")): client.get_max_id('user', 'foo')
def describe_api_calls(): client = Client() client.prepare_connection = MagicMock() def describe_key_addition(): client.modify = MagicMock() user_api = UserApi(client) user_api.find = MagicMock(return_value=ldap_entry) key_api = KeyApi(client) def it_calls_the_client(): pytest.xfail(reason='Need to learn how to mock user.entry_dn') client.modify.reset_mock() filename = path.join(fixture_path, 'single_key_user') key_api.add(username, user_api, filename) client.modify.assert_called_once_with(distinguished_name, mock.ANY) def it_raises_error_on_bad_key(): pytest.xfail(reason='Need to learn how to mock user.entry_dn') filename = path.join(fixture_path, 'invalid_user_key') with pytest.raises(sshpubkeys.exceptions.MalformedDataError): key_api.add(username, user_api, filename) def it_raises_error_on_bad_object(): pytest.xfail(reason='Need to learn how to mock user.entry_dn') filename = path.join(fixture_path, 'single_key_user') ldap_entry = [[ distinguished_name, { 'objectClass': [ b'top', b'posixAccount', b'shadowAccount', b'inetOrgPerson', b'organizationalPerson', b'person' ] } ]] user_api.find = MagicMock(return_value=ldap_entry) with pytest.raises( ldap3.core.exceptions.LDAPNoSuchAttributeResult): key_api.add(username, user_api, filename) def it_removes_key_from_user(mocker): # noqa: F811 pytest.xfail(reason='Need to learn how to mock user.entry_dn') client.modify = MagicMock() client.search = MagicMock() user_api = UserApi(client) user_api.find = MagicMock(return_value=ldap_entry) key_api = KeyApi(client) with open(path.join(fixture_path, 'two_key_user'), 'r') as FILE: # get current keys mocker.patch.object(key_api, '_API__current_keys', return_value=FILE.read().splitlines()) filename = path.join(fixture_path, 'delete_user_key') key_api.remove(username, user_api, filename, True) client.modify.assert_called_once_with(distinguished_name, mock.ANY) def describe_get_keys_from_ldap(): client.search = MagicMock() key_api = KeyApi(client) def it_filters_with_username(): client.search.reset_mock() filter = ['(sshPublicKey=*)', '(uid={})'.format(username)] key_api.get_keys_from_ldap(username) client.search.assert_any_call(filter, ['uid', 'sshPublicKey']) def it_filters_without_username(): client.search.reset_mock() filter = ['(sshPublicKey=*)'] key_api.get_keys_from_ldap() client.search.assert_called_once_with(filter, ['uid', 'sshPublicKey'])
def raw(config): # pragma: no cover """Dump the contents of LDAP to console in raw format.""" client = Client() client.prepare_connection() audit_api = API(client) print(audit_api.raw())
def describe_group(): # TODO: change this to use test_key.py format group_name = 'testGroup' group_type = 'user' username = '******' ldap_attributes = {'cn': b'testGroup', 'gidnumber': 99999} group_objectclass = ["(objectclass=posixGroup)"] runner = CliRunner() client = Client() client.prepare_connection = MagicMock() client.load_ldap_config = MagicMock() client.prepare_connection() client.basedn = 'dc=test,dc=org' client.server = ldap3.Server('my_fake_server') client.conn = ldap3.Connection(client.server, user='******'.format( client.basedn), password='******', client_strategy=ldap3.MOCK_SYNC) def describe_creates_group(): def describe_commandline(): def it_calls_the_api(mocker): # noqa: F811 mocker.patch('ldap_tools.group.API.create', return_value=None) mocker.patch('ldap_tools.client.Client.prepare_connection', return_value=None) runner.invoke( GroupCli.group, ['create', '--group', group_name, '--type', group_type]) ldap_tools.group.API.create.assert_called_once_with( group_name, group_type) def it_raises_exception_on_bad_type(mocker): # noqa: F811 mocker.patch('ldap_tools.group.API.create', return_value=None) mocker.patch('ldap_tools.client.Client.prepare_connection', return_value=None) runner.invoke( GroupCli.group, ['create', '--group', group_name, '--type', group_type]) assert pytest.raises(click.BadOptionUsage) def describe_api(): def it_calls_the_client(): client.add = MagicMock() client.get_max_id = MagicMock(return_value=99999) group_objectclass = ['top', 'posixGroup'] group_api = GroupApi(client) # mocker.patch.object( # group_api, '_API__ldap_attr', return_value=ldap_attributes) group_api.create(group_name, group_type) client.add.assert_called_once_with( 'cn={},ou=Group,{}'.format(group_name, client.basedn), group_objectclass, ldap_attributes) def describe_deletes_group(): def describe_commandline(): group_api = GroupApi(client) group_api.delete = MagicMock() def it_calls_the_api(mocker): # noqa: F811 mocker.patch('ldap_tools.group.API.delete', return_value=None) mocker.patch('ldap_tools.client.Client.prepare_connection', return_value=None) # We have to force here, otherwise, we get stuck # on an interactive prompt runner.invoke(GroupCli.group, ['delete', '--group', group_name, '--force']) ldap_tools.group.API.delete.assert_called_once_with(group_name) def it_exits_on_no_force_no_confirm(mocker): # noqa: F811 mocker.patch('ldap_tools.group.API.delete', return_value=None) mocker.patch('ldap_tools.client.Client.prepare_connection', return_value=None) mocker.patch('click.confirm', return_value=False) # We have to force here, otherwise, we get stuck # on an interactive prompt result = runner.invoke(GroupCli.group, ['delete', '--group', group_name]) assert result.output == "Deletion of {} aborted\n".format( group_name) def describe_api(): def it_calls_the_client(): client.delete = MagicMock() group_api = GroupApi(client) group_api.delete(group_name) client.delete.assert_called_once_with( 'cn={},ou=Group,{}'.format(group_name, client.basedn)) def describe_adds_user(): def describe_commandline(): def it_calls_the_api(mocker): # noqa: F811 mocker.patch('ldap_tools.group.API.add_user', return_value=None) mocker.patch('ldap_tools.client.Client.prepare_connection', return_value=None) group_api = GroupApi(client) group_api.add_user = MagicMock() runner.invoke(GroupCli.group, [ 'add_user', '--group', group_name, '--username', username ]) ldap_tools.group.API.add_user.assert_called_once_with( group_name, username) def describe_api(): def it_calls_the_client(mocker): # noqa: F811 client.modify = MagicMock() group_api = GroupApi(client) group_api.lookup_id = MagicMock(return_value=99999) group_api.add_user(group_name, username) client.modify.assert_called_once_with( 'cn={},ou=Group,{}'.format(group_name, client.basedn), {'memberUid': [(ldap3.MODIFY_ADD, [username])]}) def describe_removes_user(): def describe_commandline(): def it_calls_the_api(mocker): # noqa: F811 mocker.patch('ldap_tools.group.API.remove_user', return_value=None) mocker.patch('ldap_tools.client.Client.prepare_connection', return_value=None) group_api = GroupApi(client) group_api.remove_user = MagicMock() runner.invoke(GroupCli.group, [ 'remove_user', '--group', group_name, '--username', username ]) ldap_tools.group.API.remove_user.assert_called_once_with( group_name, username) def describe_api(): def it_calls_the_client(): client.modify = MagicMock() group_api = GroupApi(client) group_api.lookup_id = MagicMock(return_value=99999) group_api.remove_user(group_name, username) client.modify.assert_called_once_with( 'cn={},ou=Group,{}'.format(group_name, client.basedn), {'memberUid': [(ldap3.MODIFY_DELETE, [username])]}) def describe_indexes_groups(): def describe_commandline(): def it_calls_the_api(mocker): # noqa: F811 mocker.patch('ldap_tools.group.API.index', return_value=None) mocker.patch('ldap_tools.client.Client.prepare_connection', return_value=None) group_api = GroupApi(client) group_api.index = MagicMock() runner.invoke(GroupCli.group, ['index']) ldap_tools.group.API.index.assert_called_once_with() def describe_api(): def it_calls_the_client(): client.search = MagicMock() # client.prepare_connection = MagicMock() # client.load_ldap_config = MagicMock() group_api = GroupApi(client) group_api.index() client.search.assert_called_once_with(group_objectclass) def describe_lookup_id(): def describe_commandline(): pass # not implemented def describe_api(): def it_searches_with_correct_filter(): pytest.xfail( reason='Need to learn how to mock list.gidNumber.value') client.search = MagicMock(return_value=['foo']) group_api = GroupApi(client) group_api.lookup_id(group_name) search_filter = [ "(cn={})".format(group_name), "(objectclass=posixGroup)" ] client.search.assert_called_once_with(search_filter, ['gidNumber']) def it_raises_exception_on_no_groups_found(): client.search = MagicMock() group_api = GroupApi(client) with pytest.raises(ldap_tools.exceptions.NoGroupsFound, message=("No Groups Returned by LDAP.")): group_api.lookup_id(group_name) def it_raises_exception_on_multiple_results(): client.search = MagicMock(return_value=['foo', 'bar']) group_api = GroupApi(client) with pytest.raises( ldap_tools.exceptions.TooManyResults, message=( "Multiple groups found. Please narrow your search." )): group_api.lookup_id(group_name)
def by_user(config): """Display LDAP group membership sorted by user.""" client = Client() client.prepare_connection() audit_api = API(client) CLI.parse_membership('Groups by User', audit_api.by_user())
def delete(config, username, type): """Delete an LDAP user.""" client = Client() client.prepare_connection() user_api = API(client) user_api.delete(username, type)
def describe_user(): # TODO: change this to use test_key.py format first_name = 'Test' last_name = 'User' username = '******' user_type = 'user' group_name = 'testGroup' ldap_attributes = {'attribute': 'value'} runner = CliRunner() # initialize commandline runner client = Client() group_api = GroupApi(client) def describe_creates_user(): def describe_commandline(): def it_calls_the_api(mocker): # noqa: F811 mocker.patch('ldap_tools.user.API.create', return_value=None) mocker.patch( 'ldap_tools.client.Client.prepare_connection', return_value=None) runner.invoke(UserCli.user, [ 'create', '--name', first_name, last_name, '--type', user_type, '--group', group_name ]) ldap_tools.user.API.create.assert_called_once_with( first_name, last_name, group_name, user_type, mock.ANY) def it_raises_exception_on_bad_type(mocker): # noqa: F811 mocker.patch('ldap_tools.user.API.create', return_value=None) runner.invoke(ldap_tools.user.CLI.user, [ 'create', '--name', first_name, last_name, '--type', 'badType', '--group', group_name ]) assert pytest.raises(click.BadOptionUsage) def describe_api(): def it_assembles_the_current_username(mocker): # noqa: F811 client.add = MagicMock() user_api = UserApi(client) # mock private method __ldap_attr, so we don't have to manage # a giant dict that requires mocking many private methods client.basedn = 'dc=test,dc=org' mocker.patch.object( user_api, '_API__ldap_attr', return_value=ldap_attributes) user_api.create(first_name, last_name, group_name, user_type, group_api) user_api.ldap_attr = ldap_attributes assert user_api.username == 'test.user' def it_calls_the_client(mocker): # noqa: F811 client.add = MagicMock() user_api = UserApi(client) # mock private method __ldap_attr, so we don't have to manage # a giant dict that requires mocking many private methods client.basedn = 'dc=test,dc=org' mocker.patch.object( user_api, '_API__ldap_attr', return_value=ldap_attributes) mocker.patch.object( UserApi, '_API__object_class', return_value='inetOrgPerson') user_api.create(first_name, last_name, group_name, user_type, group_api) user_api.ldap_attr = ldap_attributes client.add.assert_called_once_with( 'uid=test.user,ou=People,dc=test,dc=org', 'inetOrgPerson', ldap_attributes) def describe_deletes_user(): def describe_commandline(): def it_calls_the_api(mocker): # noqa: F811 mocker.patch('ldap_tools.user.API.delete', return_value=None) mocker.patch( 'ldap_tools.client.Client.prepare_connection', return_value=None) runner.invoke( ldap_tools.user.CLI.user, ['delete', '--username', username, '--type', user_type]) ldap_tools.user.API.delete.assert_called_once_with( username, user_type) def describe_api(): def it_uses_the_correct_distinguished_name(mocker): # noqa: F811 mocker.patch( 'ldap_tools.client.Client.delete', return_value=None) mocker.patch( 'ldap_tools.client.Client.load_ldap_config', return_value=None) mocker.patch( 'ldap_tools.client.Client.prepare_connection', return_value=None) ldap_tools.client.Client.basedn = 'dc=test,dc=org' user_api = UserApi(client) user_api.delete(username, user_type) ldap_tools.client.Client.delete.assert_called_once_with( 'uid=test.user,ou=People,dc=test,dc=org') def describe_indexes_user(): def describe_commandline(): def it_calls_the_api(mocker): # noqa: F811 mocker.patch('ldap_tools.user.API.index', return_value=None) mocker.patch( 'ldap_tools.client.Client.prepare_connection', return_value=None) runner.invoke(ldap_tools.user.CLI.user, ['index']) ldap_tools.user.API.index.assert_called_once_with() def describe_api(): def it_passes_the_correct_filter(mocker): # noqa: F811 mocker.patch( 'ldap_tools.client.Client.search', return_value=None) mocker.patch( 'ldap_tools.client.Client.prepare_connection', return_value=None) user_api = UserApi(client) user_api.index() ldap_tools.client.Client.search.assert_called_once_with( ["(objectclass=posixAccount)"]) def describe_shows_user(): def describe_commandline(): def it_calls_the_api(mocker): # noqa: F811 mocker.patch('ldap_tools.user.API.show', return_value=None) mocker.patch( 'ldap_tools.client.Client.prepare_connection', return_value=None) runner.invoke(ldap_tools.user.CLI.user, ['show', '--username', username]) ldap_tools.user.API.show.assert_called_once_with(username) def describe_api(): def it_passes_the_correct_filter(mocker): # noqa: F811 mocker.patch( 'ldap_tools.client.Client.search', return_value=None) mocker.patch( 'ldap_tools.client.Client.prepare_connection', return_value=None) user_api = UserApi(client) user_api.show(username) filter = [ '(objectclass=posixAccount)', "(uid={})".format(username) ] ldap_tools.client.Client.search.assert_called_once_with(filter) def describe_finds_user(): def describe_commandline(): pass # no methods implemented def describe_api(): def it_finds_a_user(mocker): # noqa: F811 mocker.patch( 'ldap_tools.client.Client.search', return_value=['foo']) mocker.patch( 'ldap_tools.client.Client.prepare_connection', return_value=None) user_api = UserApi(client) user_api.find(username) ldap_tools.client.Client.search.assert_called_once_with( ['(uid={})'.format(username)]) def it_finds_two_users(mocker): # noqa: F811 mocker.patch( 'ldap_tools.client.Client.search', return_value=['foo', 'bar']) mocker.patch( 'ldap_tools.client.Client.prepare_connection', return_value=None) user_api = UserApi(client) with pytest.raises( ldap_tools.exceptions.TooManyResults, message=( "Multiple users found. Please narrow your search." )): user_api.find(username) def it_finds_no_users(mocker): # noqa: F811 mocker.patch( 'ldap_tools.client.Client.search', return_value=[]) mocker.patch( 'ldap_tools.client.Client.prepare_connection', return_value=None) user_api = UserApi(client) with pytest.raises( ldap_tools.exceptions.NoUserFound, message="User ({}) not found"): user_api.find(username)
def by_group(config): """Display LDAP group membership sorted by group.""" client = Client() client.prepare_connection() audit_api = API(client) CLI.parse_membership('Users by Group', audit_api.by_group())
def index(config): # pragma: no cover """Display group info in raw format.""" client = Client() client.prepare_connection() group_api = API(client) print(group_api.index())
def show(config, username): """Display a specific user.""" client = Client() client.prepare_connection() user_api = API(client) CLI.show_user(user_api.show(username))