Support trying all available tokens

Since the relation with vault maybe contain more than one
token and we have no way to know if they are valid, we
support trying them all until we get a good one and raise
the error only if we tried them all unsuccessfully and
have no current secret-id otherwise return current
secret-id.

Change-Id: I2ee5ffe5d53e874efb3fabc6a880bf95b00a44f9
Partial-Bug: #1849323
This commit is contained in:
Edward Hope-Morley 2019-12-06 16:29:22 +00:00
parent c8b24309e2
commit 4a1517d2fb
2 changed files with 77 additions and 8 deletions

View File

@ -11,6 +11,8 @@
# 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.
import hvac
import charmhelpers.core as ch_core
import charms.reactive as reactive
@ -44,12 +46,44 @@ def secret_backend_vault_request():
barbican_vault_charm.assess_status()
def get_secret_id(secrets_storage, current_secret_id=None):
"""Get tokens from relation and try to fetch secret-id from Vault api
Try all tokens until one succeeds. If all fail, return cached token
otherwise raise hvac.exceptions.InvalidRequest.
"""
tokens = secrets_storage.all_unit_tokens
url = secrets_storage.vault_url
for i, token in enumerate(tokens):
try:
secret_id = vault_utils.retrieve_secret_id(url, token)
return secret_id
except hvac.exceptions.InvalidRequest:
if i == len(tokens) - 1:
if current_secret_id:
return current_secret_id
else:
raise
else:
pass
@reactive.when_all('endpoint.secrets.joined', 'secrets-storage.available',
'endpoint.secrets-storage.changed')
def plugin_info_barbican_publish():
barbican = reactive.endpoint_from_flag('endpoint.secrets.joined')
secrets_storage = reactive.endpoint_from_flag(
'secrets-storage.available')
# fetch current secret-id, if any, from relation with barbican principle
current_secret_id = None
secrets = reactive.endpoint_from_flag('secrets.available')
if secrets:
for relation in secrets.relations:
data = relation.to_publish.get('data')
if data and data.get('approle_secret_id'):
current_secret_id = data.get('approle_secret_id')
with charm.provide_charm_instance() as barbican_vault_charm:
if secrets_storage.vault_ca:
ch_core.hookenv.log('Installing vault CA certificate')
@ -57,9 +91,7 @@ def plugin_info_barbican_publish():
ch_core.hookenv.log('Retrieving secret-id from vault ({})'
.format(secrets_storage.vault_url),
level=ch_core.hookenv.INFO)
secret_id = vault_utils.retrieve_secret_id(
secrets_storage.vault_url,
secrets_storage.unit_token)
secret_id = get_secret_id(secrets_storage, current_secret_id)
vault_data = {
'approle_role_id': secrets_storage.unit_role_id,
'approle_secret_id': secret_id,

View File

@ -51,6 +51,13 @@ class TestRegisteredHooks(test_utils.TestRegisteredHooks):
class TestBarbicanVaultHandlers(test_utils.PatchHelper):
def fake_hvac(self):
fake_exc = mock.MagicMock()
fake_exc.InvalidRequest = Exception
self.fake_hvac = mock.MagicMock()
self.fake_hvac.exceptions = fake_exc
return self.fake_hvac
def patch_charm(self):
barbican_vault_charm = mock.MagicMock()
self.patch_object(handlers.charm, 'provide_charm_instance',
@ -74,30 +81,60 @@ class TestBarbicanVaultHandlers(test_utils.PatchHelper):
secrets_storage.request_secret_backend.assert_called_once_with(
'charm-barbican-vault', isolated=False)
@mock.patch.object(handlers.vault_utils, 'retrieve_secret_id')
@mock.patch.object(handlers.reactive, 'endpoint_from_flag')
def test_get_secret_id(self, endpoint_from_flag, retrieve_secret_id):
with mock.patch.object(handlers, 'hvac', self.fake_hvac()):
endpoint_from_flag.all_unit_tokens = ['token1']
endpoint_from_flag.vault_url = 'https://foo.fl:8200'
retrieve_secret_id.return_value = 'big-secret'
self.assertEquals(handlers.get_secret_id(endpoint_from_flag,
'old-secret'),
'big-secret')
@mock.patch.object(handlers.vault_utils, 'retrieve_secret_id')
@mock.patch.object(handlers.reactive, 'endpoint_from_flag')
def test_get_secret_id_fail(self, endpoint_from_flag, retrieve_secret_id):
with mock.patch.object(handlers, 'hvac', self.fake_hvac()):
endpoint_from_flag.all_unit_tokens = ['token1']
endpoint_from_flag.vault_url = 'https://foo.fl:8200'
def fail(*args, **kwargs):
raise self.fake_hvac.exceptions.InvalidRequest
retrieve_secret_id.side_effect = fail
self.assertEquals(handlers.get_secret_id(endpoint_from_flag,
'old-secret'),
'old-secret')
def test_plugin_info_barbican_publish(self):
barbican_vault_charm = self.patch_charm()
self.patch_object(handlers.reactive, 'endpoint_from_flag')
barbican = mock.MagicMock()
secrets = mock.MagicMock()
secrets_storage = mock.MagicMock()
self.endpoint_from_flag.side_effect = [barbican, secrets_storage]
self.patch_object(handlers.vault_utils, 'retrieve_secret_id')
self.endpoint_from_flag.side_effect = [barbican, secrets_storage,
secrets]
self.patch_object(handlers, 'get_secret_id')
self.get_secret_id.return_value = 'big-secret'
self.patch_object(handlers.reactive, 'clear_flag')
handlers.plugin_info_barbican_publish()
self.endpoint_from_flag.assert_has_calls([
mock.call('endpoint.secrets.joined'),
mock.call('secrets-storage.available'),
mock.call('secrets.available'),
])
vault_data = {
'approle_role_id': secrets_storage.unit_role_id,
'approle_secret_id': self.retrieve_secret_id(),
'approle_secret_id': self.get_secret_id(),
'vault_url': secrets_storage.vault_url,
'kv_mountpoint': barbican_vault_charm.secret_backend_name,
'ssl_ca_crt_file': barbican_vault_charm.installed_ca_name,
}
barbican_vault_charm.install_ca_cert.assert_called_once_with(
secrets_storage.vault_ca)
barbican.publish_plugin_info.assert_called_once_with(
'vault', vault_data)
calls = [mock.call('vault', vault_data)]
barbican.publish_plugin_info.assert_has_calls(calls)
self.clear_flag.assert_called_once_with(
'endpoint.secrets-storage.changed')