salt/tests/unit/states/test_user.py
2019-01-14 15:09:27 -05:00

260 lines
11 KiB
Python

# -*- coding: utf-8 -*-
'''
:codeauthor: Rahul Handay <rahulha@saltstack.com>
'''
# Import Python Libs
from __future__ import absolute_import, print_function, unicode_literals
import logging
# Import Salt Testing Libs
from tests.support.mixins import LoaderModuleMockMixin
from tests.support.unit import TestCase, skipIf
from tests.support.mock import (
Mock,
MagicMock,
patch,
NO_MOCK,
NO_MOCK_REASON
)
# Import Salt Libs
import salt.states.user as user
log = logging.getLogger(__name__)
@skipIf(NO_MOCK, NO_MOCK_REASON)
class UserTestCase(TestCase, LoaderModuleMockMixin):
'''
Validate the user state
'''
def setup_loader_modules(self):
return {user: {}}
def test_present(self):
'''
Test to ensure that the named user is present with
the specified properties
'''
ret = {'name': 'salt',
'changes': {},
'result': False,
'comment': ''}
mock_false = MagicMock(return_value=False)
mock_empty_list = MagicMock(return_value=[])
with patch.dict(user.__grains__, {"kernel": 'Linux'}):
with patch.dict(user.__salt__, {'group.info': mock_false,
'user.info': mock_empty_list,
"user.chkey": mock_empty_list,
'user.add': mock_false}):
ret.update({'comment': 'The following group(s) are'
' not present: salt'})
self.assertDictEqual(user.present('salt', groups=['salt']), ret)
mock_false = MagicMock(side_effect=[{'key': 'value'}, {'key': 'value'},
{'key': 'value'}, False, False])
with patch.object(user, '_changes', mock_false):
with patch.dict(user.__opts__, {"test": True}):
ret.update(
{'comment': 'The following user attributes are set '
'to be changed:\n'
'key: value\n',
'result': None})
self.assertDictEqual(user.present('salt'), ret)
with patch.dict(user.__opts__, {"test": False}):
# pylint: disable=repr-flag-used-in-string
comment = (
'These values could not be changed: {0!r}'
.format({'key': 'value'})
)
# pylint: enable=repr-flag-used-in-string
ret.update({'comment': comment, 'result': False})
self.assertDictEqual(user.present('salt'), ret)
with patch.dict(user.__opts__, {"test": True}):
ret.update({'comment': 'User salt set to'
' be added', 'result': None})
self.assertDictEqual(user.present('salt'), ret)
with patch.dict(user.__opts__, {"test": False}):
ret.update({'comment': 'Failed to create new'
' user salt', 'result': False})
self.assertDictEqual(user.present('salt'), ret)
def test_present_invalid_uid_change(self):
mock_info = MagicMock(side_effect=[
{'uid': 5000,
'gid': 5000,
'groups': ['foo'],
'home': '/home/foo',
'fullname': 'Foo Bar'}
])
dunder_salt = {'user.info': mock_info,
'file.group_to_gid': MagicMock(side_effect=['foo']),
'file.gid_to_group': MagicMock(side_effect=[5000])}
# side_effect used because these mocks should only be called once
with patch.dict(user.__grains__, {'kernel': 'Linux'}), \
patch.dict(user.__salt__, dunder_salt):
ret = user.present('foo', uid=5001)
# State should have failed
self.assertFalse(ret['result'])
# Only one of uid/gid should have been flagged in the comment
self.assertEqual(ret['comment'].count('not permitted'), 1)
def test_present_invalid_gid_change(self):
mock_info = MagicMock(side_effect=[
{'uid': 5000,
'gid': 5000,
'groups': ['foo'],
'home': '/home/foo',
'fullname': 'Foo Bar'}
])
dunder_salt = {'user.info': mock_info,
'file.group_to_gid': MagicMock(side_effect=['foo']),
'file.gid_to_group': MagicMock(side_effect=[5000])}
# side_effect used because these mocks should only be called once
with patch.dict(user.__grains__, {'kernel': 'Linux'}), \
patch.dict(user.__salt__, dunder_salt):
ret = user.present('foo', gid=5001)
# State should have failed
self.assertFalse(ret['result'])
# Only one of uid/gid should have been flagged in the comment
self.assertEqual(ret['comment'].count('not permitted'), 1)
def test_present_invalid_uid_gid_change(self):
mock_info = MagicMock(side_effect=[
{'uid': 5000,
'gid': 5000,
'groups': ['foo'],
'home': '/home/foo',
'fullname': 'Foo Bar'}
])
dunder_salt = {'user.info': mock_info,
'file.group_to_gid': MagicMock(side_effect=['foo']),
'file.gid_to_group': MagicMock(side_effect=[5000])}
# side_effect used because these mocks should only be called once
with patch.dict(user.__grains__, {'kernel': 'Linux'}), \
patch.dict(user.__salt__, dunder_salt):
ret = user.present('foo', uid=5001, gid=5001)
# State should have failed
self.assertFalse(ret['result'])
# Both the uid and gid should have been flagged in the comment
self.assertEqual(ret['comment'].count('not permitted'), 2)
def test_present_uid_gid_change(self):
before = {'uid': 5000,
'gid': 5000,
'groups': ['foo'],
'home': '/home/foo',
'fullname': 'Foo Bar'}
after = {'uid': 5001,
'gid': 5001,
'groups': ['othergroup'],
'home': '/home/foo',
'fullname': 'Foo Bar'}
# user.info should be called 4 times. Once the first time that
# _changes() is called, once before and after changes are applied (to
# get the before/after for the changes dict, and one last time to
# confirm that no changes still need to be made.
mock_info = MagicMock(side_effect=[before, before, after, after])
mock_group_to_gid = MagicMock(side_effect=['foo', 'othergroup'])
mock_gid_to_group = MagicMock(side_effect=[5000, 5001])
dunder_salt = {'user.info': mock_info,
'user.chuid': Mock(),
'user.chgid': Mock(),
'file.group_to_gid': mock_group_to_gid,
'file.gid_to_group': mock_gid_to_group}
# side_effect used because these mocks should only be called once
with patch.dict(user.__grains__, {'kernel': 'Linux'}), \
patch.dict(user.__salt__, dunder_salt), \
patch.dict(user.__opts__, {'test': False}), \
patch('os.path.isdir', MagicMock(return_value=True)):
ret = user.present(
'foo',
uid=5001,
gid=5001,
allow_uid_change=True,
allow_gid_change=True)
self.assertEqual(
ret,
{'comment': 'Updated user foo',
'changes': {'gid': 5001,
'uid': 5001,
'groups': ['othergroup']},
'name': 'foo',
'result': True}
)
def test_absent(self):
'''
Test to ensure that the named user is absent
'''
ret = {'name': 'salt',
'changes': {},
'result': None,
'comment': ''}
mock = MagicMock(side_effect=[True, True, False])
mock1 = MagicMock(return_value=False)
with patch.dict(user.__salt__, {'user.info': mock,
'user.delete': mock1,
'group.info': mock1}):
with patch.dict(user.__opts__, {"test": True}):
ret.update({'comment': 'User salt set for removal'})
self.assertDictEqual(user.absent('salt'), ret)
with patch.dict(user.__opts__, {"test": False}):
ret.update({'comment': 'Failed to remove user salt',
'result': False})
self.assertDictEqual(user.absent('salt'), ret)
ret.update({'comment': 'User salt is not present',
'result': True})
self.assertDictEqual(user.absent('salt'), ret)
def test_changes(self):
'''
Test salt.states.user._changes
'''
mock_info = MagicMock(
return_value={
'uid': 5000,
'gid': 5000,
'groups': ['foo'],
'home': '/home/foo',
'fullname': 'Foo Bar',
}
)
shadow_info = MagicMock(
return_value={
'min': 2,
'max': 88888,
'inact': 77,
'warn': 14,
}
)
shadow_hash = MagicMock(return_value='abcd')
dunder_salt = {'user.info': mock_info,
'shadow.info': shadow_info,
'shadow.default_hash': shadow_hash,
'file.group_to_gid': MagicMock(side_effect=['foo']),
'file.gid_to_group': MagicMock(side_effect=[5000])}
def mock_exists(*args):
return True
# side_effect used because these mocks should only be called once
with patch.dict(user.__grains__, {'kernel': 'Linux'}), \
patch.dict(user.__salt__, dunder_salt), \
patch.dict(user.__opts__, {"test": False}), \
patch('os.path.isdir', mock_exists):
ret = user._changes('foo', maxdays=999999, inactdays=0, warndays=7)
assert ret == {
'maxdays': 999999,
'mindays': 0,
'fullname': '',
'warndays': 7,
'inactdays': 0
}