blob: cce630d467b3b74cf9af5601740f8c66853fde4e [file] [log] [blame]
# Copyright 2015 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Tests for accounts_lib."""
import json
from unittest 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 from 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) -> None:
"""Test that we can parse an empty database."""
self._ParseSpec(json.dumps({}))
def testParsesDbWithComments(self) -> None:
"""Test that we handle comments properly."""
self._ParseSpec(EMPTY_ACCOUNTS_DB_WITH_COMMENTS)
def testRejectsUnkownDbKeys(self) -> None:
"""Verify 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) -> None:
"""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) -> None:
"""Test that we check for extra user spec fields."""
self.assertRaises(ValueError, self._ParseSpec, EXTRA_USER_SPEC_FIELD_DB)
def testParsesMinimalDb(self) -> None:
"""Test that we can parse a basic database."""
db = self._ParseSpec(MINIMAL_ACCOUNTS_DB)
self.assertEqual(1, len(list(db.users)))
self.assertEqual(1, len(list(db.groups)))
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) -> None:
"""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) -> None:
"""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) -> None:
"""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
)