charm-barbican-softhsm/unit_tests/test_lib_barbican_softhsm.py

207 lines
8.7 KiB
Python

# Copyright 2016 Canonical Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
from __future__ import print_function
import textwrap
import mock
import charm.openstack.softhsm as softhsm
import charms_openstack.test_utils as test_utils
class TestSoftHSM(test_utils.PatchHelper):
def test_install(self):
self.patch_object(softhsm.BarbicanSoftHSMCharm.singleton, 'install')
softhsm.install()
self.install.assert_called_once_with()
def test_on_hsm_connected(self):
self.patch_object(softhsm.BarbicanSoftHSMCharm.singleton,
'on_hsm_connected')
softhsm.on_hsm_connected('hsm-thing')
self.on_hsm_connected.assert_called_once_with('hsm-thing')
def test_assess_status(self):
self.patch_object(softhsm.BarbicanSoftHSMCharm.singleton,
'assess_status')
softhsm.assess_status()
self.assess_status.assert_called_once_with()
def test_read_pins_from_store(self):
# test with no file (patch open so that it raises an error)
mock_open = mock.MagicMock(return_value=mock.sentinel.file_handle)
with mock.patch('builtins.open', mock_open):
def raise_exception(*args):
raise OSError("Supposed to break")
mock_open.side_effect = raise_exception
pin, so_pin = softhsm.read_pins_from_store()
self.assertEqual(pin, None)
self.assertEqual(so_pin, None)
# now provide the pin and so pin as a json object
d = '{"pin": "1234", "so_pin": "5678"}'
with mock.patch('builtins.open',
mock.mock_open(read_data=d),
create=True):
pin, so_pin = softhsm.read_pins_from_store()
self.assertEqual(pin, '1234')
self.assertEqual(so_pin, '5678')
def test_write_pins_to_store(self):
f = mock.MagicMock()
self.patch_object(softhsm.os, 'fdopen', return_value=f)
self.patch_object(softhsm.os, 'open', return_value='opener')
self.patch_object(softhsm.json, 'dump')
softhsm.write_pins_to_store('1234', '5678')
self.open.assert_called_once_with(
softhsm.STORED_PINS_FILE,
softhsm.os.O_WRONLY | softhsm.os.O_CREAT,
0o600)
self.fdopen.assert_called_once_with('opener', 'w')
self.dump.assert_called_once_with(
{'pin': '1234', 'so_pin': '5678'}, f.__enter__())
def test_read_slot_id(self):
result = textwrap.dedent("""
Slot 5
Slot info:
Description: SoftHSM slot 0
Manufacturer ID: SoftHSM project
Hardware version: 2.0
Firmware version: 2.0
Token present: yes
Token info:
Manufacturer ID: SoftHSM project
Model: SoftHSM v2
Hardware version: 2.0
Firmware version: 2.0
Serial number: 02ae3171143498e7
Initialized: yes
User PIN init.: yes
Label: barbican_token
""")
self.patch_object(softhsm.subprocess, 'check_output',
return_value=result.encode())
self.assertEqual(softhsm.read_slot_id('barbican_token'), '5')
self.check_output.assert_called_once_with(
[softhsm.SOFTHSM2_UTIL_CMD, '--show-slots'])
self.assertEqual(softhsm.read_slot_id('not_found'), None)
class TestBarbicanSoftHSMCharm(test_utils.PatchHelper):
def test_install(self):
self.patch_object(softhsm.charms_openstack.charm.OpenStackCharm,
'install')
self.patch_object(softhsm.ch_core_host, 'add_user_to_group')
c = softhsm.BarbicanSoftHSMCharm()
self.patch_object(c, 'setup_token_store')
self.patch_object(softhsm.hookenv, 'status_set')
c.install()
self.install.assert_called_once_with()
self.add_user_to_group.assert_called_once_with('barbican', 'softhsm')
self.setup_token_store.assert_called_once_with()
self.status_set.assert_called_once_with(
'waiting', 'Charm installed and token store configured')
def test_setup_token_store(self):
self.patch_object(softhsm, 'read_pins_from_store')
self.patch_object(softhsm.os.path, 'exists')
self.patch_object(softhsm.os.path, 'isdir')
self.patch_object(softhsm.shutil, 'rmtree')
self.patch_object(softhsm.os, 'remove')
self.patch_object(softhsm.os, 'makedirs')
self.patch_object(softhsm.os, 'chmod')
self.patch_object(softhsm.ch_core_host, 'pwgen')
self.patch_object(softhsm, 'write_pins_to_store')
self.patch_object(softhsm.subprocess, 'check_call')
self.patch_object(softhsm.hookenv, 'log')
# first, pretend that the token store is already setup.
self.read_pins_from_store.return_value = ('1234', '5678', )
c = softhsm.BarbicanSoftHSMCharm()
c.setup_token_store()
self.assertEqual(self.log.call_count, 0)
# now pretend the token store isn't set up
self.read_pins_from_store.return_value = None, None
# assume that the token store exists and is a dir first:
self.exists.return_value = True
self.isdir.return_value = True
# return two values, for each of the two pwgen calls.
self.pwgen.side_effect = ['abcd', 'efgh']
c.setup_token_store()
# now validate it did everything we expected.
self.exists.assert_called_once_with(softhsm.TOKEN_STORE)
self.isdir.assert_called_once_with(softhsm.TOKEN_STORE)
self.rmtree.assert_called_once_with(softhsm.TOKEN_STORE)
self.makedirs.assert_called_once_with(softhsm.TOKEN_STORE)
self.chmod.assert_called_once_with(softhsm.TOKEN_STORE, 0o1777)
self.assertEqual(self.pwgen.call_count, 2)
self.write_pins_to_store.assert_called_once_with('abcd', 'efgh')
self.check_call.called_once_with([
'sudo', '-u', 'barbican',
softhsm.SOFTHSM2_UTIL_CMD,
'--init-token', '--free',
'--label', softhsm.BARBICAN_TOKEN_LABEL,
'--pin', 'abcd',
'--so-pin', 'efgh'])
self.log.assert_called_once_with("Initialised token store.")
def test_on_hsm_connected(self):
hsm = mock.MagicMock()
self.patch_object(softhsm, 'read_pins_from_store')
self.patch_object(softhsm, 'read_slot_id')
self.patch_object(softhsm.hookenv, 'status_set')
self.patch_object(softhsm.hookenv, 'log')
c = softhsm.BarbicanSoftHSMCharm()
self.patch_object(c, 'setup_token_store')
# simulate not being able to set up the token store
self.read_pins_from_store.return_value = None, None
with self.assertRaises(RuntimeError):
c.on_hsm_connected(hsm)
self.status_set.assert_called_once_with(
'error', "Couldn't set up the token store?")
self.setup_token_store.assert_called_once_with()
self.log.assert_called_once_with(
"Setting plugin name to softhsm2",
level=softhsm.hookenv.DEBUG)
# now assume that the pins can be read, but no slot is set up.
self.read_pins_from_store.return_value = '1234', '5678'
self.read_slot_id.return_value = None
with self.assertRaises(RuntimeError):
c.on_hsm_connected(hsm)
# now assume that the slot is also set up.
self.read_slot_id.return_value = '10'
c.on_hsm_connected(hsm)
hsm.set_plugin_data.assert_called_once_with({
"library_path": softhsm.SOFTHSM2_LIB_PATH,
"login": '1234',
"slot_id": '10'
})
# finally test corner case where token store isn't set up already
self.read_pins_from_store.side_effect = [
[None, None], ['abcd', 'efgh']]
hsm.reset_mock()
self.setup_token_store.reset_mock()
c.on_hsm_connected(hsm)
self.setup_token_store.assert_called_once_with()
hsm.set_plugin_data.assert_called_once_with({
"library_path": softhsm.SOFTHSM2_LIB_PATH,
"login": 'abcd',
"slot_id": '10'
})