blob: 6b3fc58a700e58a82aedafd290bf61a11c8a70f3 [file] [log] [blame]
# Copyright 2015 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Tests for accounts_lib."""
from __future__ import print_function
import json
import mock
from chromite.lib import accounts_lib
from chromite.lib import cros_test_lib
from chromite.lib import osutils
from chromite.lib import user_db
EMPTY_ACCOUNTS_DB_WITH_COMMENTS = """
{
# This accounts spec is empty.
"users": [
],
"groups": [
]
}
"""
MINIMAL_DB_USER = accounts_lib.User(
name='minimal', password='!', uid=1000, group_name='minimal',
description='', home='/dev/null', shell='/bin/false',
is_fixed_id=False, is_defunct=False)
MINIMAL_DB_GROUP = accounts_lib.Group(
name='minimal', password='!', gid=1000, users=['minimal'],
is_fixed_id=False, is_defunct=False)
MINIMAL_ACCOUNTS_DB = """
{
"users": [
{
# Minimal user.
"user": "minimal",
"uid": 1000,
"group_name": "minimal"
}
],
"groups": [
{
# Minimal group.
"group": "minimal",
"gid": 1000,
"users": [ "minimal" ]
}
]
}
"""
EXTRA_USER_SPEC_FIELD_DB = """
{
"users": [
{
"user": "minimal",
"uid": 1000,
"group_name": "minimal",
"gecos": "minimal user spec",
"extra": "This field is not expected."
}
]
}
"""
class AccountDatabaseTest(cros_test_lib.MockTestCase):
"""Tests for chromite.lib.accounts_lib.AccountDatabase."""
def _ParseSpec(self, contents, db=None):
"""Return a AccountDatabase that has read a file with |contents|.
Args:
contents: desired contents of accounts database to parse.
db: existing account db to override with new definitions.
Returns:
an instance of AccountDatabase.
"""
if db is None:
db = accounts_lib.AccountDatabase()
with self.PatchObject(osutils, 'ReadFile', return_value=contents):
db.AddAccountsFromDatabase('ignored')
return db
def _ParseSpecs(self, specs):
"""Return a AccountDatabase based on the account database stack in |specs|.
Args:
specs: list of json fragments (encoded as strings) to compose into a
consistent account database. This list is assumed to be in
increasing priority order so that later entries override earlier
entries.
Returns:
an instance of AccountDatabase.
"""
db = accounts_lib.AccountDatabase()
for spec in specs:
self._ParseSpec(spec, db=db)
return db
def testParsesEmptyDb(self):
"""Test that we can parse an empty database."""
self._ParseSpec(json.dumps({}))
def testParsesDbWithComments(self):
"""Test that we handle comments properly."""
self._ParseSpec(EMPTY_ACCOUNTS_DB_WITH_COMMENTS)
def testRejectsUnkownDbKeys(self):
"""Test that we check the set of keys specified in the account database."""
self.assertRaises(ValueError,
self._ParseSpec,
json.dumps({'foo': 'This is not a valid field.'}))
def testRejectsBadKeyValues(self):
"""Check that typecheck user/group specs."""
self.assertRaises(ValueError,
self._ParseSpec,
json.dumps({'users': 'This should be a list'}))
self.assertRaises(ValueError,
self._ParseSpec,
json.dumps({'groups': 'This should be a list'}))
def testRejectsExtraUserSpecFields(self):
"""Test that we check for extra user spec fields."""
self.assertRaises(ValueError, self._ParseSpec, EXTRA_USER_SPEC_FIELD_DB)
def testParsesMinimalDb(self):
"""Test that we can parse a basic database."""
db = self._ParseSpec(MINIMAL_ACCOUNTS_DB)
self.assertEqual(1, len(db.users.keys()))
self.assertEqual(1, len(db.groups.keys()))
self.assertIn(MINIMAL_DB_USER.name, db.users)
self.assertIn(MINIMAL_DB_GROUP.name, db.groups)
self.assertEqual(db.users[MINIMAL_DB_USER.name], MINIMAL_DB_USER)
self.assertEqual(db.groups[MINIMAL_DB_GROUP.name], MINIMAL_DB_GROUP)
def testComposesDbs(self):
"""Test that we can compose databases from multiple overlays."""
BASE_ID = 1000
OVERRIDE_ID = 2000
BASE_NAME = 'base'
OVERRIDE_NAME = 'override'
EXTRA_USER = 'extra.user'
base_db = json.dumps({
'users': [
{'user': BASE_NAME,
'uid': BASE_ID,
'group_name': 'base.group',
},
{'user': OVERRIDE_NAME,
'uid': OVERRIDE_ID - 1,
'group_name': 'override.group',
},
],
'groups': [
{'group': BASE_NAME,
'gid': BASE_ID,
'users': ['base.user']
},
{'group': OVERRIDE_NAME,
'gid': OVERRIDE_ID - 1,
'users': ['override.user']
},
],
})
override_db = json.dumps({
'users': [
{'user': OVERRIDE_NAME,
'uid': OVERRIDE_ID,
'group_name': 'override.group',
},
{'user': EXTRA_USER,
'uid': 3000,
'group_name': OVERRIDE_NAME,
},
],
'groups': [
{'group': OVERRIDE_NAME,
'gid': OVERRIDE_ID,
'users': [OVERRIDE_NAME, EXTRA_USER],
},
],
})
db = self._ParseSpecs([base_db, override_db])
self.assertEqual(3, len(db.users))
self.assertEqual(2, len(db.groups))
self.assertEqual(BASE_ID, db.users[BASE_NAME].uid)
self.assertEqual(BASE_ID, db.groups[BASE_NAME].gid)
self.assertEqual(OVERRIDE_ID, db.users[OVERRIDE_NAME].uid)
self.assertEqual(OVERRIDE_ID, db.groups[OVERRIDE_NAME].gid)
self.assertEqual(sorted([OVERRIDE_NAME, EXTRA_USER]),
sorted(db.groups[OVERRIDE_NAME].users))
def testInstallUser(self):
"""Test that we can install a user correctly."""
db = self._ParseSpec(MINIMAL_ACCOUNTS_DB)
mock_user_db = mock.MagicMock()
db.InstallUser(MINIMAL_DB_USER.name, mock_user_db)
installed_user = user_db.User(
user=MINIMAL_DB_USER.name, password=MINIMAL_DB_USER.password,
uid=MINIMAL_DB_USER.uid, gid=MINIMAL_DB_GROUP.gid,
gecos=MINIMAL_DB_USER.description, home=MINIMAL_DB_USER.home,
shell=MINIMAL_DB_USER.shell)
self.assertEqual([mock.call.AddUser(installed_user)],
mock_user_db.mock_calls)
def testInstallGroup(self):
"""Test that we can install a group correctly."""
db = self._ParseSpec(MINIMAL_ACCOUNTS_DB)
mock_user_db = mock.MagicMock()
db.InstallGroup(MINIMAL_DB_GROUP.name, mock_user_db)
installed_group = user_db.Group(
group=MINIMAL_DB_GROUP.name, password=MINIMAL_DB_GROUP.password,
gid=MINIMAL_DB_GROUP.gid, users=MINIMAL_DB_GROUP.users)
self.assertEqual([mock.call.AddGroup(installed_group)],
mock_user_db.mock_calls)